fix(gui): Start starter altid (kørende AD-traktor stoppes og startes på run'et), Stop-knap kun for runs vi har startet, sprungne traktorer med årsag i status/log, kapacitet <100 l = ingen vogn, ASCII-tegn (v1.12.2.0)

This commit is contained in:
2026-09-23 11:18:55 +02:00
parent 2d6e6dd7bf
commit 2ff8e46306
7 changed files with 89 additions and 33 deletions
+37 -14
View File
@@ -10,6 +10,11 @@ ADRunsController.LOG_PREFIX = "ADSmartPickup: "
ADRunsController.MAX_NETWORK_DISTANCE = 30
ADRunsController.store = ADRuns.newStore()
-- Trækkerens egne tanke (diesel/AdBlue) kan give en lille "kapacitet" uden vogn.
ADRunsController.MIN_LOAD_CAPACITY = 100
-- run-id -> true når run'et er startet fra fanen (Start/Stop-knappen følger DETTE, ikke om
-- traktorerne tilfældigvis kører AutoDrive — en manuelt startet traktor må ikke vende knappen).
ADRunsController.runningRuns = {}
local function copyList(list)
local copy = {}
@@ -261,7 +266,7 @@ local function getLoadCapacity(adEnv, vehicle)
return nil
end
local _, capacity = adEnv.AutoDrive.getAllFillLevels(trailers)
return capacity ~= nil and capacity > 0 and capacity or nil
return capacity ~= nil and capacity >= ADRunsController.MIN_LOAD_CAPACITY and capacity or nil
end
local function intersects(listA, listB)
@@ -596,17 +601,28 @@ local function isVehicleActive(vehicle)
return stateModule ~= nil and stateModule.isActive ~= nil and stateModule:isActive()
end
-- Som ADInputManager:input_start_stop: loops nulstilles og AD stoppes.
local function stopVehicle(vehicle)
local stateModule = vehicle.ad.stateModule
if stateModule.setLoopsDone ~= nil then
stateModule:setLoopsDone(0)
end
vehicle:stopAutoDrive()
end
-- Anvend run-tilstand OG start traktoren i ét pcall'et kald: kaster vehicle:startAutoDrive()
-- (fx AD's egen validering), skal den ene traktor fejle uden at hele startRun-loopet
-- afbrydes — ellers når de øvrige tildelte traktorer aldrig at blive startet.
-- Start præcis som AD's HUD (ADInputManager:input_start_stop): det er MODE'ens start(),
-- der opretter køreopgaverne og selv kalder startAutoDrive — startAutoDrive alene sætter
-- kun "aktiv", og traktoren bliver holdende.
-- Kører traktoren AutoDrive i forvejen (startet manuelt eller i en gammel tilstand), stoppes
-- den først, så run'ets tilstand og opgaver gælder fra nu.
local function applyAndStart(adEnv, run, vehicle, ingredients)
applyRunToVehicle(adEnv, run, vehicle, ingredients)
if isVehicleActive(vehicle) then
return
stopVehicle(vehicle)
end
applyRunToVehicle(adEnv, run, vehicle, ingredients)
local stateModule = vehicle.ad.stateModule
local farmId = ADSmartPickup.getFarmId(vehicle)
if stateModule.setActualFarmId ~= nil and farmId ~= nil then
@@ -624,7 +640,8 @@ local function applyAndStart(adEnv, run, vehicle, ingredients)
end
end
-- Pr. tildelt traktor: validér, skriv AD-tilstand, start. ✖-traktorer springes over.
-- Pr. tildelt traktor: validér, skriv AD-tilstand, start. Afviste traktorer springes over
-- og logges med årsag (samme nøgle som fanen viser).
function ADRunsController.startRun(adEnv, runId)
local run = ADRuns.getRun(ADRunsController.store, runId)
local results = {}
@@ -632,15 +649,20 @@ function ADRunsController.startRun(adEnv, runId)
return results
end
local vehiclesById = getFarmVehiclesById(adEnv)
local started = 0
for _, vehicleId in ipairs(run.vehicleIds) do
local vehicle = vehiclesById[vehicleId]
if vehicle ~= nil then
local validation = ADRunsController.validateVehicle(adEnv, run, vehicle)
local result = {vehicle = vehicle, ok = validation.ok, reason = validation.reason}
if not validation.ok then
Logging.info(ADRunsController.LOG_PREFIX .. "run '%s': '%s' sprunget over (%s)", tostring(run.name), tostring(vehicle:getName()), tostring(validation.reason))
end
if validation.ok then
local ingredients = ADRunsController.getRunIngredients(adEnv, run, vehicle)
local ok, err = pcall(applyAndStart, adEnv, run, vehicle, ingredients)
if ok then
started = started + 1
local target = run.kind == "outbound" and ("kilde " .. tostring(run.sourceWayPointIds[1])) or ("markør " .. tostring(run.targetWayPointId))
Logging.info(ADRunsController.LOG_PREFIX .. "run '%s': '%s' startet mod %s", tostring(run.name), tostring(vehicle:getName()), target)
else
@@ -651,16 +673,10 @@ function ADRunsController.startRun(adEnv, runId)
table.insert(results, result)
end
end
return results
end
-- Som ADInputManager:input_start_stop: loops nulstilles og AD stoppes.
local function stopVehicle(vehicle)
local stateModule = vehicle.ad.stateModule
if stateModule.setLoopsDone ~= nil then
stateModule:setLoopsDone(0)
if started > 0 then
ADRunsController.runningRuns[runId] = true
end
vehicle:stopAutoDrive()
return results
end
-- En kørende traktor der fjernes fra sit run (eller hvis run slettes) kører run'ets job —
@@ -689,9 +705,12 @@ function ADRunsController.stopRun(adEnv, runId)
Logging.info(ADRunsController.LOG_PREFIX .. "run '%s': '%s' stoppet", tostring(run.name), tostring(vehicle:getName()))
end
end
ADRunsController.runningRuns[runId] = nil
return stopped
end
-- running = traktorer med AutoDrive aktiv (uanset hvem der startede dem); isRunning = run'et er
-- startet fra fanen og mindst én traktor kører stadig (Start/Stop-knappen følger isRunning).
function ADRunsController.getRunStatus(adEnv, run)
local running = 0
local vehiclesById = getFarmVehiclesById(adEnv)
@@ -701,7 +720,11 @@ function ADRunsController.getRunStatus(adEnv, run)
running = running + 1
end
end
return {running = running, total = #run.vehicleIds}
local isRunning = ADRunsController.runningRuns[run.id] == true and running > 0
if ADRunsController.runningRuns[run.id] and running == 0 then
ADRunsController.runningRuns[run.id] = nil
end
return {running = running, total = #run.vehicleIds, isRunning = isRunning}
end
-- ---- overblik ----
+24 -10
View File
@@ -7,6 +7,7 @@ SmartPickupFrame = {}
SmartPickupFrame._mt = Class(SmartPickupFrame, TabbedMenuFrameElement)
SmartPickupFrame.REFRESH_INTERVAL_MS = 1000
SmartPickupFrame.STATUS_OVERRIDE_TICKS = 6
SmartPickupFrame.LOG_PREFIX = "ADSmartPickup: "
SmartPickupFrame.STATE_OFF, SmartPickupFrame.STATE_ON = 1, 2
SmartPickupFrame.TAB_OVERVIEW, SmartPickupFrame.TAB_SUPPLY, SmartPickupFrame.TAB_OUTBOUND, SmartPickupFrame.TAB_STORAGE = 1, 2, 3, 4
@@ -475,9 +476,9 @@ function SmartPickupFrame:describeVehicle(adEnv, run, vehicle, isInRun)
end
end
if validation.ok then
table.insert(parts, "✔")
table.insert(parts, "OK")
else
table.insert(parts, "✖ " .. self.i18n:getText("spu_reason_" .. tostring(validation.reason)))
table.insert(parts, "X " .. self.i18n:getText("spu_reason_" .. tostring(validation.reason)))
end
return table.concat(parts, " — ")
end
@@ -687,8 +688,8 @@ function SmartPickupFrame:refreshLive()
self:refreshNeedsRows(adEnv, run)
end
local status = ADRunsController.getRunStatus(adEnv, run)
isRunning = status.running > 0
local text = isRunning and string.format(self.i18n:getText("spu_status_running"), status.running, status.total)
isRunning = status.isRunning == true
local text = status.running > 0 and string.format(self.i18n:getText("spu_status_running"), status.running, status.total)
or string.format(self.i18n:getText("spu_status_stopped"), status.total)
self:setStatusText(self.statusOverride or text)
for option, data in pairs(self.rowData) do
@@ -701,9 +702,12 @@ function SmartPickupFrame:refreshLive()
else
self:setStatusText(self.statusOverride or self.i18n:getText("spu_noRuns"))
end
-- Engangsbesked ("Startet 2, sprunget over 1", "flyttet fra ..."): vist nu, ryddet her,
-- så næste opdatering om et sekund viser den levende status igen.
self.statusOverride = nil
-- Engangsbesked ("Startet 2, sprunget over 1: …", "flyttet fra ..."): står i
-- STATUS_OVERRIDE_TICKS opdateringer (sekunder), så man når at læse hvorfor.
self.statusOverrideTicks = (self.statusOverrideTicks or 0) - 1
if self.statusOverrideTicks <= 0 then
self.statusOverride = nil
end
self.startButtonInfo.text = self.i18n:getText(isRunning and "spu_button_stop" or "spu_button_start")
self.startButtonInfo.disabled = run == nil
self.renameButtonInfo.disabled = run == nil
@@ -758,6 +762,7 @@ function SmartPickupFrame:onClickVehicle(state, element)
if previousRunId ~= nil then
local previous = ADRuns.getRun(ADRunsController.store, previousRunId)
frame.statusOverride = string.format(frame.i18n:getText("spu_movedFrom"), previous ~= nil and previous.name or tostring(previousRunId))
frame.statusOverrideTicks = SmartPickupFrame.STATUS_OVERRIDE_TICKS
end
else
ADRunsController.unassignVehicle(data.vehicle)
@@ -772,15 +777,24 @@ function SmartPickupFrame:onClickStartStop()
self:safe(function(frame)
local adEnv, run = frame.getAdEnv(), frame:getRun()
if adEnv == nil or run == nil then return end
if ADRunsController.getRunStatus(adEnv, run).running > 0 then
if ADRunsController.getRunStatus(adEnv, run).isRunning then
ADRunsController.stopRun(adEnv, run.id)
frame.statusOverride = nil
else
local started, skipped = 0, 0
local started, skipped, reasons = 0, 0, {}
for _, result in ipairs(ADRunsController.startRun(adEnv, run.id)) do
if result.ok then started = started + 1 else skipped = skipped + 1 end
if result.ok then
started = started + 1
else
skipped = skipped + 1
table.insert(reasons, string.format("%s: %s", tostring(result.vehicle:getName()), frame.i18n:getText("spu_reason_" .. tostring(result.reason))))
end
end
frame.statusOverride = string.format(frame.i18n:getText("spu_status_started"), started, skipped)
if #reasons > 0 then
frame.statusOverride = frame.statusOverride .. " — " .. table.concat(reasons, "; ")
end
frame.statusOverrideTicks = SmartPickupFrame.STATUS_OVERRIDE_TICKS
end
ADRunsController.save()
frame:requestRebuild()
@@ -3,7 +3,7 @@
-- Kun bygninger med indhold vises; teksterne opdateres hvert sekund uden genopbygning.
-- Funktionerne lægges på SmartPickupFrame (indlæses efter SmartPickupFrame.lua).
-- "Sugar mill (fabrik): Sukker 150.0k/180.0k ▸ · Roer 356.8k/360.0k" (▸ = output)
-- "Sugar mill (fabrik): *Sukker 150.0k/180.0k · Roer 356.8k/360.0k" (* = output)
function SmartPickupFrame:describeInventory(entry)
local parts = {}
for _, item in ipairs(entry.entries) do
@@ -13,7 +13,7 @@ function SmartPickupFrame:describeInventory(entry)
else
amount = string.format("%s/%s", SmartPickupFrame.formatCompact(item.level or 0), SmartPickupFrame.formatCompact(item.capacity or 0))
end
table.insert(parts, string.format("%s %s%s", tostring(item.title), amount, item.isOutput and " ▸" or ""))
table.insert(parts, string.format("%s%s %s", item.isOutput and "*" or "", tostring(item.title), amount))
end
local kindText = self.i18n:getText("spu_source_kind_" .. tostring(entry.kind))
return string.format("%s (%s): %s", tostring(entry.name), kindText, table.concat(parts, " · "))
+2 -2
View File
@@ -61,7 +61,7 @@
<text name="spu_tab_supply" text="Forsyning"/>
<text name="spu_tab_outbound" text="Udkørsel"/>
<text name="spu_tab_storage" text="Lagre"/>
<text name="spu_section_storage" text="— Gårdens beholdning (▸ = fabrikkens output) —"/>
<text name="spu_section_storage" text="— Gårdens beholdning (* = fabrikkens output) —"/>
<text name="spu_storage_pallets" text="%d paller"/>
<text name="spu_storage_empty" text="Ingen bygninger med indhold"/>
<text name="spu_source_kind_husbandry" text="stald"/>
@@ -95,7 +95,7 @@
<text name="spu_price_pct" text="%d %% af top (%s)"/>
<text name="spu_price_none" text="intet salgssted"/>
<text name="spu_outbound_waiting" text="venter: %s"/>
<text name="spu_outbound_warning" text="⚠ %s"/>
<text name="spu_outbound_warning" text="!! %s"/>
<text name="spu_activity_holding" text="holder ved %s (%s)"/>
<text name="spu_source_kind_factory" text="fabrik"/>
<text name="spu_source_kind_silo" text="silo"/>
+2 -2
View File
@@ -61,7 +61,7 @@
<text name="spu_tab_supply" text="Supply"/>
<text name="spu_tab_outbound" text="Outbound"/>
<text name="spu_tab_storage" text="Storage"/>
<text name="spu_section_storage" text="— Farm inventory (▸ = production output) —"/>
<text name="spu_section_storage" text="— Farm inventory (* = production output) —"/>
<text name="spu_storage_pallets" text="%d pallets"/>
<text name="spu_storage_empty" text="No buildings with contents"/>
<text name="spu_source_kind_husbandry" text="barn"/>
@@ -95,7 +95,7 @@
<text name="spu_price_pct" text="%d %% of top (%s)"/>
<text name="spu_price_none" text="no selling point"/>
<text name="spu_outbound_waiting" text="waiting: %s"/>
<text name="spu_outbound_warning" text="⚠ %s"/>
<text name="spu_outbound_warning" text="!! %s"/>
<text name="spu_activity_holding" text="holding at %s (%s)"/>
<text name="spu_source_kind_factory" text="factory"/>
<text name="spu_source_kind_silo" text="silo"/>
+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.12.1.0</version>
<version>1.12.2.0</version>
<title>
<en>AD Smart Pickup</en>
</title>