From d46ebbac04c8a495dd2a7317d405e7402447d08e Mon Sep 17 00:00:00 2001 From: masterdraco Date: Wed, 23 Sep 2026 18:19:27 +0200 Subject: [PATCH] =?UTF-8?q?feat(gui):=20flow-tavle=20=E2=80=94=20faner=20F?= =?UTF-8?q?lows/Lagre,=20=C3=A9n=20r=C3=A6kke=20pr.=20flow,=20Fra/Til=20so?= =?UTF-8?q?m=20bygninger=20med=20automatisk=20mark=C3=B8r=20(v1.13.0.0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FS25_ADSmartPickup/adRunsController.lua | 2 +- FS25_ADSmartPickup/adSmartPickup.lua | 1 + FS25_ADSmartPickup/gui/SmartPickupFlowTab.lua | 199 ++++++++++++++++++ FS25_ADSmartPickup/gui/SmartPickupFrame.lua | 169 ++++----------- FS25_ADSmartPickup/l10n/l10n_da.xml | 23 ++ FS25_ADSmartPickup/l10n/l10n_en.xml | 23 ++ FS25_ADSmartPickup/modDesc.xml | 3 +- README.md | 15 ++ 8 files changed, 309 insertions(+), 126 deletions(-) create mode 100644 FS25_ADSmartPickup/gui/SmartPickupFlowTab.lua diff --git a/FS25_ADSmartPickup/adRunsController.lua b/FS25_ADSmartPickup/adRunsController.lua index e03aa66..32662ec 100644 --- a/FS25_ADSmartPickup/adRunsController.lua +++ b/FS25_ADSmartPickup/adRunsController.lua @@ -861,7 +861,7 @@ function ADRunsController.startRun(adEnv, runId) 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} + local result = {vehicle = vehicle, ok = validation.ok, reason = validation.reason, missingBuilding = validation.missingBuilding} 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 diff --git a/FS25_ADSmartPickup/adSmartPickup.lua b/FS25_ADSmartPickup/adSmartPickup.lua index bdd5419..dfa7562 100644 --- a/FS25_ADSmartPickup/adSmartPickup.lua +++ b/FS25_ADSmartPickup/adSmartPickup.lua @@ -16,6 +16,7 @@ if ADSmartPickup.dir ~= nil and source ~= nil then source(ADSmartPickup.dir .. "gui/SmartPickupSupplyTab.lua") source(ADSmartPickup.dir .. "gui/SmartPickupOutboundTab.lua") source(ADSmartPickup.dir .. "gui/SmartPickupStorageTab.lua") + source(ADSmartPickup.dir .. "gui/SmartPickupFlowTab.lua") end -- Markøren er dér hvor trækkerens næse stopper; læssetriggeren ligger et diff --git a/FS25_ADSmartPickup/gui/SmartPickupFlowTab.lua b/FS25_ADSmartPickup/gui/SmartPickupFlowTab.lua new file mode 100644 index 0000000..b0892a3 --- /dev/null +++ b/FS25_ADSmartPickup/gui/SmartPickupFlowTab.lua @@ -0,0 +1,199 @@ +-- Smart Pickup — fanen Flows +-- Tavle øverst: én tekstrække pr. flow (Vare · Fra → Til · Regel · Traktorer · Status), det valgte +-- markeret med ">". Under tavlen: detail-blok for det valgte flow — Retning, Fra/Til som BYGNINGER +-- (markøren findes automatisk), Varer, Regler, Vent ved, Traktorer, Status — og Wait-puljerne. +-- Funktionerne lægges på SmartPickupFrame (indlæses efter SmartPickupFrame.lua). + +-- ---- tavlen ---- + +local function joinOr(list, empty) + if #list == 0 then return empty end + return table.concat(list, " + ") +end + +-- "Mælk · Kostald 1 + Kostald 2 → salg · pris ≥ 90 %, lager > 80 % · Virtus · kører (1/1) — SUGAR 78 % af top" +function SmartPickupFrame:describeFlow(adEnv, run, isSelected) + local row = ADRunsController.getFlowRow(adEnv, run) + local i18n = self.i18n + local products + if row.productAuto then + products = i18n:getText("spu_flow_needBased") + else + local titles = {} + for _, name in ipairs(row.productNames) do + local index = g_fillTypeManager:getFillTypeIndexByName(name) + local title = index ~= nil and g_fillTypeManager.getFillTypeTitleByIndex ~= nil and g_fillTypeManager:getFillTypeTitleByIndex(index) or name + table.insert(titles, tostring(title)) + end + products = joinOr(titles, i18n:getText("spu_flow_noProducts")) + end + local missing = {} + for _, name in ipairs(row.missing) do missing[name] = true end + local function mark(names) + local out = {} + for _, name in ipairs(names) do table.insert(out, missing[name] and (name .. " !") or name) end + return out + end + local from, to + if row.kind == "outbound" then + from = joinOr(mark(row.fromNames), i18n:getText("spu_flow_noSource")) + local seen, policies = {}, {} + for _, policy in ipairs(row.policies) do + if not seen[policy] then seen[policy] = true; table.insert(policies, i18n:getText("spu_policy_" .. policy)) end + end + to = joinOr(policies, i18n:getText("spu_flow_noProducts")) + else + from = i18n:getText("spu_from_allSilos") + to = joinOr(mark(row.toNames), i18n:getText("spu_flow_noTarget")) + end + local rule + if row.kind == "outbound" then + rule = string.format(i18n:getText("spu_flow_ruleOut"), row.sellAtPercent or 90, row.storeAbovePercent or 80) + if row.collectRound then rule = rule .. ", " .. i18n:getText("spu_flow_round") end + else + rule = row.loops ~= nil and row.loops > 0 and string.format(i18n:getText("spu_flow_ruleInLoops"), row.loops) or i18n:getText("spu_flow_ruleIn") + end + local vehicles = joinOr(row.vehicleNames, i18n:getText("spu_flow_noVehicle")) + local status = row.running > 0 and string.format(i18n:getText("spu_status_running"), row.running, row.total) or string.format(i18n:getText("spu_status_stopped"), row.total) + if row.warning ~= nil then + status = status .. " — !! " .. row.warning + elseif row.reason ~= nil then + status = status .. " — " .. row.reason + end + local prefix = isSelected and "> " or " " + return string.format("%s%s: %s · %s → %s · %s · %s · %s", prefix, run.name, products, from, to, rule, vehicles, status) +end + +-- Tavlen klones bagest og flyttes derefter forrest i layoutet (foran de faste rækker). +function SmartPickupFrame:fillBoardRows(adEnv) + self.boardRows = {} + local rows = {} + local header = self:addTextRow(self.i18n:getText("spu_section_flows")) + table.insert(rows, header.parent) + for _, run in ipairs(ADRunsController.store.runs) do + local title = self:addTextRow(self:describeFlow(adEnv, run, run.id == self.selectedRunId)) + table.insert(rows, title.parent) + table.insert(self.boardRows, {run = run, title = title}) + end + if #ADRunsController.store.runs == 0 then + table.insert(rows, self:addTextRow(self.i18n:getText("spu_noRuns")).parent) + end + 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 + +function SmartPickupFrame:refreshBoard(adEnv) + for _, entry in ipairs(self.boardRows or {}) do + if ADRuns.getRun(ADRunsController.store, entry.run.id) ~= nil then + entry.title:setText(self:describeFlow(adEnv, ADRuns.getRun(ADRunsController.store, entry.run.id), entry.run.id == self.selectedRunId)) + end + end +end + +-- ---- detail-blok ---- + +-- "Kostald 1 (stald) — markør 'cow 1 milk' 12 m" / "… — INGEN MARKØR inden for 40 m" +function SmartPickupFrame:describeBuilding(building, role) + local marker = role == "load" and building.loadMarker or building.unloadMarker + local kindText = self.i18n:getText("spu_source_kind_" .. tostring(building.kind)) + if marker == nil then + return string.format(" %s (%s) — %s", building.name, kindText, self.i18n:getText("spu_noMarker")) + end + return string.format(" %s (%s) — %s", building.name, kindText, string.format(self.i18n:getText("spu_markerAt"), marker.name, marker.distance)) +end + +-- Afkrydsning pr. bygning: kilder (udkørsel, role "load") eller mål (forsyning, role "unload"). +function SmartPickupFrame:fillBuildingRows(adEnv, run, role) + if ADBuildings == nil then + self:addTextRow(self.i18n:getText("spu_status_noAd")) + return + end + local chosen = {} + local ids = role == "load" and run.sourceBuildingIds or run.targetBuildingIds + for _, id in ipairs(ids or {}) do chosen[id] = true end + local count = 0 + for _, building in ipairs(ADBuildings.list(adEnv, g_currentMission:getFarmId())) do + local fits = (role == "load" and building.canBeSource) or (role == "unload" and building.canBeTarget) + if fits then + count = count + 1 + local row = self:addRow(self.ingredientPrefab, {kind = role == "load" and "sourceBuilding" or "targetBuilding", buildingId = building.id}) + row:getDescendantByName("title"):setText(self:describeBuilding(building, role)) + row:getDescendantByName("option"):setState(chosen[building.id] and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false) + end + end + -- valgte bygninger der ikke længere findes (solgt) + for _, id in ipairs(ids or {}) do + if ADBuildings.find(adEnv, g_currentMission:getFarmId(), id) == nil then + local row = self:addRow(self.ingredientPrefab, {kind = role == "load" and "sourceBuilding" or "targetBuilding", buildingId = id}) + row:getDescendantByName("title"):setText(string.format(" %s — %s", tostring(id), self.i18n:getText("spu_buildingGone"))) + row:getDescendantByName("option"):setState(SmartPickupFrame.STATE_ON, false) + end + end + if count == 0 then + self:addTextRow(self.i18n:getText("spu_noBuildings")) + end +end + +function SmartPickupFrame:rebuildFlowDetail(adEnv, run) + ADRunsController.resolveMarkers(adEnv, run) + run = self:getRun() + local isOut = run.kind == "outbound" + self:addTextRow(string.format(self.i18n:getText("spu_section_flow"), run.name)) + self.directionRowOption = self:addToggleRow("direction", self.i18n:getText("spu_direction"), {self.i18n:getText("spu_direction_in"), self.i18n:getText("spu_direction_out")}, isOut) + if isOut then + self:addTextRow(self.i18n:getText("spu_section_from")) + self:fillBuildingRows(adEnv, run, "load") + self:addTextRow(self.i18n:getText("spu_section_products")) + self:fillProductRows(adEnv, run) + self:addTextRow(self.i18n:getText("spu_section_rules")) + self.sellAtOption:setState(SmartPickupFrame.getStepIndex(self.sellAtSteps, run.sellAtPercent, ADRuns.DEFAULT_SELL_AT_PERCENT), false) + self.storeAboveOption:setState(SmartPickupFrame.getStepIndex(self.storeAboveSteps, run.storeAbovePercent, ADRuns.DEFAULT_STORE_ABOVE_PERCENT), false) + self.collectRoundRowOption = self:addToggleRow("collectRound", self.i18n:getText("spu_collectRound"), {self.i18n:getText("ui_no"), self.i18n:getText("ui_yes")}, run.collectRound == true) + self:fillOutboundStatusRows(adEnv, run) + else + self:addTextRow(self.i18n:getText("spu_section_to")) + self:fillBuildingRows(adEnv, run, "unload") + self:addTextRow(self.i18n:getText("spu_section_targets")) + self.ingredientModeRowOption = self:addToggleRow("ingredientMode", self.i18n:getText("spu_ingredientMode"), {self.i18n:getText("spu_mode_auto"), self.i18n:getText("spu_mode_manual")}, run.ingredientMode == "manual") + self:fillIngredientRows(adEnv, run) + self:fillNeedsRows(adEnv, run) + self:addTextRow(self.i18n:getText("spu_section_rules")) + self.loopsOption:setState(run.loops + 1, false) + self.returnBelowOption:setState(self:getReturnBelowIndex(run), false) + end +end + +-- ---- callbacks ---- + +function SmartPickupFrame:onClickDirection(state) + self:safe(function(frame) + local run = frame:getRun() + if run == nil then return end + ADRunsController.updateRun(run.id, {kind = state == SmartPickupFrame.STATE_ON and "outbound" or "supply"}) + frame:requestRebuild() + end) +end + +function SmartPickupFrame:onClickBuilding(state, element) + self:safe(function(frame) + local run, data = frame:getRun(), frame.rowData[element] + if run == nil or data == nil then return end + if data.kind == "sourceBuilding" then + ADRunsController.store = ADRuns.toggleSourceBuilding(ADRunsController.store, run.id, data.buildingId) + else + ADRunsController.store = ADRuns.toggleTargetBuilding(ADRunsController.store, run.id, data.buildingId) + end + local updated = ADRuns.getRun(ADRunsController.store, run.id) + Logging.info(SmartPickupFrame.LOG_PREFIX .. "run '%s': %s=%s", tostring(run.name), data.kind == "sourceBuilding" and "sourceBuildings" or "targetBuildings", table.concat(data.kind == "sourceBuilding" and updated.sourceBuildingIds or updated.targetBuildingIds, " ")) + ADRunsController.resolveMarkers(frame.getAdEnv(), updated) + frame:requestRebuild() + end) +end diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua index afc6367..5385b81 100644 --- a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua +++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua @@ -10,10 +10,8 @@ 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 -SmartPickupFrame.TAB_NAMES = {"spu_tab_overview", "spu_tab_supply", "spu_tab_outbound", "spu_tab_storage"} --- Hver fane viser kun runs af sin egen slags; Oversigt og Lagre opretter forsynings-runs. -SmartPickupFrame.TAB_KIND = {"supply", "supply", "outbound", "supply"} +SmartPickupFrame.TAB_FLOWS, SmartPickupFrame.TAB_STORAGE = 1, 2 +SmartPickupFrame.TAB_NAMES = {"spu_tab_flows", "spu_tab_storage"} SmartPickupFrame.POLICIES = {"off", "store", "sell", "sellElseStore"} -- Afvisningsgrunde der handler om VOGNEN (ikke run'ets opsætning): traktoren kan ikke vælges. SmartPickupFrame.VEHICLE_BLOCKING_REASONS = {noTrailer = true, cannotCarry = true, needsAutoload = true} @@ -34,7 +32,7 @@ function SmartPickupFrame.new(i18n, getAdEnv) self.pendingRebuild = false self.frameOpen = false self.focusLost = false - self.currentTab = SmartPickupFrame.TAB_OVERVIEW + self.currentTab = SmartPickupFrame.TAB_FLOWS self.subCategoryTabs = {} self.backButtonInfo = {inputAction = InputAction.MENU_BACK} self.startButtonInfo = {inputAction = InputAction.MENU_ACTIVATE, text = i18n:getText("spu_button_start"), callback = function() self:onClickStartStop() end} @@ -121,9 +119,6 @@ function SmartPickupFrame.getStepIndex(steps, value, default) return index end -function SmartPickupFrame:getTabKind() - return SmartPickupFrame.TAB_KIND[self.currentTab] or "supply" -end function SmartPickupFrame:getReturnBelowIndex(run) local index = 1 @@ -246,13 +241,15 @@ function SmartPickupFrame:onClickTab(state) self:switchTab(state) end --- De faste rækker hører til hver sin fane: Run + Wait-pulje er fælles, Varer/Ture/tærskel er --- Forsyning, Sælg fra/Kør til lager/Saml op er Udkørsel. Oversigt viser ingen. -function SmartPickupFrame:setStaticRowsVisible(tab) - local isSupply, isOutbound = tab == SmartPickupFrame.TAB_SUPPLY, tab == SmartPickupFrame.TAB_OUTBOUND +-- De faste rækker: Flow-vælger + Wait-pulje for alle flows; Ture/kør hjem for forsyning; Sælg fra/ +-- Kør til lager for udkørsel. Fanen Lagre viser ingen. +function SmartPickupFrame:setStaticRowsVisible(tab, run) + local isFlows = tab == SmartPickupFrame.TAB_FLOWS + local hasRun = isFlows and run ~= nil + local isSupply, isOutbound = hasRun and run.kind ~= "outbound", hasRun and run.kind == "outbound" local visibility = { - {self.runOption, isSupply or isOutbound}, - {self.waitPoolOption, isSupply or isOutbound}, + {self.runOption, isFlows}, + {self.waitPoolOption, hasRun}, {self.loopsOption, isSupply}, {self.returnBelowOption, isSupply}, {self.sellAtOption, isOutbound}, {self.storeAboveOption, isOutbound}, } @@ -264,15 +261,8 @@ function SmartPickupFrame:setStaticRowsVisible(tab) end end --- Run'et der vises: kun runs af fanens slags; skifter fanen, vælges fanens første run. function SmartPickupFrame:getRunsForTab() - local kind, result = self:getTabKind(), {} - for _, run in ipairs(ADRunsController.store.runs) do - if (run.kind or "supply") == kind then - table.insert(result, run) - end - end - return result + return ADRunsController.store.runs end function SmartPickupFrame:getRun() @@ -298,7 +288,8 @@ function SmartPickupFrame:clearRows() self.overviewRows = {} self.productRows, self.outboundStatusRows = {}, {} self.storageRows = {} - self.ingredientModeRowOption, self.collectRoundRowOption = nil, nil + self.boardRows = {} + self.ingredientModeRowOption, self.collectRoundRowOption, self.directionRowOption = nil, nil, nil end -- Ja/Nej-række som klon af ingredientPrefab (faste BinaryOption-elementer fra XML'en viser kun @@ -351,14 +342,12 @@ end function SmartPickupFrame:rebuild() local adEnv = self.getAdEnv() local store = ADRunsController.store - local tabRuns = self:getRunsForTab() - local selected = self.selectedRunId ~= nil and ADRuns.getRun(store, self.selectedRunId) or nil - if selected == nil or (selected.kind or "supply") ~= self:getTabKind() then - self.selectedRunId = tabRuns[1] ~= nil and tabRuns[1].id or nil + if self.selectedRunId == nil or ADRuns.getRun(store, self.selectedRunId) == nil then + self.selectedRunId = store.runs[1] ~= nil and store.runs[1].id or nil end self:clearRows() - local isOverview = self.currentTab == SmartPickupFrame.TAB_OVERVIEW - self:setStaticRowsVisible(self.currentTab) + local run = self:getRun() + self:setStaticRowsVisible(self.currentTab, run) if self.currentTab == SmartPickupFrame.TAB_STORAGE then if adEnv ~= nil then self:fillStorageRows(adEnv) @@ -370,39 +359,26 @@ function SmartPickupFrame:rebuild() self:refreshLive() return end - if isOverview then - if adEnv ~= nil then - self:fillOverviewRows(adEnv) - end - self:addTextRow(self.i18n:getText("spu_waitPools")) - self:fillPoolRows(adEnv) - self.statusRow = self:addTextRow("") - self:colorRows() - self.boxLayout:invalidateLayout() - self:restoreFocusIfLost() - self:refreshLive() - return - end self:fillRunOption(adEnv) - local run = self:getRun() local hasRun = run ~= nil and adEnv ~= nil for _, element in ipairs({self.waitPoolOption, self.loopsOption, self.returnBelowOption, self.sellAtOption, self.storeAboveOption}) do if element ~= nil then element:setDisabled(not hasRun) end end + if adEnv ~= nil then + self:fillBoardRows(adEnv) + end if hasRun then self:fillWaitPoolOption(adEnv, run) - if self.currentTab == SmartPickupFrame.TAB_OUTBOUND then - self:rebuildOutboundTab(adEnv, run) - else - self:rebuildSupplyTab(adEnv, run) - end + self:rebuildFlowDetail(adEnv, run) self:addTextRow(self.i18n:getText("spu_section_vehicles")) self:fillVehicleRows(adEnv, run) self:addTextRow(self.i18n:getText("spu_section_status")) end self.statusRow = self:addTextRow("") + self:addTextRow(self.i18n:getText("spu_waitPools")) + self:fillPoolRows(adEnv) self:colorRows() self.boxLayout:invalidateLayout() self:refreshLive() @@ -498,7 +474,11 @@ function SmartPickupFrame:describeVehicle(adEnv, run, vehicle, isInRun) if validation.ok then table.insert(parts, "OK") else - table.insert(parts, "X " .. self.i18n:getText("spu_reason_" .. tostring(validation.reason))) + local reason = self.i18n:getText("spu_reason_" .. tostring(validation.reason)) + if validation.reason == "noMarker" and validation.missingBuilding ~= nil then + reason = string.format(reason, tostring(validation.missingBuilding)) + end + table.insert(parts, "X " .. reason) end return table.concat(parts, " — ") end @@ -535,6 +515,7 @@ function SmartPickupFrame:syncOptionsFromModel(adEnv, run) if group == run.waitPoolGroup then poolIndex = index end end sync(self.waitPoolOption, poolIndex) + sync(self.directionRowOption, run.kind == "outbound" and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF) if run.kind == "outbound" then sync(self.sellAtOption, SmartPickupFrame.getStepIndex(self.sellAtSteps, run.sellAtPercent, ADRuns.DEFAULT_SELL_AT_PERCENT)) sync(self.storeAboveOption, SmartPickupFrame.getStepIndex(self.storeAboveSteps, run.storeAbovePercent, ADRuns.DEFAULT_STORE_ABOVE_PERCENT)) @@ -588,74 +569,6 @@ function SmartPickupFrame:describeActivity(adEnv, vehicle) 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 - 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 = {}, targets = {}} - 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 - if run.kind == "outbound" then - entry.outbound = add(" " .. self:describeOutboundOverview(adEnv, run)) - else - for _, target in ipairs(ADRunsController.getTargetsLevels(adEnv, run)) do - table.insert(entry.targets, {wayPointId = target.wayPointId, title = add(" " .. self:describeTargetLevels(target))}) - end - end - 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.outbound ~= nil then - entry.outbound:setText(" " .. self:describeOutboundOverview(adEnv, entry.run)) - end - local byId = {} - for _, target in ipairs(ADRunsController.getTargetsLevels(adEnv, entry.run)) do - byId[target.wayPointId] = target - end - for _, item in ipairs(entry.targets) do - if byId[item.wayPointId] ~= nil then - item.title:setText(" " .. self:describeTargetLevels(byId[item.wayPointId])) - end - end - end -end - function SmartPickupFrame:fillPoolRows(adEnv) if adEnv == nil then return @@ -697,7 +610,9 @@ function SmartPickupFrame:refreshLive() self:setStatusText(self.i18n:getText("spu_status_noAd")) return end - self:refreshOverview(adEnv) + if self.currentTab == SmartPickupFrame.TAB_FLOWS then + self:refreshBoard(adEnv) + end self:refreshStorageRows(adEnv) local isRunning = false if run ~= nil then @@ -754,6 +669,10 @@ function SmartPickupFrame:onClickIngredient(state, element) return self:onClickIngredientMode(state) elseif data ~= nil and data.kind == "collectRound" then return self:onClickCollectRound(state) + elseif data ~= nil and data.kind == "direction" then + return self:onClickDirection(state) + elseif data ~= nil and (data.kind == "sourceBuilding" or data.kind == "targetBuilding") then + return self:onClickBuilding(state, element) end self:safe(function(frame) local run, data = frame:getRun(), frame.rowData[element] @@ -811,7 +730,11 @@ function SmartPickupFrame:onClickStartStop() 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)))) + local reasonText = frame.i18n:getText("spu_reason_" .. tostring(result.reason)) + if result.reason == "noMarker" and result.missingBuilding ~= nil then + reasonText = string.format(reasonText, tostring(result.missingBuilding)) + end + table.insert(reasons, string.format("%s: %s", tostring(result.vehicle:getName()), reasonText)) end end frame.statusOverride = string.format(frame.i18n:getText("spu_status_started"), started, skipped) @@ -839,13 +762,11 @@ function SmartPickupFrame:onNewRunName(value, clickOk) if not clickOk or value == nil or not value:match("%S") then return end - local kind = frame:getTabKind() - local run = ADRunsController.createRun(value, kind) + local run = ADRunsController.createRun(value, "supply") frame.selectedRunId = run.id frame.statusOverride = nil - local tab = kind == "outbound" and SmartPickupFrame.TAB_OUTBOUND or SmartPickupFrame.TAB_SUPPLY - frame.currentTab = tab - frame:switchTab(tab) + frame.currentTab = SmartPickupFrame.TAB_FLOWS + frame:switchTab(SmartPickupFrame.TAB_FLOWS) end) end diff --git a/FS25_ADSmartPickup/l10n/l10n_da.xml b/FS25_ADSmartPickup/l10n/l10n_da.xml index 7c1a9c6..0b3492b 100644 --- a/FS25_ADSmartPickup/l10n/l10n_da.xml +++ b/FS25_ADSmartPickup/l10n/l10n_da.xml @@ -101,5 +101,28 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FS25_ADSmartPickup/l10n/l10n_en.xml b/FS25_ADSmartPickup/l10n/l10n_en.xml index 33c7f1d..01251cc 100644 --- a/FS25_ADSmartPickup/l10n/l10n_en.xml +++ b/FS25_ADSmartPickup/l10n/l10n_en.xml @@ -101,5 +101,28 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index 7de5a80..130e0dc 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.12.21.0 + 1.13.0.0 <en>AD Smart Pickup</en> @@ -21,6 +21,7 @@ + diff --git a/README.md b/README.md index 257cbe0..9b64d9c 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,21 @@ vare valgt betyder ingen bytte-mulighed, og traktoren venter som hidtil. Wait-lo kun mens aflæsningsopgaven peger på AD's rigtige aflæsningsmål, så en silo-omvej (fx efter et bytte) aldrig fejlagtigt bliver opfattet som et fuldt mål. +## v1.13 — Flow-tavle (bygningsbaseret opsætning) + +ESC-fanen har nu to under-faner: **Flows** og **Lagre**. Flows viser én række pr. flow (run): +`> Mælk: Sukker · Kostald 1 + Kostald 2 → salg · pris ≥ 90 %, lager > 80 % · Virtus · kører (1/1) — SUGAR 78 % af top` +og under tavlen en detail-blok for det valgte flow: **Retning** (ind til bygning / ud fra bygning), +**Fra** (udkørsel) eller **Til** (forsyning) som afkrydsning pr. **bygning** — moden finder selv den +AD-markør der står nærmest bygningens læsse-/aflæsningstrigger (≤ 40 m; siloer via det strenge +opslag) og viser den: `Kostald 1 (stald) — markør 'cow 1 milk' 12 m` eller `INGEN MARKØR inden for +40 m`. Derefter Varer, Regler, Vent ved, Traktorer og Status som før. Forsyning har "Fra = alle +siloer" (som hidtil). Wait-puljerne står nederst. + +Gamle runs oversættes automatisk: markører der er en bygnings markør bliver til bygningen; rå +markører uden bygning beholdes og vises som `markør '…'`. Start afviser med `'' mangler en +AD-markør ved triggeren`, når en valgt bygning ingen markør har. Runs gemmes som XML version 3. + ## v1.12 — Udkørsel af produkter Et run har nu en **retning**: *Forsyning* (som hidtil) eller *Udkørsel*. ESC-fanen har tre