diff --git a/FS25_ADSmartPickup/adFieldJobs.lua b/FS25_ADSmartPickup/adFieldJobs.lua
index bd6857f..82d7d28 100644
--- a/FS25_ADSmartPickup/adFieldJobs.lua
+++ b/FS25_ADSmartPickup/adFieldJobs.lua
@@ -208,6 +208,7 @@ function ADFieldJobs.load()
ADFieldJobs.state.fleetMode = loaded.fleetMode or "all"
ADFieldJobs.state.courseRecords = loaded.courseRecords or {}
ADFieldJobs.state.manual = loaded.manual or {}
+ ADFieldJobs.state.paused = loaded.paused == true
else
Logging.warning(ADFieldJobs.LOG_PREFIX .. "marker kunne ikke indlæses: %s", tostring(loaded))
end
@@ -252,6 +253,13 @@ function ADFieldJobs.setFleetMode(mode)
ADFieldJobs.save()
end
+-- Pause (Stop alt og parkér): intet nyt sendes ud, ingen genstart efter indlæsning. Gemmes.
+function ADFieldJobs.setPaused(value)
+ ADFieldJobs.state.paused = value == true
+ ADFieldJobs.save()
+ Logging.info(ADFieldJobs.LOG_PREFIX .. "markarbejde %s", value and "på pause" or "genoptaget")
+end
+
function ADFieldJobs.toggleManual(vehicleId)
ADFieldJobs.state.manual = ADFieldFleet.toggle(ADFieldJobs.state.manual or {}, vehicleId)
ADFieldJobs.save()
@@ -407,6 +415,8 @@ function ADFieldJobs.tick(adEnv, dt)
-- lidt efter start, og indtil da ligner de maskiner ledige (in-game 26/9: kultivatoren blev taget midt
-- i spillerens eget CP-job 45 s efter indlæsning)
if ADFieldJobs.now < ADFieldJobs.STARTUP_GRACE_MS then return end
+ -- Stop alt og parkér: intet sendes ud og intet genstartes, før spilleren trykker Start alle
+ if ADFieldJobs.state.paused then return end
if not ADFieldJobs.resumedAfterLoad then
-- én gang, før noget nyt sendes ud: CP-maskiner der var midt i et job, fortsætter (og tæller som aktive)
ADFieldJobs.resumedAfterLoad = true
diff --git a/FS25_ADSmartPickup/adFieldSilos.lua b/FS25_ADSmartPickup/adFieldSilos.lua
index 397d88d..d2c4a47 100644
--- a/FS25_ADSmartPickup/adFieldSilos.lua
+++ b/FS25_ADSmartPickup/adFieldSilos.lua
@@ -337,7 +337,8 @@ function ADFieldSilos.planNow(adEnv)
local plan = ADFieldSiloPlanner.plan({silos = silos, stores = stores, loans = loanList(), vehicles = vehicles,
paused = currentPaused(), maxVehicles = cfg.maxVehicles, minLiters = cfg.minLiters})
ADFieldSilos.lastPlan = plan
- if not cfg.enabled then return plan end
+ local paused = ADFieldJobs ~= nil and ADFieldJobs.state ~= nil and ADFieldJobs.state.paused
+ if not cfg.enabled or paused then return plan end
local silosById, storesById = {}, {}
for _, silo in ipairs(silos) do silosById[silo.id] = silo end
for _, store in ipairs(stores) do storesById[store.id] = store end
diff --git a/FS25_ADSmartPickup/adFieldStorage.lua b/FS25_ADSmartPickup/adFieldStorage.lua
index 8df1e2c..1b576c9 100644
--- a/FS25_ADSmartPickup/adFieldStorage.lua
+++ b/FS25_ADSmartPickup/adFieldStorage.lua
@@ -68,6 +68,7 @@ function ADFieldStorage.save(state)
index = index + 1
end
xmlFile:setString(ADFieldStorage.ROOT .. "#fleetMode", state.fleetMode or "all")
+ xmlFile:setInt(ADFieldStorage.ROOT .. "#paused", state.paused == true and 1 or 0)
local vehicleIndex = 0
for vehicleId in pairs(state.fleet) do
xmlFile:setString(string.format("%s.fleet.vehicle(%d)#id", ADFieldStorage.ROOT, vehicleIndex), vehicleId)
@@ -89,12 +90,13 @@ function ADFieldStorage.save(state)
end
function ADFieldStorage.load()
- local result = {fields = {}, fleet = {}, fleetMode = "all", courseRecords = {}, manual = {}}
+ local result = {fields = {}, fleet = {}, fleetMode = "all", courseRecords = {}, manual = {}, paused = false}
local path = ADFieldStorage.getPath()
if path == nil or not fileExists(path) then return result end
local xmlFile = XMLFile.load("ADSmartPickupFields", path)
if xmlFile == nil then return result end
result.fleetMode = xmlFile:getString(ADFieldStorage.ROOT .. "#fleetMode") == "ticked" and "ticked" or "all"
+ result.paused = xmlFile:getInt(ADFieldStorage.ROOT .. "#paused") == 1
xmlFile:iterate(ADFieldStorage.ROOT .. ".field", function(_, key)
local field = ADFieldModel.fromRecord(ADFieldStorage.readRecord(xmlFile, key))
if field.id ~= nil then result.fields[field.id] = field end
diff --git a/FS25_ADSmartPickup/adFieldWork.lua b/FS25_ADSmartPickup/adFieldWork.lua
index 47d457a..f6a0bae 100644
--- a/FS25_ADSmartPickup/adFieldWork.lua
+++ b/FS25_ADSmartPickup/adFieldWork.lua
@@ -1083,6 +1083,21 @@ function ADFieldWork.windDownHelpers(adEnv, fieldId, stage)
end
end
+-- Stop alle markarbejdets opgaver (Stop alt og parkér). Returnerer {{vehicle, manual}}; markernes trin bevares,
+-- så arbejdet fortsætter efter pausen.
+function ADFieldWork.stopAllAssignments(adEnv)
+ local stopped = {}
+ local list = {}
+ for _, assignment in pairs(ADFieldJobs.state.assignments) do table.insert(list, assignment) end
+ for _, assignment in ipairs(list) do
+ pcall(stopAd, assignment.vehicle)
+ pcall(ADCpBridge.stop, assignment.vehicle)
+ table.insert(stopped, {vehicle = assignment.vehicle, manual = assignment.manual == true})
+ ADFieldWork.release(assignment)
+ end
+ return stopped
+end
+
function ADFieldWork.stopAll(adEnv, fieldId)
for _, assignment in ipairs(ADFieldJobs.assignmentsFor(fieldId)) do
pcall(stopAd, assignment.vehicle)
diff --git a/FS25_ADSmartPickup/adRunsController.lua b/FS25_ADSmartPickup/adRunsController.lua
index bdb0c50..a1d7efc 100644
--- a/FS25_ADSmartPickup/adRunsController.lua
+++ b/FS25_ADSmartPickup/adRunsController.lua
@@ -1153,6 +1153,56 @@ function ADRunsController.queueStartAll(adEnv)
return summary
end
+-- Stop alt og parkér (Oversigt): flows, markarbejde og marksilo-lån stoppes, køretøjerne sendes til deres
+-- AD-parkeringspunkt, og moden sættes på pause (gemmes), så intet starter igen før "Start alle".
+-- Høstere med manuel transport parkeres ikke (må ikke køre på vejen med skærebord). Returnerer
+-- {stopped, parked, noPark = {navne}, manual = {navne}}.
+function ADRunsController.stopAllAndPark(adEnv)
+ local summary = {stopped = 0, parked = 0, noPark = {}, manual = {}}
+ ADRunsController.clearStartQueue()
+ local toPark, seen = {}, {}
+ local vehiclesById = getFarmVehiclesById(adEnv)
+ for _, run in ipairs(ADRunsController.store.runs) do
+ for _, vehicleId in ipairs(run.vehicleIds) do
+ local vehicle = vehiclesById[vehicleId]
+ if vehicle ~= nil and not seen[vehicle] then
+ seen[vehicle] = true
+ if isVehicleActive(vehicle) then
+ stopVehicle(vehicle)
+ summary.stopped = summary.stopped + 1
+ end
+ table.insert(toPark, vehicle)
+ end
+ end
+ ADRunsController.runningRuns[run.id] = nil
+ end
+ if ADFieldWork ~= nil and ADFieldWork.stopAllAssignments ~= nil then
+ for _, stopped in ipairs(ADFieldWork.stopAllAssignments(adEnv) or {}) do
+ summary.stopped = summary.stopped + 1
+ if stopped.manual then
+ table.insert(summary.manual, tostring(ADRuns.vehicleLabel(stopped.vehicle)))
+ elseif not seen[stopped.vehicle] then
+ seen[stopped.vehicle] = true
+ table.insert(toPark, stopped.vehicle)
+ end
+ end
+ end
+ if ADFieldSilos ~= nil and ADFieldSilos.stopAll ~= nil then pcall(ADFieldSilos.stopAll, adEnv) end
+ if ADFieldJobs ~= nil and ADFieldJobs.setPaused ~= nil then ADFieldJobs.setPaused(true) end
+ for _, vehicle in ipairs(toPark) do
+ local park = ADFieldWork ~= nil and ADFieldWork.parkMarkerId ~= nil and ADFieldWork.parkMarkerId(adEnv, vehicle) or nil
+ if park ~= nil then
+ local ok = pcall(ADFieldWork.startAd, adEnv, vehicle, adEnv.AutoDrive.MODE_DRIVETO, park, nil)
+ if ok then summary.parked = summary.parked + 1 end
+ else
+ table.insert(summary.noPark, tostring(ADRuns.vehicleLabel(vehicle)))
+ end
+ end
+ Logging.info(ADRunsController.LOG_PREFIX .. "Stop alt og parkér: %d stoppet, %d til parkering, %d uden parkeringspunkt, %d høstere hentes manuelt — pause",
+ summary.stopped, summary.parked, #summary.noPark, #summary.manual)
+ return summary
+end
+
-- Starter næste traktor i køen, når der er gået STAGGER_MS siden forrige.
function ADRunsController.processStartQueue(adEnv, dt)
if #ADRunsController.startQueue == 0 then return end
diff --git a/FS25_ADSmartPickup/adSmartPickup.lua b/FS25_ADSmartPickup/adSmartPickup.lua
index 87fdf36..dc4472b 100644
--- a/FS25_ADSmartPickup/adSmartPickup.lua
+++ b/FS25_ADSmartPickup/adSmartPickup.lua
@@ -1254,6 +1254,9 @@ function ADSmartPickup:update(dt)
if ADOutbound ~= nil and ADOutbound.releaseInactive ~= nil then
pcall(ADOutbound.releaseInactive)
end
+ if ADRunsController ~= nil and ADRunsController.processStartQueue ~= nil then
+ pcall(ADRunsController.processStartQueue, getAutoDriveEnv(), dt)
+ end
if ADRunsController ~= nil and ADRunsController.applyVehicleNamesThrottled ~= nil then
pcall(ADRunsController.applyVehicleNamesThrottled, getAutoDriveEnv(), dt)
end
diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua
index 3ddfd64..26686d3 100644
--- a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua
+++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua
@@ -58,7 +58,9 @@ function SmartPickupFrame.new(i18n, getAdEnv)
self.renameBuildingButtonInfo = {inputAction = InputAction.MENU_EXTRA_2, text = i18n:getText("spu_button_renameBuilding"), callback = function() self:onClickRenameBuilding() end}
self.storageButtons = {self.backButtonInfo, self.renameBuildingButtonInfo}
self.flowButtons = {self.backButtonInfo, self.startButtonInfo, self.newButtonInfo, self.renameButtonInfo, self.deleteButtonInfo}
- self.overviewButtons = {self.backButtonInfo, self.startAllButtonInfo, self.stopAllButtonInfo, self.newButtonInfo}
+ -- Stop alt og parkér: flows + markarbejde + marksilo stoppes, køretøjer til parkering, pause til Start alle
+ self.stopParkButtonInfo = {inputAction = InputAction.MENU_CANCEL, text = i18n:getText("spu_button_stopPark"), callback = function() self:onClickStopAndPark() end}
+ self.overviewButtons = {self.backButtonInfo, self.startAllButtonInfo, self.stopAllButtonInfo, self.stopParkButtonInfo, self.newButtonInfo}
-- Marker: Start nu / Stop for den valgte mark
-- Marker: [Start nu/Stop], [Opsæt mark], [Markflåde] (gui/SmartPickupFieldGuide.lua); guidens knapper
self:initFieldButtons(i18n)
@@ -628,7 +630,15 @@ function SmartPickupFrame:refreshLive()
running, total = running + status.running, total + status.total
if status.running > 0 then activeRuns = activeRuns + 1 end
end
- self:setStatusText(self.statusOverride or string.format(self.i18n:getText("spu_status_overview"), running, total, activeRuns, #ADRunsController.store.runs))
+ local statusText = string.format(self.i18n:getText("spu_status_overview"), running, total, activeRuns, #ADRunsController.store.runs)
+ local queued = #(ADRunsController.startQueue or {})
+ if queued > 0 then
+ local totalQueued = ADRunsController.startQueueTotal or queued
+ statusText = string.format(self.i18n:getText("spu_status_starting"), totalQueued - queued, totalQueued)
+ elseif ADFieldJobs ~= nil and ADFieldJobs.state ~= nil and ADFieldJobs.state.paused then
+ statusText = self.i18n:getText("spu_status_paused")
+ end
+ self:setStatusText(self.statusOverride or statusText)
self.stopAllButtonInfo.disabled = running == 0
elseif run ~= nil then
self:syncOptionsFromModel(adEnv, run)
@@ -786,11 +796,28 @@ function SmartPickupFrame:onClickStartAll()
self:safe(function(frame)
local adEnv = frame.getAdEnv()
if adEnv == nil then return end
- local summary = ADRunsController.startAllRuns(adEnv)
- local _, skipped, reasons = frame:summarizeStartResults(summary.results)
- Logging.info(SmartPickupFrame.LOG_PREFIX .. "Start alle: %d flows startet (%d traktorer), %d kørte allerede, %d sprunget over",
- summary.runsStarted, summary.vehiclesStarted, summary.alreadyRunning, skipped)
- frame:showOverride(string.format(frame.i18n:getText("spu_status_startedAll"), summary.runsStarted, summary.vehiclesStarted, summary.alreadyRunning, skipped), reasons)
+ -- pausen (Stop alt og parkér) ophæves; traktorerne startes én ad gangen med mellemrum
+ if ADFieldJobs ~= nil and ADFieldJobs.state ~= nil and ADFieldJobs.state.paused then ADFieldJobs.setPaused(false) end
+ local summary = ADRunsController.queueStartAll(adEnv)
+ local _, skipped, reasons = frame:summarizeStartResults(summary.skipped)
+ Logging.info(SmartPickupFrame.LOG_PREFIX .. "Start alle: %d traktorer i kø (én hvert %d. sekund), %d flows kørte allerede, %d sprunget over",
+ summary.queued, math.floor(ADRunsController.STAGGER_MS / 1000), summary.alreadyRunning, skipped)
+ frame:showOverride(string.format(frame.i18n:getText("spu_status_queuedAll"), summary.queued, math.floor(ADRunsController.STAGGER_MS / 1000), summary.alreadyRunning, skipped), reasons)
+ ADRunsController.save()
+ frame:requestRebuild()
+ end)
+end
+
+function SmartPickupFrame:onClickStopAndPark()
+ self:safe(function(frame)
+ local adEnv = frame.getAdEnv()
+ if adEnv == nil then return end
+ local summary = ADRunsController.stopAllAndPark(adEnv)
+ local text = string.format(frame.i18n:getText("spu_status_stopParked"), summary.stopped, summary.parked)
+ local notes = {}
+ if #summary.noPark > 0 then table.insert(notes, string.format(frame.i18n:getText("spu_status_noPark"), table.concat(summary.noPark, ", "))) end
+ if #summary.manual > 0 then table.insert(notes, string.format(frame.i18n:getText("spu_status_fetchManual"), table.concat(summary.manual, ", "))) end
+ frame:showOverride(text, notes)
ADRunsController.save()
frame:requestRebuild()
end)
diff --git a/FS25_ADSmartPickup/l10n/l10n_da.xml b/FS25_ADSmartPickup/l10n/l10n_da.xml
index 9d4c41d..d2013f2 100644
--- a/FS25_ADSmartPickup/l10n/l10n_da.xml
+++ b/FS25_ADSmartPickup/l10n/l10n_da.xml
@@ -218,6 +218,13 @@
+
+
+
+
+
+
+
diff --git a/FS25_ADSmartPickup/l10n/l10n_en.xml b/FS25_ADSmartPickup/l10n/l10n_en.xml
index 919a3e9..df664da 100644
--- a/FS25_ADSmartPickup/l10n/l10n_en.xml
+++ b/FS25_ADSmartPickup/l10n/l10n_en.xml
@@ -218,6 +218,13 @@
+
+
+
+
+
+
+
diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml
index bad3104..c9d70b5 100644
--- a/FS25_ADSmartPickup/modDesc.xml
+++ b/FS25_ADSmartPickup/modDesc.xml
@@ -1,7 +1,7 @@
masterdraco
- 1.41.1.0
+ 1.42.0.0
AD Smart Pickup
diff --git a/tests/test_adFieldStorage.lua b/tests/test_adFieldStorage.lua
index 4910b33..3aa09ec 100644
--- a/tests/test_adFieldStorage.lua
+++ b/tests/test_adFieldStorage.lua
@@ -98,5 +98,13 @@ ADFieldStorage.save({fields = {}, fleet = {}, fleetMode = "all", manual = {vehic
check("ST8 manuel transport indlæst", ADFieldStorage.load().manual.vehicleCombine, true)
check("ST8 gammel fil: ingen manuelle", next(old.manual or {}), nil)
+-- ST9: pause ("Stop alt og parkér") gemmes, så intet starter af sig selv efter indlæsning
+disk = {}
+ADFieldStorage.save({fields = {}, fleet = {}, fleetMode = "all", paused = true})
+check("ST9 pause indlæst", ADFieldStorage.load().paused, true)
+disk = {}
+ADFieldStorage.save({fields = {}, fleet = {}, fleetMode = "all"})
+check("ST9 ingen pause", ADFieldStorage.load().paused, false)
+
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
os.exit(failures == 0 and 0 or 1)
diff --git a/tests/test_adRunsController.lua b/tests/test_adRunsController.lua
index 4040129..3164f7b 100644
--- a/tests/test_adRunsController.lua
+++ b/tests/test_adRunsController.lua
@@ -863,5 +863,38 @@ do
check("C20 run markeret kørende", ADRunsController.runningRuns[qrun.id], true)
end
+
+-- C21: Stop alt og parkér — stopper flow-traktorerne, sender dem til AD-parkering, sætter pause
+do
+ buildWorld()
+ local p1 = makeVehicle("vehicleP1", "P1", {STRAW, TMR}, 1000000, 100, 0)
+ local p2 = makeVehicle("vehicleP2", "P2", {STRAW, TMR}, 1000000, 110, 0)
+ local prun = ADRunsController.createRun("Park")
+ ADRunsController.updateRun(prun.id, {targetWayPointIds = {94}})
+ ADRunsController.assignVehicle(prun.id, p1)
+ ADRunsController.assignVehicle(prun.id, p2)
+ ADRunsController.startRun(adEnv, prun.id)
+ local parked, paused = {}, nil
+ local oldWork, oldJobs, oldSilos = ADFieldWork, ADFieldJobs, ADFieldSilos
+ ADFieldWork = {
+ parkMarkerId = function(_, v) return v == p1 and 700 or nil end,
+ startAd = function(_, v, mode, first) parked[v] = first end,
+ stopAllAssignments = function() return {} end,
+ }
+ ADFieldJobs = {setPaused = function(value) paused = value end}
+ ADFieldSilos = {stopAll = function() end}
+ ADRunsController.queueStartAll(adEnv)
+ local summary = ADRunsController.stopAllAndPark(adEnv)
+ check("C21 begge stoppet", (p1.stopped or 0) >= 1 and (p2.stopped or 0) >= 1, true)
+ check("C21 P1 kører til parkering", parked[p1], 700)
+ check("C21 P2 uden parkering bliver stående", parked[p2], nil)
+ check("C21 tæller parkerede", summary.parked, 1)
+ check("C21 nævner dem uden parkering", summary.noPark[1], "P2")
+ check("C21 pause sat", paused, true)
+ check("C21 startkø tømt", #ADRunsController.startQueue, 0)
+ check("C21 run ikke længere kørende", ADRunsController.runningRuns[prun.id], nil)
+ ADFieldWork, ADFieldJobs, ADFieldSilos = oldWork, oldJobs, oldSilos
+end
+
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
os.exit(failures == 0 and 0 or 1)