diff --git a/FS25_ADSmartPickup/adRunsController.lua b/FS25_ADSmartPickup/adRunsController.lua index 6e20e87..2926ba6 100644 --- a/FS25_ADSmartPickup/adRunsController.lua +++ b/FS25_ADSmartPickup/adRunsController.lua @@ -403,6 +403,67 @@ function ADRunsController.getRunStatus(adEnv, run) return {running = running, total = #run.vehicleIds} end +-- ---- overblik ---- + +-- Hvad laver traktoren lige nu? {kind = "stopped"|"waiting"|"loaded"|"empty", fillType, liters, place} +-- place = Wait-markør, aflæsningsmål eller den silo moden valgte. Kun eksisterende data bruges. +function ADRunsController.getVehicleActivity(adEnv, vehicle) + if not isVehicleActive(vehicle) then + return {kind = "stopped"} + end + local wait = ADUnloadWait ~= nil and ADUnloadWait.getWaitFor ~= nil and ADUnloadWait.getWaitFor(vehicle) or nil + if wait ~= nil then + return {kind = "waiting", fillType = wait.fillType, liters = wait.rest, place = wait.markerName} + end + local fillType, liters = nil, 0 + if ADUnloadWait ~= nil and ADUnloadWait.getRestLoad ~= nil then + fillType, liters = ADUnloadWait.getRestLoad(adEnv, vehicle) + end + local stateModule = vehicle.ad.stateModule + if fillType ~= nil and liters >= 100 then + local target = stateModule.getSecondMarker ~= nil and stateModule:getSecondMarker() or nil + return {kind = "loaded", fillType = fillType, liters = liters, place = target ~= nil and target.name or nil} + end + local pickup = ADSmartPickup.getActivePickup ~= nil and ADSmartPickup.getActivePickup(vehicle) or nil + if pickup ~= nil then + return {kind = "empty", fillType = pickup.fillType, place = pickup.markerName} + end + local first = stateModule.getFirstMarker ~= nil and stateModule:getFirstMarker() or nil + return {kind = "empty", place = first ~= nil and first.name or nil} +end + +-- Målets niveauer for run'ets varer: {{fillType, level, capacity}, ...} mest trængende først. +function ADRunsController.getTargetLevels(adEnv, run) + local farmId = g_currentMission:getFarmId() + local target = run.targetWayPointId ~= nil and ADSmartPickup.getUnloadStationAtWayPointId(adEnv, run.targetWayPointId, farmId) or nil + if target == nil then + return {} + end + local fillTypes = {} + if run.ingredientMode == "manual" then + for _, name in ipairs(run.ingredients) do + local index = g_fillTypeManager:getFillTypeIndexByName(name) + if index ~= nil then table.insert(fillTypes, index) end + end + else + for _, fillType in ipairs(ADRunsController.getTargetAcceptedFillTypes(adEnv, run)) do + table.insert(fillTypes, fillType.index) + end + end + local levels = {} + for _, fillType in ipairs(fillTypes) do + local level, capacity = ADSmartPickup.getStationLevelAndCapacity(target, fillType, farmId) + if level ~= nil then + table.insert(levels, {fillType = fillType, level = level, capacity = capacity, free = capacity - level}) + end + end + table.sort(levels, function(a, b) + if a.free ~= b.free then return a.free > b.free end + return a.fillType < b.fillType + end) + return levels +end + -- ---- opslag til dropdowns ---- -- Mål er steder der SKAL holdes fyldt: stalde (1) og fabrikker (2). Siloer tager alt og diff --git a/FS25_ADSmartPickup/adSmartPickup.lua b/FS25_ADSmartPickup/adSmartPickup.lua index fb7c27e..10a69bd 100644 --- a/FS25_ADSmartPickup/adSmartPickup.lua +++ b/FS25_ADSmartPickup/adSmartPickup.lua @@ -555,6 +555,15 @@ end -- Ingredienslisten for køretøjet: run'ets liste når det er i et run (ingen tændknap -- nødvendig), ellers AD's multi-valg (>1 vare). nil = ingen behovsstyring. +-- Den silo og vare moden sidst sendte køretøjet efter (til overblikket); nil = ingen. +function ADSmartPickup.getActivePickup(vehicle) + local pickup = activePickups[vehicle] + if pickup == nil then + return nil + end + return {markerName = pickup.markerName, fillType = pickup.fillType} +end + function ADSmartPickup.getIngredientsFor(adEnv, driver) local run = ADRunsController ~= nil and ADRunsController.getRunForVehicle(driver) or nil if run ~= nil then @@ -693,7 +702,7 @@ local function installHook(adEnv) activePickups[driver] = nil return originalGetNextPickup(self, driver, forcedSkip) end - activePickups[driver] = {station = choice.station, fillTypes = choice.fillTypes} + activePickups[driver] = {station = choice.station, fillTypes = choice.fillTypes, markerName = choice.markerName, fillType = choice.fillType or choice.fillTypes[1]} -- hold AutoDrives egen rotations-tilstand konsistent med vores valg self.pickups[choice.group] = self.pickups[choice.group] or {} diff --git a/FS25_ADSmartPickup/adUnloadWait.lua b/FS25_ADSmartPickup/adUnloadWait.lua index c723a6e..8872f0a 100644 --- a/FS25_ADSmartPickup/adUnloadWait.lua +++ b/FS25_ADSmartPickup/adUnloadWait.lua @@ -194,6 +194,19 @@ function ADUnloadWait.releaseInactive() end -- Hvem holder hvor (GUI'ets Wait-puljer-visning). Sorteret på mappe, dernæst markør-id. +-- Vognens indhold (største enhed) og evt. Wait-tilstand — til overblikket i fanen. +function ADUnloadWait.getRestLoad(adEnv, vehicle) + return getRestLoad(adEnv, vehicle) +end + +function ADUnloadWait.getWaitFor(vehicle) + local wait = waits[vehicle] + if wait == nil then + return nil + end + return {markerName = wait.waitMarker ~= nil and wait.waitMarker.name or nil, fillType = wait.fillType, rest = wait.rest} +end + function ADUnloadWait.getClaims() local result = {} for _, wait in pairs(waits) do diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua index 347bf4f..81b6791 100644 --- a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua +++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua @@ -176,6 +176,7 @@ function SmartPickupFrame:clearRows() self.clonedRows, self.rowData = {}, {} self.statusRow = nil self.needsRow = nil + self.overviewRows = {} end function SmartPickupFrame:addRow(prefab, data) @@ -221,6 +222,9 @@ function SmartPickupFrame:rebuild() self.selectedRunId = store.runs[1] ~= nil and store.runs[1].id or nil end self:clearRows() + if adEnv ~= nil then + self:fillOverviewRows(adEnv) + end self:fillRunOption(adEnv) local run = self:getRun() local hasRun = run ~= nil and adEnv ~= nil @@ -396,44 +400,104 @@ function SmartPickupFrame:syncOptionsFromModel(adEnv, run) sync(self.loopsOption, run.loops + 1) end --- "Hentes (mest trængende først): Forage 0/33.750 l · Silage 0/33.750 l · Straw 21.375/21.375 l" -function SmartPickupFrame:describeNeeds(adEnv, run) - local farmId = g_currentMission:getFarmId() - local target = run.targetWayPointId ~= nil and ADSmartPickup.getUnloadStationAtWayPointId(adEnv, run.targetWayPointId, farmId) or nil - if target == nil then - return self.i18n:getText("spu_needs_noTarget") - end - local fillTypes = {} - if run.ingredientMode == "manual" then - for _, name in ipairs(run.ingredients) do - local index = g_fillTypeManager:getFillTypeIndexByName(name) - if index ~= nil then table.insert(fillTypes, index) end - end - else - for _, fillType in ipairs(ADRunsController.getTargetAcceptedFillTypes(adEnv, run)) do - table.insert(fillTypes, fillType.index) - end - end - local needs = {} - for _, fillType in ipairs(fillTypes) do - local level, capacity = ADSmartPickup.getStationLevelAndCapacity(target, fillType, farmId) - if level ~= nil then - table.insert(needs, {fillType = fillType, level = level, capacity = capacity, free = capacity - level}) - end - end - table.sort(needs, function(a, b) - if a.free ~= b.free then return a.free > b.free end - return a.fillType < b.fillType - end) +-- "Silage 0/33.750 l · Straw 21.375/21.375 l" — mest trængende først. +function SmartPickupFrame:formatLevels(levels) local parts = {} - for _, need in ipairs(needs) do + for _, need in ipairs(levels) do local title = g_fillTypeManager.getFillTypeTitleByIndex ~= nil and g_fillTypeManager:getFillTypeTitleByIndex(need.fillType) or g_fillTypeManager:getFillTypeNameByIndex(need.fillType) table.insert(parts, string.format("%s %s/%s l", tostring(title), g_i18n:formatNumber(need.level, 0), g_i18n:formatNumber(need.capacity, 0))) end - if #parts == 0 then + return table.concat(parts, " · ") +end + +function SmartPickupFrame:describeNeeds(adEnv, run) + if run.targetWayPointId == nil then + return self.i18n:getText("spu_needs_noTarget") + end + local levels = ADRunsController.getTargetLevels(adEnv, run) + if #levels == 0 then return self.i18n:getText("spu_needs_none") end - return string.format(self.i18n:getText("spu_needs"), table.concat(parts, " · ")) + return string.format(self.i18n:getText("spu_needs"), self:formatLevels(levels)) +end + +-- ---- overblik (øverst): ét afsnit pr. run — status, traktorernes aktivitet, målets niveauer ---- + +function SmartPickupFrame:describeActivity(adEnv, vehicle) + local activity = ADRunsController.getVehicleActivity(adEnv, vehicle) + local label = ADRunsController.getVehicleLabel(adEnv, vehicle) + local fillTypeName = activity.fillType ~= nil and (g_fillTypeManager.getFillTypeTitleByIndex ~= nil and g_fillTypeManager:getFillTypeTitleByIndex(activity.fillType) or g_fillTypeManager:getFillTypeNameByIndex(activity.fillType)) or "?" + local place = tostring(activity.place or "?") + if activity.kind == "stopped" then + return string.format("%s: %s", label, self.i18n:getText("spu_activity_stopped")) + elseif activity.kind == "waiting" then + return string.format("%s: %s", label, string.format(self.i18n:getText("spu_activity_waiting"), place, g_i18n:formatNumber(activity.liters or 0, 0), fillTypeName)) + elseif activity.kind == "loaded" then + return string.format("%s: %s", label, string.format(self.i18n:getText("spu_activity_loaded"), g_i18n:formatNumber(activity.liters or 0, 0), fillTypeName, place)) + end + if activity.fillType ~= nil then + return string.format("%s: %s", label, string.format(self.i18n:getText("spu_activity_empty"), place, fillTypeName)) + end + return string.format("%s: %s", label, string.format(self.i18n:getText("spu_activity_driving"), place)) +end + +-- Rækkerne klones bagest og flyttes derefter forrest i layoutet. +function SmartPickupFrame:fillOverviewRows(adEnv) + local rows = {} + local function add(text) + local title = self:addTextRow(text) + table.insert(rows, title.parent) + return title + end + add(self.i18n:getText("spu_overview")) + local targetMarkerName = function(run) + local marker = run.targetWayPointId ~= nil and adEnv.ADGraphManager:getMapMarkerByWayPointId(run.targetWayPointId) or nil + return marker ~= nil and tostring(marker.name) or self.i18n:getText("spu_noTarget") + end + self.overviewRows = {} + for _, run in ipairs(ADRunsController.store.runs) do + local status = ADRunsController.getRunStatus(adEnv, run) + local statusText = 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) + local header = add(string.format("%s — %s", run.name, statusText)) + local entry = {run = run, header = header, vehicles = {}, target = nil} + for _, vehicleId in ipairs(run.vehicleIds) do + local vehicle = ADRunsController.findVehicleById(adEnv, vehicleId) + if vehicle ~= nil then + table.insert(entry.vehicles, {vehicle = vehicle, title = add(" " .. self:describeActivity(adEnv, vehicle))}) + end + end + entry.target = add(string.format(" %s: %s", targetMarkerName(run), self:formatLevels(ADRunsController.getTargetLevels(adEnv, run)))) + table.insert(self.overviewRows, entry) + end + -- forrest i layoutet: fjern de klonede rækker bagfra og sæt dem ind i rækkefølge først + local elements = self.boxLayout.elements + for index = #rows, 1, -1 do + for position = #elements, 1, -1 do + if elements[position] == rows[index] then + table.remove(elements, position) + break + end + end + table.insert(elements, 1, rows[index]) + end +end + +-- Opdateres hvert sekund uden genopbygning (kun tekster). +function SmartPickupFrame:refreshOverview(adEnv) + for _, entry in ipairs(self.overviewRows or {}) do + local status = ADRunsController.getRunStatus(adEnv, entry.run) + local statusText = 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) + entry.header:setText(string.format("%s — %s", entry.run.name, statusText)) + for _, item in ipairs(entry.vehicles) do + item.title:setText(" " .. self:describeActivity(adEnv, item.vehicle)) + end + if entry.target ~= nil then + local marker = entry.run.targetWayPointId ~= nil and adEnv.ADGraphManager:getMapMarkerByWayPointId(entry.run.targetWayPointId) or nil + entry.target:setText(string.format(" %s: %s", marker ~= nil and tostring(marker.name) or self.i18n:getText("spu_noTarget"), self:formatLevels(ADRunsController.getTargetLevels(adEnv, entry.run)))) + end + end end function SmartPickupFrame:fillPoolRows(adEnv) @@ -477,6 +541,7 @@ function SmartPickupFrame:refreshLive() self:setStatusText(self.i18n:getText("spu_status_noAd")) return end + self:refreshOverview(adEnv) local isRunning = false if run ~= nil then self:syncOptionsFromModel(adEnv, run) diff --git a/FS25_ADSmartPickup/l10n/l10n_da.xml b/FS25_ADSmartPickup/l10n/l10n_da.xml index c631698..9680790 100644 --- a/FS25_ADSmartPickup/l10n/l10n_da.xml +++ b/FS25_ADSmartPickup/l10n/l10n_da.xml @@ -42,6 +42,12 @@ + + + + + + diff --git a/FS25_ADSmartPickup/l10n/l10n_en.xml b/FS25_ADSmartPickup/l10n/l10n_en.xml index 6ceaf91..3e25725 100644 --- a/FS25_ADSmartPickup/l10n/l10n_en.xml +++ b/FS25_ADSmartPickup/l10n/l10n_en.xml @@ -42,6 +42,12 @@ + + + + + + diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index 8954cae..7d3b5c1 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.9.7.0 + 1.10.0.0 <en>AD Smart Pickup</en> diff --git a/tests/test_adRunsController.lua b/tests/test_adRunsController.lua index 5fc6425..7458e20 100644 --- a/tests/test_adRunsController.lua +++ b/tests/test_adRunsController.lua @@ -326,5 +326,24 @@ check("C14 traktor aktiv", modeVehicle.started, 1) check("C14 farm-id sat", modeVehicle.ad.stateModule.actualFarmId, 1) check("C14 loopsDone nulstillet", modeVehicle.ad.stateModule.loopsDone, 0) +-- C15: aktivitet til overblikket — stoppet / venter / med læs / tom på vej efter vare +ADUnloadWait = {getWaitFor = function(v) return v.waitInfo end, getRestLoad = function(_, v) return v.loadType, v.loadLiters or 0 end} +ADSmartPickup.getActivePickup = function(v) return v.pickupInfo end +buildWorld() +local actVehicle = makeVehicle("vehicleAct", "Akt", {STRAW, TMR}, 1000000, 100, 0) +check("C15 stoppet", ADRunsController.getVehicleActivity(adEnv, actVehicle).kind, "stopped") +actVehicle.ad.stateModule.active = true +actVehicle.ad.stateModule.secondMarkerId = 94 +actVehicle.loadType, actVehicle.loadLiters = STRAW, 8000 +local loaded = ADRunsController.getVehicleActivity(adEnv, actVehicle) +check("C15 med læs", loaded.kind .. "/" .. loaded.fillType .. "/" .. loaded.liters, "loaded/" .. STRAW .. "/8000") +actVehicle.loadLiters = 0 +actVehicle.pickupInfo = {markerName = "Silo 3", fillType = TMR} +local empty = ADRunsController.getVehicleActivity(adEnv, actVehicle) +check("C15 tom på vej efter TMR", empty.kind .. "/" .. tostring(empty.place) .. "/" .. tostring(empty.fillType), "empty/Silo 3/" .. TMR) +actVehicle.waitInfo = {markerName = "Cow Wait", fillType = STRAW, rest = 5000} +check("C15 venter", ADRunsController.getVehicleActivity(adEnv, actVehicle).kind, "waiting") +ADUnloadWait = nil + print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)