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 = "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,20 +217,48 @@ 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
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å.
local function watchManualTrips(adEnv)
@@ -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
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<modDesc descVersion="107">
<author>masterdraco</author>
<version>1.47.0.0</version>
<version>1.47.1.0</version>
<title>
<en>AD Smart Pickup</en>
</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: å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)