diff --git a/FS25_ADSmartPickup/adChain.lua b/FS25_ADSmartPickup/adChain.lua
index e91f581..7bf55fd 100644
--- a/FS25_ADSmartPickup/adChain.lua
+++ b/FS25_ADSmartPickup/adChain.lua
@@ -391,6 +391,8 @@ end
-- Engangsbesked pr. nyt manglende led (kun fra tick). Ikke i opstartsfristen og ikke mens gården er læst delvist
-- eller markscanningen kører (ellers falske alarmer lige efter indlæsning) — så huskes intet, og det meldes senere.
+-- Højst MAX_PROBLEM_NOTES beskeder pr. tick; resten samles i én besked (alle logges stadig).
+ADChain.MAX_PROBLEM_NOTES = 3
function ADChain.notifyProblems(last)
if last == nil or ADChain.now < ADChain.STARTUP_GRACE_MS then return 0 end
if last.fieldsIncomplete or (last.data ~= nil and last.data.partial) then return 0 end
@@ -403,11 +405,16 @@ function ADChain.notifyProblems(last)
for _, root in ipairs(last.chain ~= nil and last.chain.roots or {}) do
rootNames[root.key] = tostring((last.titles or {})[root.name] or root.name)
end
- for _, problem in ipairs(fresh) do
+ for index, problem in ipairs(fresh) do
-- kædens navn foran, så to kæder der mangler samme vare kan skelnes
local text = tostring(rootNames[problem.rootKey] or problem.rootKey) .. ": " .. ADChainView.problemText(problem, last, textOf, nil, fmt)
Logging.info(ADChain.LOG_PREFIX .. "manglende led: %s", text)
- ADFieldJobs.notify(text, true)
+ if index <= ADChain.MAX_PROBLEM_NOTES then ADFieldJobs.notify(text, true) end
+ end
+ -- resten samles i én besked (fx mange fermenteringssiloer efter indlæsning); alle står i fanen Kæder
+ if #fresh > ADChain.MAX_PROBLEM_NOTES then
+ local ok, text = pcall(string.format, textOf("spu_chain_notifyMore"), #fresh - ADChain.MAX_PROBLEM_NOTES)
+ ADFieldJobs.notify(ok and text or ("+" .. tostring(#fresh - ADChain.MAX_PROBLEM_NOTES)), true)
end
return #fresh
end
diff --git a/FS25_ADSmartPickup/adStorageStats.lua b/FS25_ADSmartPickup/adStorageStats.lua
index 1042d67..160c14e 100644
--- a/FS25_ADSmartPickup/adStorageStats.lua
+++ b/FS25_ADSmartPickup/adStorageStats.lua
@@ -279,13 +279,11 @@ end
-- "Kræver handling" fra kæden (ADChain.last): ÉT gult punkt "Kæde: N forslag" (klik -> fanen Kæder), så
-- forslagene ikke fylder listen. Rangeres under alle røde punkter (>= 3000), øverst blandt de gule, over "sælg nu".
ADStorageStats.CHAIN_SCORE = 2000
--- Manglende led: én rød post pr. kæde ("Brød mangler et led: Mel"), højst MAX_CHAIN_PROBLEMS (resten står i
--- fanen Kæder; den sidste viste post får more = antal skjulte). Score CHAIN_PROBLEM_SCORE + alvor (0-99): under
--- "rød tom" (3900-4000: løber tør inden et døgn), over "rød fuld" (3000-3100). Alvor = manglende andel af behovet;
--- kredsløb (kan ikke beregnes) = 99.
-ADStorageStats.CHAIN_PROBLEM_SCORE = 3500
-ADStorageStats.MAX_CHAIN_PROBLEMS = 2
-ADStorageStats.MAX_CHAIN_PROBLEM_ITEMS = 2
+-- Manglende led: ALLE kædeproblemer i ÉN rød post ("Kæder: N manglende led", klik -> fanen Kæder med kæderne
+-- foldet ud), så en gård med mange kæder (fx fermenteringssiloer) ikke fylder "Kræver handling". N = unikke
+-- (kæde, vare). Score CHAIN_PROBLEM_SCORE + alvor (0-99): under "rød fuld" (3000-3100), så lagerets røde poster
+-- altid vises først, og over de gule (<= 2000). Alvor = manglende andel af behovet; kredsløb = 99.
+ADStorageStats.CHAIN_PROBLEM_SCORE = 2900
local function severity(problems)
local missing, need = 0, 0
@@ -297,50 +295,33 @@ local function severity(problems)
return math.floor(99 * math.min(1, missing / need))
end
-local function problemEntry(root, problems, titleOf)
- local items, seen = {}, {}
- for _, problem in ipairs(problems) do
- local title = titleOf(problem.fillType)
- if not seen[title] then
- seen[title] = true
- table.insert(items, title)
- end
- end
- local shown = {}
- for index = 1, math.min(#items, ADStorageStats.MAX_CHAIN_PROBLEM_ITEMS) do shown[index] = items[index] end
- local itemsText = table.concat(shown, ", ")
- if #items > #shown then itemsText = itemsText .. " +" .. tostring(#items - #shown) end
- local rootTitle = titleOf(root.name)
- return {kind = "chainProblem", level = "red", score = ADStorageStats.CHAIN_PROBLEM_SCORE + severity(problems), rootKey = root.key,
- rootTitle = rootTitle, itemsText = itemsText, text = rootTitle .. ": " .. itemsText}
-end
-
local function chainProblemEntries(last)
- local titles = last.titles or {}
- local titleOf = function(name) return tostring(titles[name] or name) end
- local byRoot = {}
+ local known = {}
+ for _, root in ipairs(last.chain ~= nil and last.chain.roots or {}) do known[root.key] = true end
+ local problems, seen, rootKeys, rootSeen = {}, {}, {}, {}
for _, problem in ipairs(last.chain ~= nil and last.chain.problems or {}) do
- if problem.rootKey ~= nil then
- byRoot[problem.rootKey] = byRoot[problem.rootKey] or {}
- table.insert(byRoot[problem.rootKey], problem)
+ if problem.rootKey ~= nil and known[problem.rootKey] then
+ table.insert(problems, problem)
+ seen[tostring(problem.rootKey) .. "|" .. tostring(problem.fillType)] = true
+ if not rootSeen[problem.rootKey] then
+ rootSeen[problem.rootKey] = true
+ table.insert(rootKeys, problem.rootKey)
+ end
end
end
- local entries = {}
- for _, root in ipairs(last.chain ~= nil and last.chain.roots or {}) do
- if byRoot[root.key] ~= nil then table.insert(entries, problemEntry(root, byRoot[root.key], titleOf)) end
- end
- entries = ADStorageStats.rankAttention(entries)
- local result = {}
- for index = 1, math.min(#entries, ADStorageStats.MAX_CHAIN_PROBLEMS) do result[index] = entries[index] end
- if #entries > #result then result[#result].more = #entries - #result end
- return result
+ if #problems == 0 then return {} end
+ local count = 0
+ for _ in pairs(seen) do count = count + 1 end
+ table.sort(rootKeys, function(a, b) return tostring(a) < tostring(b) end)
+ return {{kind = "chainProblem", level = "red", score = ADStorageStats.CHAIN_PROBLEM_SCORE + severity(problems), count = count,
+ rootKeys = rootKeys, text = string.format("Kæder: %d", count)}}
end
--- "Slukket men nødvendig" (ADChain.last.dependency.offNeeded): én rød post pr. fabrik, højst MAX_DEPENDENCY_ENTRIES
--- (den sidste får more = antal skjulte). Score DEPENDENCY_SCORE + hast (0-99, færre lagerdage = højere; ukendt = 50):
--- over manglende led (3500-3599), under "rød tom" (3900-4000).
+-- "Slukket men nødvendig" (ADChain.last.dependency.offNeeded): højst MAX_DEPENDENCY_ENTRIES post (den mest hastende
+-- fabrik; more = antal skjulte, vist som "+k"). Score DEPENDENCY_SCORE + hast (0-99, færre lagerdage = højere;
+-- ukendt = 50): over "rød fuld" (3000-3100), under "rød tom" (3900-4000).
ADStorageStats.DEPENDENCY_SCORE = 3700
-ADStorageStats.MAX_DEPENDENCY_ENTRIES = 2
+ADStorageStats.MAX_DEPENDENCY_ENTRIES = 1
local function urgency(days)
if days == nil then return 50 end
diff --git a/FS25_ADSmartPickup/gui/SmartPickupAttentionFlow.lua b/FS25_ADSmartPickup/gui/SmartPickupAttentionFlow.lua
index d530b75..30eeeb1 100644
--- a/FS25_ADSmartPickup/gui/SmartPickupAttentionFlow.lua
+++ b/FS25_ADSmartPickup/gui/SmartPickupAttentionFlow.lua
@@ -106,10 +106,10 @@ end
function SmartPickupFrame:onAttentionClick(entry)
self:safe(function(frame)
if entry ~= nil and (entry.kind == "chainSuggest" or entry.kind == "chainProblem") then
- -- manglende led: kæden foldes ud, så problemteksten står under den røde linje
- if entry.kind == "chainProblem" and entry.rootKey ~= nil then
+ -- manglende led: kæderne med problemer foldes ud, så problemteksten står under den røde linje
+ if entry.kind == "chainProblem" then
frame.chainExpanded = frame.chainExpanded or {}
- frame.chainExpanded[entry.rootKey] = true
+ for _, rootKey in ipairs(entry.rootKeys or {}) do frame.chainExpanded[rootKey] = true end
end
frame:switchTab(SmartPickupFrame.TAB_CHAINS)
return
diff --git a/FS25_ADSmartPickup/gui/SmartPickupStorageView.lua b/FS25_ADSmartPickup/gui/SmartPickupStorageView.lua
index 932df21..bf3c618 100644
--- a/FS25_ADSmartPickup/gui/SmartPickupStorageView.lua
+++ b/FS25_ADSmartPickup/gui/SmartPickupStorageView.lua
@@ -202,14 +202,13 @@ function SmartPickupFrame:alertText(entry)
return string.format(i18n:getText("spu_attention_chainSuggests"), entry.count or 0)
end
if entry.kind == "chainProblem" then
- local text = string.format(i18n:getText("spu_attention_chainProblem"), tostring(entry.rootTitle), tostring(entry.itemsText))
- if (entry.more or 0) > 0 then text = text .. string.format(" (+%d)", entry.more) end
- return text
+ return string.format(i18n:getText("spu_attention_chainProblems"), entry.count or 0)
end
if entry.kind == "dependencyOff" then
- local text = string.format(i18n:getText("spu_attention_depOff"), tostring(entry.buildingName))
- if (entry.more or 0) > 0 then text = text .. string.format(" (+%d)", entry.more) end
- return text
+ if (entry.more or 0) > 0 then
+ return string.format(i18n:getText("spu_attention_depOffMore"), tostring(entry.buildingName), entry.more)
+ end
+ return string.format(i18n:getText("spu_attention_depOff"), tostring(entry.buildingName))
end
if entry.kind == "empty" then
return string.format(i18n:getText("spu_alert_empty"), stat.title, self:formatDays(stat.days), fc(stat.farmLevel or 0), fc(math.abs(stat.net or 0)))
diff --git a/FS25_ADSmartPickup/l10n/l10n_da.xml b/FS25_ADSmartPickup/l10n/l10n_da.xml
index 7304767..2e054a7 100644
--- a/FS25_ADSmartPickup/l10n/l10n_da.xml
+++ b/FS25_ADSmartPickup/l10n/l10n_da.xml
@@ -599,6 +599,7 @@
+
@@ -616,7 +617,7 @@
-
+
@@ -636,6 +637,7 @@
+
diff --git a/FS25_ADSmartPickup/l10n/l10n_en.xml b/FS25_ADSmartPickup/l10n/l10n_en.xml
index 0963905..780764a 100644
--- a/FS25_ADSmartPickup/l10n/l10n_en.xml
+++ b/FS25_ADSmartPickup/l10n/l10n_en.xml
@@ -599,6 +599,7 @@
+
@@ -616,7 +617,7 @@
-
+
@@ -636,6 +637,7 @@
+