feat(kobling): spuTrip kører en tur i flere trin — Connect kobler på, Afkobl kobler af, almindelige markører holder 10 s undervejs (v1.47.1.0)

This commit is contained in:
2026-09-28 17:15:49 +02:00
parent be043ac220
commit b57940a933
3 changed files with 97 additions and 37 deletions
+78 -31
View File
@@ -19,12 +19,13 @@ ADHitch.MARKER_PREFIXES = {
{prefix = "connect", action = "attach"}, {prefix = "connect", action = "attach"},
{prefix = "kobl", 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.ARRIVE_DISTANCE = 5 -- m fra markøren, AD standser traktoren tæt på waypointet
ADHitch.STILL_SPEED = 1 -- km/t 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.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.ACT_TIMEOUT_MS = 8000 -- så længe forsøges kobling/afkobling efter ankomst
ADHitch.TICK_MS = 250 ADHitch.TICK_MS = 250
ADHitch.ROUTE_WAIT_MS = 10000 -- pause ved almindelige markører midt i en tur
-- ---- ren logik ---- -- ---- ren logik ----
@@ -52,12 +53,25 @@ function ADHitch.rearImplements(implements)
return rear return rear
end 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(...) function ADHitch.parseTrip(...)
local text = table.concat({...}, " ") local steps = {}
local first, second = text:match("^%s*(.-)%s*>%s*(.-)%s*$") for part in (table.concat({...}, " ") .. ">"):gmatch("([^>]*)>") do
if first == nil then return (text:match("^%s*(.-)%s*$")), nil end local name = part:match("^%s*(.-)%s*$")
return first, second 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 end
-- markers = {{name, id}} -> markøren med navnet (store/små bogstaver ligegyldigt) -- markers = {{name, id}} -> markøren med navnet (store/små bogstaver ligegyldigt)
@@ -203,20 +217,48 @@ function ADHitch.drop(adEnv, vehicle, markerId, onDone)
return start(adEnv, vehicle, markerId, "detach", onDone) return start(adEnv, vehicle, markerId, "detach", onDone)
end end
-- "Hent vogn og kør til": kør til Connect-markøren, kobl på, kør så med AD til målet. -- Tur: markørerne køres i rækkefølge. Connect = kobl på, Afkobl = kobl af, andre = kør dertil (og hold
function ADHitch.trip(adEnv, vehicle, connectId, destinationId, onDone) -- ROUTE_WAIT_MS, hvis det ikke er sidste trin). Stopper ved første fejl. onDone(vehicle, ok, årsag) til sidst.
return ADHitch.fetch(adEnv, vehicle, connectId, function(tractor, ok, detail) local waits = {} -- {vehicle, leftMs, continue}
if not ok then
if onDone ~= nil then onDone(tractor, false, detail) end 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 return
end end
local destination = adEnv.ADGraphManager:getMapMarkerByWayPointId(destinationId) local marker = adEnv.ADGraphManager:getMapMarkerByWayPointId(markerIds[index])
ADFieldWork.startAd(adEnv, tractor, adEnv.AutoDrive.MODE_DRIVETO, destinationId) local action = ADHitch.stepAction(marker ~= nil and marker.name or nil)
Logging.info(ADHitch.LOG_PREFIX .. "'%s' kører med '%s' til '%s'", vehicleName(tractor), vehicleName(detail), start(adEnv, vehicle, markerIds[index], action, function(_, ok, detail)
destination ~= nil and destination.name or tostring(destinationId)) if not ok then
if onDone ~= nil then onDone(tractor, true, detail) end 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)
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å. -- Spilleren sender selv en traktor til en Connect-/Afkobl-markør med AD "Kør til": den kobles også.
local function watchManualTrips(adEnv) local function watchManualTrips(adEnv)
@@ -231,8 +273,8 @@ local function watchManualTrips(adEnv)
end end
end end
local DONE_TEXT = {attach = "koblede '%s' på", detach = "koblede '%s' 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"} 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) local function finish(vehicle, job, ok, detail)
jobs[vehicle] = nil 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 if job.onDone ~= nil then pcall(job.onDone, vehicle, ok, detail) end
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) function ADHitch.tick(adEnv, dt)
if adEnv == nil or adEnv.ADGraphManager == nil then return end 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 if tickTimer < ADHitch.TICK_MS then return end
local elapsed = tickTimer local elapsed = tickTimer
tickTimer = 0 tickTimer = 0
tickWaits(elapsed)
watchManualTrips(adEnv) 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 local succeeded, detail = false, nil
if job.state == "acting" then if job.state == "acting" then
succeeded, detail = ACTIONS[job.action](vehicle) succeeded, detail = ACTIONS[job.action](vehicle)
@@ -291,26 +337,27 @@ function ADHitch:consoleDetach()
return "spuDetach: intet koblet af (" .. tostring(detail) .. ")" return "spuDetach: intet koblet af (" .. tostring(detail) .. ")"
end end
-- "spuTrip Connect Bredal > Start": maskinen spilleren sidder i henter vognen og kører til målet (spilleren -- "spuTrip Connect Bredal > Start > Afkobl Bredal > Start": maskinen spilleren sidder i kører turen
-- kan blive siddende; AD kører også med spilleren i førerhuset). -- (spilleren kan blive siddende; AD kører også med spilleren i førerhuset).
function ADHitch:consoleTrip(...) function ADHitch:consoleTrip(...)
local vehicle = currentVehicle() local vehicle = currentVehicle()
if vehicle == nil then return "spuTrip: sæt dig i en traktor først" end if vehicle == nil then return "spuTrip: sæt dig i en traktor først" end
local adEnv = ADFieldSilos ~= nil and ADFieldSilos.adEnv or nil local adEnv = ADFieldSilos ~= nil and ADFieldSilos.adEnv or nil
if adEnv == nil then return "spuTrip: AutoDrive er ikke klar endnu" end if adEnv == nil then return "spuTrip: AutoDrive er ikke klar endnu" end
local connectName, destinationName = ADHitch.parseTrip(...) local names = ADHitch.parseTrip(...)
local markers = adEnv.ADGraphManager:getMapMarkers() if #names == 0 then return "spuTrip: brug fx spuTrip Connect Bredal > Start > Afkobl Bredal > Start" end
local connect, destination = ADHitch.findMarker(markers, connectName), ADHitch.findMarker(markers, destinationName) local markers, ids = adEnv.ADGraphManager:getMapMarkers(), {}
if connect == nil or ADHitch.parseMarker(connect.name) ~= "attach" then for _, name in ipairs(names) do
return "spuTrip: ukendt Connect-markør '" .. tostring(connectName) .. "'" local marker = ADHitch.findMarker(markers, name)
if marker == nil then return "spuTrip: ukendt markør '" .. name .. "'" end
table.insert(ids, marker.id)
end end
if destination == nil then return "spuTrip: ukendt mål '" .. tostring(destinationName) .. "' (brug: spuTrip Connect Bredal > Start)" end ADHitch.route(adEnv, vehicle, ids)
ADHitch.trip(adEnv, vehicle, connect.id, destination.id) return "spuTrip: " .. vehicleName(vehicle) .. " kører " .. table.concat(names, " > ")
return "spuTrip: " .. vehicleName(vehicle) .. " henter ved '" .. connect.name .. "' og kører til '" .. destination.name .. "'"
end end
if addConsoleCommand ~= nil then if addConsoleCommand ~= nil then
addConsoleCommand("spuConnect", "Kobler nærmeste redskab på den maskine du sidder i", "consoleConnect", ADHitch) 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) addConsoleCommand("spuDetach", "Kobler redskaberne bag den maskine du sidder i af", "consoleDetach", ADHitch)
end end
+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.47.0.0</version> <version>1.47.1.0</version>
<title> <title>
<en>AD Smart Pickup</en> <en>AD Smart Pickup</en>
</title> </title>
+17 -4
View File
@@ -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: AD stoppede langt fra markøren", lostJob.state, "failed")
check("job: årsag stoppet undervejs", lostJob.reason, "stoppedAway") check("job: årsag stoppet undervejs", lostJob.reason, "stoppedAway")
-- tur: "Connect Bredal > Start" -> to markørnavne -- tur: "Connect Bredal > Start" -> to markørnavne
local a, b = H.parseTrip("Connect", "Bredal", ">", "Start") local route = H.parseTrip("Connect", "Bredal", ">", "Start", ">", "Afkobl", "Bredal", ">", "Start")
check("tur: hent", a, "Connect Bredal") check("tur: antal trin", #route, 4)
check("tur: mål", b, "Start") check("tur: første", route[1], "Connect Bredal")
check("tur: uden mål", select(2, H.parseTrip("Connect", "Bredal")), nil) 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}} 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: navn uden hensyn til store/små", H.findMarker(markers, "connect bredal").id, 9)
check("markør: ukendt", H.findMarker(markers, "Stald"), nil) check("markør: ukendt", H.findMarker(markers, "Stald"), nil)