feat(gui): guiden "Opsæt flow" — Flows-fanen får tavle + resumé (klik = ret ét trin, Navn = omdøb) og trin-for-trin-guide på samme ramme som markguiden; Nyt flow oprettes først ved Gem (v1.32.0.0)
This commit is contained in:
@@ -0,0 +1,447 @@
|
|||||||
|
-- AD Flow Guide
|
||||||
|
-- "Opsæt flow": flowguidens trin oven på ADGuide (ren Lua, ingen spil-API) — samme trin-skærm og knapper
|
||||||
|
-- som markguiden. Kladden er en kopi af flowets felter, så intet ændres før [Gem]/[Start flow]. Det
|
||||||
|
-- spil-afhængige kommer i `context` (GUI'et henter det ud fra kladden, se previewRun):
|
||||||
|
-- {buildings = {{id, name, label, hasMarker, text}} -- kandidater til Fra (udkørsel) / Til (forsyning)
|
||||||
|
-- fillTypes = {{name, title}} -- varer målet tager (forsyning, Vælg selv)
|
||||||
|
-- products = {{name, title}} -- varer kilderne har (udkørsel)
|
||||||
|
-- storeBuildings = {{id, name, text}} -- lagre der kan tage flowets lagervarer
|
||||||
|
-- waitPools = {{group, count}}
|
||||||
|
-- vehicles = {{id, label, text, otherRunId, otherRunName, blocked}}} -- label = kort navn (resumé)
|
||||||
|
-- Gem: ADRunsController.createRun/updateRun + assignVehicle/unassignVehicle (GUI'et).
|
||||||
|
|
||||||
|
ADFlowGuide = {}
|
||||||
|
|
||||||
|
ADFlowGuide.ORDER = {"name", "direction", "buildings", "goods", "store", "rules", "wait", "tractors", "summary"}
|
||||||
|
ADFlowGuide.POLICIES = {"off", "store", "sell", "sellElseStore"}
|
||||||
|
ADFlowGuide.MAX_NAMED_TRACTORS = 3
|
||||||
|
ADFlowGuide.MAX_NAMED_GOODS = 3
|
||||||
|
|
||||||
|
local function L(key, args) return {key = key, args = args} end
|
||||||
|
local function T(text) return {text = text} end
|
||||||
|
|
||||||
|
local function isOut(guide) return guide.draft.kind == "outbound" end
|
||||||
|
|
||||||
|
local function hasStorePolicy(draft)
|
||||||
|
for _, product in ipairs(draft.products or {}) do
|
||||||
|
if product.policy == "store" or product.policy == "sellElseStore" then return true end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local VISIBLE = {
|
||||||
|
name = function(guide) return guide.draft.isNew == true end,
|
||||||
|
store = function(guide) return isOut(guide) and hasStorePolicy(guide.draft) end,
|
||||||
|
}
|
||||||
|
|
||||||
|
function ADFlowGuide.isVisible(step, guide)
|
||||||
|
local rule = VISIBLE[step]
|
||||||
|
if rule ~= nil then return rule(guide) == true end
|
||||||
|
for _, id in ipairs(ADFlowGuide.ORDER) do
|
||||||
|
if id == step then return true end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
ADFlowGuide.DEF = {order = ADFlowGuide.ORDER, isVisible = ADFlowGuide.isVisible}
|
||||||
|
|
||||||
|
-- Overskrift pr. trin; "Fra"/"Til" følger retningen.
|
||||||
|
function ADFlowGuide.stepKey(step, guide)
|
||||||
|
if step == "buildings" then
|
||||||
|
return (guide ~= nil and isOut(guide)) and "spu_guide_flow_step_from" or "spu_guide_flow_step_to"
|
||||||
|
end
|
||||||
|
return "spu_guide_flow_step_" .. tostring(step)
|
||||||
|
end
|
||||||
|
|
||||||
|
local KIND_HINTS = {buildings = true, goods = true, rules = true}
|
||||||
|
function ADFlowGuide.hintKey(step, guide)
|
||||||
|
if KIND_HINTS[step] then
|
||||||
|
return "spu_guide_flow_hint_" .. step .. ((guide ~= nil and isOut(guide)) and "_out" or "_in")
|
||||||
|
end
|
||||||
|
return "spu_guide_flow_hint_" .. tostring(step)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ---- kladde ----
|
||||||
|
|
||||||
|
local RUN_FIELDS = {
|
||||||
|
"id", "name", "kind", "sourceBuildingIds", "targetBuildingIds", "storeBuildingIds", "products",
|
||||||
|
"ingredientMode", "ingredients", "sellAtPercent", "storeAbovePercent", "collectRound", "loops",
|
||||||
|
"returnBelowPercent", "waitPoolGroup", "vehicleIds",
|
||||||
|
}
|
||||||
|
|
||||||
|
local function draftFrom(run, isNew)
|
||||||
|
local draft = {}
|
||||||
|
for _, key in ipairs(RUN_FIELDS) do draft[key] = ADGuide.deepCopy(run[key]) end
|
||||||
|
for _, key in ipairs({"sourceBuildingIds", "targetBuildingIds", "storeBuildingIds", "products", "ingredients", "vehicleIds"}) do
|
||||||
|
draft[key] = draft[key] or {}
|
||||||
|
end
|
||||||
|
draft.kind = draft.kind == "outbound" and "outbound" or "supply"
|
||||||
|
draft.ingredientMode = draft.ingredientMode == "manual" and "manual" or "auto"
|
||||||
|
draft.sellAtPercent = draft.sellAtPercent or ADRuns.DEFAULT_SELL_AT_PERCENT
|
||||||
|
draft.storeAbovePercent = draft.storeAbovePercent or ADRuns.DEFAULT_STORE_ABOVE_PERCENT
|
||||||
|
draft.returnBelowPercent = draft.returnBelowPercent or ADRuns.DEFAULT_RETURN_BELOW_PERCENT
|
||||||
|
draft.loops = draft.loops or 0
|
||||||
|
draft.waitPoolGroup = draft.waitPoolGroup or ""
|
||||||
|
draft.collectRound = draft.collectRound == true
|
||||||
|
draft.isNew = isNew == true
|
||||||
|
draft.showAllBuildings = false
|
||||||
|
draft.storeAuto = #draft.storeBuildingIds == 0
|
||||||
|
return draft
|
||||||
|
end
|
||||||
|
|
||||||
|
function ADFlowGuide.open(run, context, options)
|
||||||
|
return ADGuide.new(ADFlowGuide.DEF, draftFrom(run, false), context, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Nyt flow: intet oprettes før [Gem]; kladden har standardværdierne fra ADRuns.createRun.
|
||||||
|
function ADFlowGuide.openNew(name, context)
|
||||||
|
local _, run = ADRuns.createRun(ADRuns.newStore(), name, "supply")
|
||||||
|
local draft = draftFrom(run, true)
|
||||||
|
draft.id = nil
|
||||||
|
return ADGuide.new(ADFlowGuide.DEF, draft, context)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Run-formet tabel til controllerens opslag (varer, vogne) før noget er gemt: markører fra de valgte
|
||||||
|
-- bygninger. markerIds(buildingId, role) -> {wayPointId, ...}.
|
||||||
|
function ADFlowGuide.previewRun(draft, markerIds)
|
||||||
|
local run = ADGuide.deepCopy(draft)
|
||||||
|
local function resolve(ids, role)
|
||||||
|
local result, seen = {}, {}
|
||||||
|
for _, buildingId in ipairs(ids or {}) do
|
||||||
|
for _, wayPointId in ipairs(markerIds(buildingId, role) or {}) do
|
||||||
|
if not seen[wayPointId] then
|
||||||
|
seen[wayPointId] = true
|
||||||
|
table.insert(result, wayPointId)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
run.sourceWayPointIds = resolve(draft.sourceBuildingIds, "load")
|
||||||
|
run.targetWayPointIds = resolve(draft.targetBuildingIds, "unload")
|
||||||
|
run.targetWayPointId = run.targetWayPointIds[1]
|
||||||
|
return run
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ---- valg ----
|
||||||
|
|
||||||
|
local SET_VALUES = {
|
||||||
|
kind = true, ingredientMode = true, sellAtPercent = true, storeAbovePercent = true, loops = true,
|
||||||
|
returnBelowPercent = true, waitPoolGroup = true,
|
||||||
|
}
|
||||||
|
local BOOL_VALUES = {showAllBuildings = true, storeAuto = true, collectRound = true}
|
||||||
|
local LIST_VALUES = {ingredients = true, storeBuildingIds = true, vehicleIds = true}
|
||||||
|
|
||||||
|
local function setPolicy(products, name, policy)
|
||||||
|
local result = {}
|
||||||
|
for _, product in ipairs(products or {}) do
|
||||||
|
if product.fillType ~= name then table.insert(result, {fillType = product.fillType, policy = product.policy}) end
|
||||||
|
end
|
||||||
|
if policy ~= "off" then table.insert(result, {fillType = name, policy = policy}) end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function ADFlowGuide.choose(guide, id, value)
|
||||||
|
local draft = ADGuide.deepCopy(guide.draft)
|
||||||
|
if SET_VALUES[id] then
|
||||||
|
draft[id] = value
|
||||||
|
elseif BOOL_VALUES[id] then
|
||||||
|
draft[id] = value == true
|
||||||
|
elseif LIST_VALUES[id] then
|
||||||
|
draft[id] = ADGuide.toggleItem(draft[id], value.item, value.on)
|
||||||
|
elseif id == "buildings" then
|
||||||
|
local key = draft.kind == "outbound" and "sourceBuildingIds" or "targetBuildingIds"
|
||||||
|
draft[key] = ADGuide.toggleItem(draft[key], value.item, value.on)
|
||||||
|
elseif id == "policy" then
|
||||||
|
draft.products = setPolicy(draft.products, value.item, value.value)
|
||||||
|
elseif id == "name" then
|
||||||
|
if type(value) ~= "string" or not value:match("%S") then return guide end
|
||||||
|
draft.name = value
|
||||||
|
else
|
||||||
|
return guide
|
||||||
|
end
|
||||||
|
return ADGuide.withDraft(guide, draft)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ---- rækker ----
|
||||||
|
|
||||||
|
local function choice(id, title, options, value, item)
|
||||||
|
return {type = "choice", id = id, title = title, options = options, value = value, item = item}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function toggle(id, title, value)
|
||||||
|
return {type = "toggle", id = id, title = title, off = L("ui_no"), on = L("ui_yes"), value = value == true}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function check(id, item, text, value)
|
||||||
|
return {type = "check", id = id, item = item, title = T(text), value = value == true}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function text(key, args) return {type = "text", label = L(key, args)} end
|
||||||
|
|
||||||
|
local function percentOptions(from, to, step)
|
||||||
|
local options = {}
|
||||||
|
for percent = from, to, step do table.insert(options, {label = T(string.format("%d %%", percent)), value = percent}) end
|
||||||
|
return options
|
||||||
|
end
|
||||||
|
|
||||||
|
local function chosenSet(list)
|
||||||
|
local set = {}
|
||||||
|
for _, value in ipairs(list or {}) do set[value] = true end
|
||||||
|
return set
|
||||||
|
end
|
||||||
|
|
||||||
|
local function buildingIds(draft)
|
||||||
|
return draft.kind == "outbound" and draft.sourceBuildingIds or draft.targetBuildingIds
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rowsName(guide)
|
||||||
|
return {
|
||||||
|
{type = "text", label = T(guide.draft.name or "")},
|
||||||
|
{type = "link", id = "rename", value = true, label = L("spu_guide_flow_rename")},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rowsDirection(guide)
|
||||||
|
return {choice("kind", L("spu_direction"), {
|
||||||
|
{label = L("spu_direction_in"), value = "supply"}, {label = L("spu_direction_out"), value = "outbound"}}, guide.draft.kind)}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Bygninger i fast rækkefølge (navn, markør); uden markør kun med "Vis alle" (eller når valgt).
|
||||||
|
local function rowsBuildings(guide)
|
||||||
|
local draft, rows = guide.draft, {}
|
||||||
|
local chosen = chosenSet(buildingIds(draft))
|
||||||
|
local candidates = guide.context.buildings or {}
|
||||||
|
local shown = ADFlowView.buildingRows(candidates, chosen, draft.showAllBuildings)
|
||||||
|
local hidden = ADFlowView.hiddenCount(candidates, shown)
|
||||||
|
if hidden > 0 or draft.showAllBuildings then
|
||||||
|
table.insert(rows, toggle("showAllBuildings", L("spu_showAllBuildings", {hidden}), draft.showAllBuildings))
|
||||||
|
end
|
||||||
|
for _, building in ipairs(shown) do
|
||||||
|
table.insert(rows, check("buildings", building.id, building.text or building.name, chosen[building.id]))
|
||||||
|
end
|
||||||
|
if #shown == 0 then table.insert(rows, text("spu_noBuildings")) end
|
||||||
|
return rows
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rowsGoods(guide)
|
||||||
|
local draft, context, rows = guide.draft, guide.context, {}
|
||||||
|
if isOut(guide) then
|
||||||
|
local products = context.products or {}
|
||||||
|
if #products == 0 then return {text("spu_guide_flow_noProducts")} end
|
||||||
|
local options = {}
|
||||||
|
for _, policy in ipairs(ADFlowGuide.POLICIES) do table.insert(options, {label = L("spu_policy_" .. policy), value = policy}) end
|
||||||
|
for _, product in ipairs(products) do
|
||||||
|
table.insert(rows, choice("policy", T(product.text or product.title), options, ADRuns.getProductPolicy(draft, product.name), product.name))
|
||||||
|
end
|
||||||
|
return rows
|
||||||
|
end
|
||||||
|
table.insert(rows, choice("ingredientMode", L("spu_guide_flow_goods"), {
|
||||||
|
{label = L("spu_guide_flow_goodsAutoChoice"), value = "auto"}, {label = L("spu_guide_manual"), value = "manual"}}, draft.ingredientMode))
|
||||||
|
if draft.ingredientMode ~= "manual" then
|
||||||
|
table.insert(rows, text("spu_guide_flow_goodsAutoText"))
|
||||||
|
return rows
|
||||||
|
end
|
||||||
|
local chosen = chosenSet(draft.ingredients)
|
||||||
|
for _, fillType in ipairs(context.fillTypes or {}) do
|
||||||
|
table.insert(rows, check("ingredients", fillType.name, " " .. tostring(fillType.title), chosen[fillType.name]))
|
||||||
|
end
|
||||||
|
if #(context.fillTypes or {}) == 0 then table.insert(rows, text("spu_guide_flow_noGoodsTo")) end
|
||||||
|
return rows
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rowsStore(guide)
|
||||||
|
local draft, rows = guide.draft, {}
|
||||||
|
table.insert(rows, choice("storeAuto", L("spu_guide_store"), {
|
||||||
|
{label = L("spu_guide_flow_storeAutoChoice"), value = true}, {label = L("spu_guide_manual"), value = false}}, draft.storeAuto))
|
||||||
|
if draft.storeAuto then
|
||||||
|
table.insert(rows, text("spu_guide_flow_storeAutoText"))
|
||||||
|
return rows
|
||||||
|
end
|
||||||
|
local chosen = chosenSet(draft.storeBuildingIds)
|
||||||
|
for _, building in ipairs(guide.context.storeBuildings or {}) do
|
||||||
|
table.insert(rows, check("storeBuildingIds", building.id, building.text, chosen[building.id]))
|
||||||
|
end
|
||||||
|
if #(guide.context.storeBuildings or {}) == 0 then table.insert(rows, text("spu_noBuildings")) end
|
||||||
|
return rows
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rowsRules(guide)
|
||||||
|
local draft = guide.draft
|
||||||
|
if isOut(guide) then
|
||||||
|
return {
|
||||||
|
choice("sellAtPercent", L("spu_sellAt"), percentOptions(ADRuns.MIN_SELL_AT_PERCENT, ADRuns.MAX_SELL_AT_PERCENT, 5), draft.sellAtPercent),
|
||||||
|
choice("storeAbovePercent", L("spu_storeAbove"), percentOptions(ADRuns.MIN_STORE_ABOVE_PERCENT, ADRuns.MAX_STORE_ABOVE_PERCENT, 5), draft.storeAbovePercent),
|
||||||
|
toggle("collectRound", L("spu_collectRound"), draft.collectRound),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
local loops = {{label = L("spu_loops_infinite"), value = 0}}
|
||||||
|
for count = 1, ADRuns.MAX_LOOPS do table.insert(loops, {label = T(tostring(count)), value = count}) end
|
||||||
|
return {
|
||||||
|
choice("loops", L("spu_loops"), loops, draft.loops),
|
||||||
|
choice("returnBelowPercent", L("spu_returnBelow"), percentOptions(ADRuns.MIN_RETURN_BELOW_PERCENT, ADRuns.MAX_RETURN_BELOW_PERCENT, 10), draft.returnBelowPercent),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rowsWait(guide)
|
||||||
|
local options = {{label = L("spu_waitPool_all"), value = ""}}
|
||||||
|
for _, pool in ipairs(guide.context.waitPools or {}) do
|
||||||
|
table.insert(options, {label = T(string.format("%s (%d)", tostring(pool.group), pool.count or 0)), value = pool.group})
|
||||||
|
end
|
||||||
|
return {choice("waitPoolGroup", L("spu_waitPool"), options, guide.draft.waitPoolGroup or "")}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Traktorer: flowets egne først, så de ledige. Traktorer i andre flows og blokerede (vognen passer ikke)
|
||||||
|
-- skjules med en linje hver.
|
||||||
|
local function rowsTractors(guide)
|
||||||
|
local draft, rows = guide.draft, {}
|
||||||
|
local inRun = chosenSet(draft.vehicleIds)
|
||||||
|
local entries = {}
|
||||||
|
local other, blocked = 0, 0
|
||||||
|
for _, vehicle in ipairs(guide.context.vehicles or {}) do
|
||||||
|
local state = "free"
|
||||||
|
if inRun[vehicle.id] then
|
||||||
|
state = "in"
|
||||||
|
elseif vehicle.otherRunId ~= nil and vehicle.otherRunId ~= draft.id then
|
||||||
|
state = "other"
|
||||||
|
other = other + 1
|
||||||
|
elseif vehicle.blocked then
|
||||||
|
state = "blocked"
|
||||||
|
blocked = blocked + 1
|
||||||
|
end
|
||||||
|
table.insert(entries, {id = vehicle.id, label = tostring(vehicle.text), state = state})
|
||||||
|
end
|
||||||
|
for _, entry in ipairs(ADFlowView.vehicleRows(entries, false)) do
|
||||||
|
table.insert(rows, check("vehicleIds", entry.id, entry.label, entry.state == "in"))
|
||||||
|
end
|
||||||
|
if #rows == 0 then table.insert(rows, text("spu_guide_flow_noTractors")) end
|
||||||
|
if other > 0 then table.insert(rows, text("spu_guide_flow_otherFlows", {other})) end
|
||||||
|
if blocked > 0 then table.insert(rows, text("spu_guide_flow_blocked", {blocked})) end
|
||||||
|
return rows
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rowsSummary(guide)
|
||||||
|
local rows = {}
|
||||||
|
for _, line in ipairs(ADFlowGuide.summary(guide)) do
|
||||||
|
table.insert(rows, {type = "link", id = "jump", value = line.step, line = line})
|
||||||
|
end
|
||||||
|
return rows
|
||||||
|
end
|
||||||
|
|
||||||
|
local ROWS = {
|
||||||
|
name = rowsName, direction = rowsDirection, buildings = rowsBuildings, goods = rowsGoods, store = rowsStore,
|
||||||
|
rules = rowsRules, wait = rowsWait, tractors = rowsTractors, summary = rowsSummary,
|
||||||
|
}
|
||||||
|
|
||||||
|
function ADFlowGuide.rows(guide)
|
||||||
|
local builder = ROWS[guide.step]
|
||||||
|
return builder ~= nil and builder(guide) or {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ---- resumé ----
|
||||||
|
|
||||||
|
-- Korte navne til resuméet (field, ellers navn, ellers rækkens tekst).
|
||||||
|
local function namesFor(ids, entries, field)
|
||||||
|
local byId = {}
|
||||||
|
for _, entry in ipairs(entries or {}) do byId[entry.id or entry.name] = entry[field] or entry.name or entry.text end
|
||||||
|
local names = {}
|
||||||
|
for _, id in ipairs(ids or {}) do table.insert(names, tostring(byId[id] or id)) end
|
||||||
|
return names
|
||||||
|
end
|
||||||
|
|
||||||
|
local SUMMARY = {
|
||||||
|
name = function(guide) return {T(guide.draft.name or "")} end,
|
||||||
|
direction = function(guide) return {L(isOut(guide) and "spu_direction_out" or "spu_direction_in")} end,
|
||||||
|
buildings = function(guide)
|
||||||
|
local names = namesFor(buildingIds(guide.draft), guide.context.buildings, "name")
|
||||||
|
if #names == 0 then return {L("spu_guide_flow_none")} end
|
||||||
|
return {T(ADFlowView.joinCounted(names, " + ", ""))}
|
||||||
|
end,
|
||||||
|
goods = function(guide)
|
||||||
|
local draft = guide.draft
|
||||||
|
if isOut(guide) then
|
||||||
|
local parts = {}
|
||||||
|
local titles = {}
|
||||||
|
for _, product in ipairs(guide.context.products or {}) do titles[product.name] = product.title end
|
||||||
|
for _, product in ipairs(draft.products or {}) do
|
||||||
|
table.insert(parts, {key = "spu_policy_" .. product.policy, prefix = tostring(titles[product.fillType] or product.fillType) .. ": "})
|
||||||
|
end
|
||||||
|
if #parts == 0 then return {L("spu_guide_flow_none")} end
|
||||||
|
return parts
|
||||||
|
end
|
||||||
|
if draft.ingredientMode ~= "manual" then return {L("spu_guide_flow_goodsAuto")} end
|
||||||
|
if #draft.ingredients == 0 then return {L("spu_guide_flow_none")} end
|
||||||
|
if #draft.ingredients > ADFlowGuide.MAX_NAMED_GOODS then return {L("spu_guide_flow_sumGoods", {#draft.ingredients})} end
|
||||||
|
local byName = {}
|
||||||
|
for _, fillType in ipairs(guide.context.fillTypes or {}) do byName[fillType.name] = fillType.title end
|
||||||
|
local names = {}
|
||||||
|
for _, name in ipairs(draft.ingredients) do table.insert(names, tostring(byName[name] or name)) end
|
||||||
|
return {T(table.concat(names, " + "))}
|
||||||
|
end,
|
||||||
|
store = function(guide)
|
||||||
|
local draft = guide.draft
|
||||||
|
if draft.storeAuto or #draft.storeBuildingIds == 0 then return {L("spu_guide_sum_auto")} end
|
||||||
|
if #draft.storeBuildingIds > 2 then return {L("spu_guide_sum_stores", {#draft.storeBuildingIds})} end
|
||||||
|
return {T(table.concat(namesFor(draft.storeBuildingIds, guide.context.storeBuildings, "name"), " + "))}
|
||||||
|
end,
|
||||||
|
rules = function(guide)
|
||||||
|
local draft = guide.draft
|
||||||
|
if isOut(guide) then
|
||||||
|
local parts = {L("spu_guide_flow_sumSellAt", {draft.sellAtPercent}), L("spu_guide_flow_sumStoreAbove", {draft.storeAbovePercent})}
|
||||||
|
if draft.collectRound then table.insert(parts, L("spu_guide_flow_sumRound")) end
|
||||||
|
return parts
|
||||||
|
end
|
||||||
|
local loops = (draft.loops or 0) > 0 and L("spu_guide_flow_sumLoops", {draft.loops}) or L("spu_guide_flow_sumLoopsInf")
|
||||||
|
return {loops, L("spu_guide_flow_sumReturn", {draft.returnBelowPercent})}
|
||||||
|
end,
|
||||||
|
wait = function(guide)
|
||||||
|
local group = guide.draft.waitPoolGroup or ""
|
||||||
|
return {group == "" and L("spu_waitPool_all") or T(group)}
|
||||||
|
end,
|
||||||
|
tractors = function(guide)
|
||||||
|
local ids = guide.draft.vehicleIds
|
||||||
|
if #ids == 0 then return {L("spu_guide_flow_sumNoTractors")} end
|
||||||
|
if #ids > ADFlowGuide.MAX_NAMED_TRACTORS then return {L("spu_guide_flow_sumTractors", {#ids})} end
|
||||||
|
return {T(table.concat(namesFor(ids, guide.context.vehicles, "label"), ", "))}
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Resumé-linjer {step, title, parts}: Navn (altid) + de synlige trin.
|
||||||
|
function ADFlowGuide.summary(guide)
|
||||||
|
local lines = {}
|
||||||
|
for _, step in ipairs(ADFlowGuide.ORDER) do
|
||||||
|
local build = SUMMARY[step]
|
||||||
|
if build ~= nil and (step == "name" or ADFlowGuide.isVisible(step, guide)) then
|
||||||
|
table.insert(lines, {step = step, title = L(ADFlowGuide.stepKey(step, guide)), parts = build(guide)})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return lines
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ---- gem ----
|
||||||
|
|
||||||
|
function ADFlowGuide.result(guide)
|
||||||
|
local draft = guide.draft
|
||||||
|
local storeIds = ADGuide.deepCopy(draft.storeBuildingIds)
|
||||||
|
if ADFlowGuide.isVisible("store", guide) and draft.storeAuto then storeIds = {} end
|
||||||
|
local changes = {
|
||||||
|
kind = draft.kind, sourceBuildingIds = ADGuide.deepCopy(draft.sourceBuildingIds),
|
||||||
|
targetBuildingIds = ADGuide.deepCopy(draft.targetBuildingIds), storeBuildingIds = storeIds,
|
||||||
|
products = ADGuide.deepCopy(draft.products), ingredientMode = draft.ingredientMode,
|
||||||
|
ingredients = ADGuide.deepCopy(draft.ingredients), sellAtPercent = draft.sellAtPercent,
|
||||||
|
storeAbovePercent = draft.storeAbovePercent, collectRound = draft.collectRound, loops = draft.loops,
|
||||||
|
returnBelowPercent = draft.returnBelowPercent, waitPoolGroup = draft.waitPoolGroup or "",
|
||||||
|
}
|
||||||
|
if draft.name ~= nil and draft.name:match("%S") then changes.name = draft.name end
|
||||||
|
return {isNew = draft.isNew == true, runId = draft.id, name = draft.name, kind = draft.kind, changes = changes,
|
||||||
|
vehicleIds = ADGuide.deepCopy(draft.vehicleIds)}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Traktorer der skal tildeles/fjernes efter gem.
|
||||||
|
function ADFlowGuide.vehicleChanges(oldIds, newIds)
|
||||||
|
local old, new = chosenSet(oldIds), chosenSet(newIds)
|
||||||
|
local added, removed = {}, {}
|
||||||
|
for _, id in ipairs(newIds or {}) do if not old[id] then table.insert(added, id) end end
|
||||||
|
for _, id in ipairs(oldIds or {}) do if not new[id] then table.insert(removed, id) end end
|
||||||
|
return added, removed
|
||||||
|
end
|
||||||
@@ -140,9 +140,15 @@ function ADGuide.withContext(guide, context)
|
|||||||
return clone(guide, {context = context})
|
return clone(guide, {context = context})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- label = {key = "...", args = {...}} (oversættes) eller {text = "..."} (færdig tekst).
|
-- label = {key = "...", args = {...}} (oversættes) eller {text = "..."} (færdig tekst); valgfri prefix
|
||||||
|
-- (færdig tekst foran, fx "Mælk: " foran den oversatte politik).
|
||||||
function ADGuide.labelText(label, getText)
|
function ADGuide.labelText(label, getText)
|
||||||
if type(label) ~= "table" then return "" end
|
if type(label) ~= "table" then return "" end
|
||||||
|
if label.prefix ~= nil then
|
||||||
|
local rest = {}
|
||||||
|
for key, value in pairs(label) do if key ~= "prefix" then rest[key] = value end end
|
||||||
|
return tostring(label.prefix) .. ADGuide.labelText(rest, getText)
|
||||||
|
end
|
||||||
if label.text ~= nil then return tostring(label.text) end
|
if label.text ~= nil then return tostring(label.text) end
|
||||||
local template = getText(label.key)
|
local template = getText(label.key)
|
||||||
if label.args == nil or #label.args == 0 then return tostring(template) end
|
if label.args == nil or #label.args == 0 then return tostring(template) end
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ if ADSmartPickup.dir ~= nil and source ~= nil then
|
|||||||
source(ADSmartPickup.dir .. "gui/SmartPickupFieldTab.lua")
|
source(ADSmartPickup.dir .. "gui/SmartPickupFieldTab.lua")
|
||||||
source(ADSmartPickup.dir .. "gui/SmartPickupGuide.lua")
|
source(ADSmartPickup.dir .. "gui/SmartPickupGuide.lua")
|
||||||
source(ADSmartPickup.dir .. "gui/SmartPickupFieldGuide.lua")
|
source(ADSmartPickup.dir .. "gui/SmartPickupFieldGuide.lua")
|
||||||
|
source(ADSmartPickup.dir .. "gui/SmartPickupFlowGuide.lua")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Markøren er dér hvor trækkerens næse stopper; læssetriggeren ligger et
|
-- Markøren er dér hvor trækkerens næse stopper; læssetriggeren ligger et
|
||||||
|
|||||||
@@ -0,0 +1,303 @@
|
|||||||
|
-- Smart Pickup — fanen Flows: tavle + resumé, og guiden "Opsæt flow"
|
||||||
|
-- Tavlen øverst (én række pr. flow), det valgte flows resumé (klik på en linje = ret ét trin; Navn = omdøb)
|
||||||
|
-- og status. [Opsæt flow] / [Nyt flow] åbner guiden på den fælles trin-skærm (gui/SmartPickupGuide.lua).
|
||||||
|
-- Henter det spil-afhængige som `context` til ADFlowGuide og gemmer via ADRunsController.
|
||||||
|
-- Funktionerne lægges på SmartPickupFrame (indlæses efter SmartPickupFrame.lua).
|
||||||
|
|
||||||
|
function SmartPickupFrame:initFlowButtons(i18n)
|
||||||
|
self.flowSetupButtonInfo = {inputAction = InputAction.MENU_EXTRA_1, text = i18n:getText("spu_guide_flow_open"), callback = function() self:onClickFlowSetup() end}
|
||||||
|
self.flowNewButtonInfo = {inputAction = InputAction.MENU_EXTRA_2, text = i18n:getText("spu_button_new"), callback = function() self:onClickFlowNew() end}
|
||||||
|
-- Flows-fanen: [Start/Stop] [Opsæt flow] [Nyt flow] [Slet]; omdøb ligger på resuméets Navn-linje
|
||||||
|
self.flowButtons = {self.backButtonInfo, self.startButtonInfo, self.flowSetupButtonInfo, self.flowNewButtonInfo, self.deleteButtonInfo}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ---- traktor-tekst (også til guidens traktorliste) ----
|
||||||
|
|
||||||
|
-- "Fastrac 2135 4WS (#1) — 1.000.000 l — kan bære: Halm, TMR — OK" / "… — X ingen vogn".
|
||||||
|
function SmartPickupFrame:describeVehicle(adEnv, run, vehicle, isInRun)
|
||||||
|
local label = ADRunsController.getVehicleLabel(adEnv, vehicle)
|
||||||
|
local validation = ADRunsController.validateVehicle(adEnv, run, vehicle)
|
||||||
|
local parts = {label}
|
||||||
|
if validation.isAutoload and validation.capacity == nil then
|
||||||
|
table.insert(parts, self.i18n:getText("spu_capacity_pallets"))
|
||||||
|
elseif validation.capacity ~= nil then
|
||||||
|
table.insert(parts, string.format(self.i18n:getText("spu_capacity"), g_i18n:formatNumber(validation.capacity, 0)))
|
||||||
|
local carriable = self:describeCarriable(adEnv, run, vehicle, validation.carriable)
|
||||||
|
if carriable ~= nil then
|
||||||
|
table.insert(parts, carriable)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if validation.ok then
|
||||||
|
table.insert(parts, "OK")
|
||||||
|
else
|
||||||
|
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
|
||||||
|
|
||||||
|
-- Hvilke af run'ets varer vognen kan bære (titler); nil når ingen.
|
||||||
|
function SmartPickupFrame:describeCarriable(adEnv, run, vehicle, carriable)
|
||||||
|
local canCarry = {}
|
||||||
|
for _, fillType in ipairs(carriable or {}) do
|
||||||
|
canCarry[fillType] = true
|
||||||
|
end
|
||||||
|
local titles = {}
|
||||||
|
for _, fillType in ipairs(ADRunsController.getRunIngredients(adEnv, run, vehicle)) do
|
||||||
|
if canCarry[fillType] then
|
||||||
|
local title = g_fillTypeManager.getFillTypeTitleByIndex ~= nil and g_fillTypeManager:getFillTypeTitleByIndex(fillType) or nil
|
||||||
|
table.insert(titles, tostring(title or g_fillTypeManager:getFillTypeNameByIndex(fillType)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #titles == 0 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return string.format(self.i18n:getText("spu_canCarry"), table.concat(titles, ", "))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ---- context til ADFlowGuide ----
|
||||||
|
|
||||||
|
local function trim(text)
|
||||||
|
return (string.gsub(tostring(text or ""), "^%s+", ""))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function idSet(list)
|
||||||
|
local set = {}
|
||||||
|
for _, id in ipairs(list or {}) do set[id] = true end
|
||||||
|
return set
|
||||||
|
end
|
||||||
|
|
||||||
|
function SmartPickupFrame:flowBuildingContext(adEnv, draft, farmId)
|
||||||
|
local isOut = draft.kind == "outbound"
|
||||||
|
local role = isOut and "load" or "unload"
|
||||||
|
local chosen = idSet(isOut and draft.sourceBuildingIds or draft.targetBuildingIds)
|
||||||
|
local buildings, found = {}, {}
|
||||||
|
for _, building in ipairs(ADBuildings.list(adEnv, farmId)) do
|
||||||
|
local fits = (isOut and building.canBeSource) or (not isOut and building.canBeTarget)
|
||||||
|
if fits or chosen[building.id] then
|
||||||
|
local markers = ADBuildings.getMarkers ~= nil and ADBuildings.getMarkers(building, role) or {}
|
||||||
|
found[building.id] = true
|
||||||
|
table.insert(buildings, {id = building.id, name = tostring(building.name), label = markers[1] ~= nil and tostring(markers[1].name) or nil,
|
||||||
|
hasMarker = markers[1] ~= nil, text = trim(self:describeBuilding(building, role))})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- valgte bygninger der ikke længere findes (solgt): kan fravælges
|
||||||
|
for id in pairs(chosen) do
|
||||||
|
if not found[id] then
|
||||||
|
local gone = self.i18n:getText("spu_buildingGone")
|
||||||
|
table.insert(buildings, {id = id, name = gone, hasMarker = false, text = gone})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return buildings
|
||||||
|
end
|
||||||
|
|
||||||
|
function SmartPickupFrame:flowStoreContext(adEnv, preview, farmId)
|
||||||
|
local goods = ADRunsController.getStoreGoods(adEnv, preview)
|
||||||
|
local chosen = idSet(preview.storeBuildingIds)
|
||||||
|
local result = {}
|
||||||
|
for _, building in ipairs(ADBuildings.list(adEnv, farmId)) do
|
||||||
|
local isStoreKind = building.kind == "silo" or building.kind == "factory" or building.kind == "objectStorage"
|
||||||
|
if isStoreKind and (chosen[building.id] or ADRunsController.canStoreAt(adEnv, building, goods)) then
|
||||||
|
table.insert(result, {id = building.id, name = tostring(building.name), text = trim(self:describeBuilding(building, "unload"))})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function SmartPickupFrame:flowVehicleContext(adEnv, preview)
|
||||||
|
local result = {}
|
||||||
|
for _, vehicle in ipairs(ADRunsController.getFarmVehicles(adEnv)) do
|
||||||
|
local ok, entry = pcall(function()
|
||||||
|
local text = self:describeVehicle(adEnv, preview, vehicle, false)
|
||||||
|
local validation = ADRunsController.validateVehicle(adEnv, preview, vehicle)
|
||||||
|
local other = ADRunsController.getRunForVehicle(vehicle)
|
||||||
|
return {id = ADRunsController.getVehicleId(vehicle), text = text, label = ADRunsController.getVehicleLabel(adEnv, vehicle), otherRunId = other ~= nil and other.id or nil,
|
||||||
|
otherRunName = other ~= nil and other.name or nil, blocked = SmartPickupFrame.VEHICLE_BLOCKING_REASONS[validation.reason] == true}
|
||||||
|
end)
|
||||||
|
if ok and entry.id ~= nil then table.insert(result, entry) end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- draft: kladden (eller selve run'et — samme feltnavne).
|
||||||
|
function SmartPickupFrame:flowGuideContext(adEnv, draft)
|
||||||
|
local farmId = g_currentMission:getFarmId()
|
||||||
|
local preview = ADFlowGuide.previewRun(draft, function(buildingId, role)
|
||||||
|
local building = ADBuildings.find(adEnv, farmId, buildingId)
|
||||||
|
local ids = {}
|
||||||
|
for _, marker in ipairs(building ~= nil and ADBuildings.getMarkers ~= nil and ADBuildings.getMarkers(building, role) or {}) do
|
||||||
|
table.insert(ids, marker.id)
|
||||||
|
end
|
||||||
|
return ids
|
||||||
|
end)
|
||||||
|
local context = {buildings = self:flowBuildingContext(adEnv, draft, farmId), fillTypes = {}, products = {}, storeBuildings = {},
|
||||||
|
waitPools = ADRunsController.getWaitPools(adEnv), vehicles = self:flowVehicleContext(adEnv, preview)}
|
||||||
|
if draft.kind == "outbound" then
|
||||||
|
for _, product in ipairs(ADRunsController.getRunProducts(adEnv, preview)) do
|
||||||
|
local title = tostring(product.title)
|
||||||
|
table.insert(context.products, {name = product.name, title = title,
|
||||||
|
text = string.format("%s — %s l", title, SmartPickupFrame.formatCompact(product.available or 0))})
|
||||||
|
end
|
||||||
|
context.storeBuildings = self:flowStoreContext(adEnv, preview, farmId)
|
||||||
|
else
|
||||||
|
for _, fillType in ipairs(ADRunsController.getTargetAcceptedFillTypes(adEnv, preview)) do
|
||||||
|
table.insert(context.fillTypes, {name = fillType.name, title = tostring(fillType.title)})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return context
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ---- fanen: tavle + resumé ----
|
||||||
|
|
||||||
|
function SmartPickupFrame:fillFlowTabRows(adEnv)
|
||||||
|
if self:hasGuideOn(SmartPickupFrame.TAB_FLOWS) and self:fillGuideRows(adEnv) then return end
|
||||||
|
self:fillOverviewRows(adEnv)
|
||||||
|
local run = self:getRun()
|
||||||
|
if run ~= nil then self:fillFlowSummary(adEnv, run) end
|
||||||
|
end
|
||||||
|
|
||||||
|
function SmartPickupFrame:fillFlowSummary(adEnv, run)
|
||||||
|
local i18n = self.i18n
|
||||||
|
self:addTextRow(string.format(i18n:getText("spu_guide_flow_section"), tostring(run.name)))
|
||||||
|
self:addTextRow(" " .. i18n:getText("spu_guide_sum_hint"))
|
||||||
|
local guide = ADFlowGuide.open(run, self:flowGuideContext(adEnv, run))
|
||||||
|
local getText = function(key) return i18n:getText(key) end
|
||||||
|
local runId = run.id
|
||||||
|
for _, line in ipairs(ADFlowGuide.summary(guide)) do
|
||||||
|
local step = line.step
|
||||||
|
self:addLinkRow(" " .. ADGuide.lineText(line, getText) .. i18n:getText("spu_guide_editSuffix"), function(frame)
|
||||||
|
if step == "name" then frame:onClickRename() else frame:openFlowStep(runId, step) end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
local cells = self:flowCells(adEnv, run)
|
||||||
|
self:addTextRow(" " .. string.format(i18n:getText("spu_field_statusFull"), tostring(cells[6])))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Hvert sekund: tavlens celler, statuslinje og knapper.
|
||||||
|
function SmartPickupFrame:refreshFlowTab(adEnv)
|
||||||
|
local run = self:getRun()
|
||||||
|
local isRunning = false
|
||||||
|
if self:hasGuideOn(SmartPickupFrame.TAB_FLOWS) then
|
||||||
|
self:setStatusText(self.statusOverride or "")
|
||||||
|
else
|
||||||
|
self:refreshOverview(adEnv)
|
||||||
|
if run ~= nil then
|
||||||
|
local status = ADRunsController.getRunStatus(adEnv, run)
|
||||||
|
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)
|
||||||
|
else
|
||||||
|
-- tavlen siger allerede "Ingen flows"
|
||||||
|
self:setStatusText(self.statusOverride or "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.startButtonInfo.text = self.i18n:getText(isRunning and "spu_button_stop" or "spu_button_start")
|
||||||
|
self.startButtonInfo.disabled = run == nil
|
||||||
|
self.flowSetupButtonInfo.disabled = run == nil
|
||||||
|
self.deleteButtonInfo.disabled = run == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ---- hooks til den fælles guide-skærm ----
|
||||||
|
|
||||||
|
SmartPickupFrame.GUIDE_HOOKS = SmartPickupFrame.GUIDE_HOOKS or {}
|
||||||
|
SmartPickupFrame.GUIDE_HOOKS.flow = {
|
||||||
|
model = ADFlowGuide,
|
||||||
|
tab = SmartPickupFrame.TAB_FLOWS,
|
||||||
|
heading = function(frame, guide)
|
||||||
|
return string.format(frame.i18n:getText("spu_guide_flow_title"), tostring(guide.draft.name))
|
||||||
|
end,
|
||||||
|
context = function(frame, adEnv, guide)
|
||||||
|
-- flowet er slettet mens guiden var åben
|
||||||
|
if not guide.draft.isNew and ADRuns.getRun(ADRunsController.store, guide.draft.id) == nil then return nil end
|
||||||
|
return frame:flowGuideContext(adEnv, guide.draft)
|
||||||
|
end,
|
||||||
|
choose = function(frame, id)
|
||||||
|
if id ~= "rename" then return false end
|
||||||
|
TextInputDialog.show(SmartPickupFrame.onGuideNameResult, frame, frame.guide.draft.name, frame.i18n:getText("spu_renameTitle"), nil, 30, g_i18n:getText("button_ok"))
|
||||||
|
return true
|
||||||
|
end,
|
||||||
|
startKey = function() return "spu_guide_flow_start" end,
|
||||||
|
save = function(frame, guide, start) frame:saveFlowGuide(guide, start) end,
|
||||||
|
}
|
||||||
|
|
||||||
|
function SmartPickupFrame:onGuideNameResult(value, clickOk)
|
||||||
|
self:safe(function(frame)
|
||||||
|
if not clickOk or frame.guide == nil or frame.guideKind ~= "flow" then return end
|
||||||
|
frame.guide = ADFlowGuide.choose(frame.guide, "name", value)
|
||||||
|
frame:requestRebuild()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Gem: nyt flow oprettes først nu; felterne via updateRun; traktorer tildeles/fjernes; markører findes.
|
||||||
|
-- start = true: flowet startes bagefter ([Start flow]).
|
||||||
|
function SmartPickupFrame:saveFlowGuide(guide, start)
|
||||||
|
local adEnv = self.getAdEnv()
|
||||||
|
local result = ADFlowGuide.result(guide)
|
||||||
|
local runId = result.runId
|
||||||
|
local oldIds = {}
|
||||||
|
if result.isNew then
|
||||||
|
runId = ADRunsController.createRun(result.name, result.kind).id
|
||||||
|
else
|
||||||
|
local existing = ADRuns.getRun(ADRunsController.store, runId)
|
||||||
|
if existing == nil then return end
|
||||||
|
oldIds = existing.vehicleIds
|
||||||
|
end
|
||||||
|
ADRunsController.updateRun(runId, result.changes)
|
||||||
|
local added, removed = ADFlowGuide.vehicleChanges(oldIds, result.vehicleIds)
|
||||||
|
for _, id in ipairs(removed) do
|
||||||
|
local vehicle = adEnv ~= nil and ADRunsController.findVehicleById(adEnv, id) or nil
|
||||||
|
if vehicle ~= nil then
|
||||||
|
ADRunsController.unassignVehicle(vehicle)
|
||||||
|
else
|
||||||
|
ADRunsController.store = ADRuns.unassignVehicle(ADRunsController.store, id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _, id in ipairs(added) do
|
||||||
|
local vehicle = adEnv ~= nil and ADRunsController.findVehicleById(adEnv, id) or nil
|
||||||
|
if vehicle ~= nil then ADRunsController.assignVehicle(runId, vehicle) end
|
||||||
|
end
|
||||||
|
ADRunsController.resolveMarkers(adEnv, ADRuns.getRun(ADRunsController.store, runId))
|
||||||
|
self.selectedRunId = runId
|
||||||
|
local saved = string.format(self.i18n:getText("spu_guide_flow_saved"), tostring(result.name))
|
||||||
|
if start and adEnv ~= nil and not ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, runId)).isRunning then
|
||||||
|
local started, skipped, reasons = self:summarizeStartResults(ADRunsController.startRun(adEnv, runId))
|
||||||
|
self:showOverride(saved .. " — " .. string.format(self.i18n:getText("spu_status_started"), started, skipped), reasons)
|
||||||
|
else
|
||||||
|
self:showOverride(saved)
|
||||||
|
end
|
||||||
|
ADRunsController.save()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ---- knapper og [Ret] ----
|
||||||
|
|
||||||
|
function SmartPickupFrame:onClickFlowSetup()
|
||||||
|
self:safe(function(frame)
|
||||||
|
local adEnv, run = frame.getAdEnv(), frame:getRun()
|
||||||
|
if adEnv == nil or run == nil then return end
|
||||||
|
frame:openGuide("flow", ADFlowGuide.open(run, frame:flowGuideContext(adEnv, run)))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- [Nyt flow] (også fra fanen Oversigt): guiden på Flows-fanen; intet oprettes før [Gem].
|
||||||
|
function SmartPickupFrame:onClickFlowNew()
|
||||||
|
self:safe(function(frame)
|
||||||
|
local adEnv = frame.getAdEnv()
|
||||||
|
if adEnv == nil then return end
|
||||||
|
local name = string.format(frame.i18n:getText("spu_newRunName"), #ADRunsController.store.runs + 1)
|
||||||
|
local guide = ADFlowGuide.openNew(name, {})
|
||||||
|
guide = ADGuide.withContext(guide, frame:flowGuideContext(adEnv, guide.draft))
|
||||||
|
if frame.currentTab ~= SmartPickupFrame.TAB_FLOWS then frame:switchTab(SmartPickupFrame.TAB_FLOWS) end
|
||||||
|
frame:openGuide("flow", guide)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function SmartPickupFrame:openFlowStep(runId, step)
|
||||||
|
local adEnv = self.getAdEnv()
|
||||||
|
local run = ADRuns.getRun(ADRunsController.store, runId)
|
||||||
|
if adEnv == nil or run == nil then return end
|
||||||
|
self:openGuide("flow", ADFlowGuide.open(run, self:flowGuideContext(adEnv, run), {step = step, single = true, returnTo = "close"}))
|
||||||
|
end
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
-- Smart Pickup — fanen Flows
|
-- Smart Pickup — fanen Flows
|
||||||
-- Tavle øverst: én tekstrække pr. flow (Vare · Fra → Til · Regel · Traktorer · Status), det valgte
|
-- Tavlen (én række pr. flow, det valgte markeret med ">") bruges af fanerne Oversigt og Flows; Flows-fanens
|
||||||
-- markeret med ">". Under tavlen: detail-blok for det valgte flow — Retning, Fra/Til som BYGNINGER
|
-- resumé og guiden "Opsæt flow" ligger i gui/SmartPickupFlowGuide.lua. Her også bygnings-teksten og de
|
||||||
-- (markøren findes automatisk), Varer, Regler, Vent ved, Traktorer, Status — og Wait-puljerne.
|
-- gamle klik-callbacks for retning/bygninger.
|
||||||
-- Funktionerne lægges på SmartPickupFrame (indlæses efter SmartPickupFrame.lua).
|
-- Funktionerne lægges på SmartPickupFrame (indlæses efter SmartPickupFrame.lua).
|
||||||
|
|
||||||
-- ---- tavlen ----
|
-- ---- tavlen ----
|
||||||
@@ -137,86 +137,6 @@ function SmartPickupFrame:describeBuilding(building, role)
|
|||||||
return string.format(" %s — %s", building.name, table.concat(parts, " · "))
|
return string.format(" %s — %s", building.name, table.concat(parts, " · "))
|
||||||
end
|
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 role == "store" and run.storeBuildingIds or run.targetBuildingIds
|
|
||||||
local rowKind = role == "load" and "sourceBuilding" or role == "store" and "storeBuilding" or "targetBuilding"
|
|
||||||
for _, id in ipairs(ids or {}) do chosen[id] = true end
|
|
||||||
local count = 0
|
|
||||||
-- kandidater i fast rækkefølge (navn, markør); uden markør kun med "Vis alle bygninger"
|
|
||||||
local candidates = {}
|
|
||||||
-- "Lager til": kun bygninger der kan tage flowets lagervarer (æg-paller -> palleskure der tager æg)
|
|
||||||
local storeGoods = role == "store" and ADRunsController.getStoreGoods(adEnv, run) or nil
|
|
||||||
for _, building in ipairs(ADBuildings.list(adEnv, g_currentMission:getFarmId())) do
|
|
||||||
local isStoreKind = building.kind == "silo" or building.kind == "factory" or building.kind == "objectStorage"
|
|
||||||
local fits = (role == "load" and building.canBeSource) or (role == "unload" and building.canBeTarget)
|
|
||||||
or (role == "store" and isStoreKind and (chosen[building.id] or ADRunsController.canStoreAt(adEnv, building, storeGoods)))
|
|
||||||
if fits then
|
|
||||||
local markers = ADBuildings.getMarkers ~= nil and ADBuildings.getMarkers(building, role == "store" and "unload" or role) or {}
|
|
||||||
table.insert(candidates, {id = building.id, name = building.name, label = markers[1] ~= nil and tostring(markers[1].name) or nil, hasMarker = markers[1] ~= nil, building = building})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local shown = ADFlowView.buildingRows(candidates, chosen, self.showAllBuildings == true)
|
|
||||||
local hiddenBuildings = ADFlowView.hiddenCount(candidates, shown)
|
|
||||||
if hiddenBuildings > 0 or self.showAllBuildings then
|
|
||||||
self:addToggleRow("showAllBuildings", string.format(self.i18n:getText("spu_showAllBuildings"), hiddenBuildings), {self.i18n:getText("ui_no"), self.i18n:getText("ui_yes")}, self.showAllBuildings == true)
|
|
||||||
end
|
|
||||||
for _, entry in ipairs(shown) do
|
|
||||||
local building = entry.building
|
|
||||||
count = count + 1
|
|
||||||
local row = self:addRow(self.ingredientPrefab, {kind = rowKind, buildingId = building.id})
|
|
||||||
row:getDescendantByName("title"):setText(self:describeBuilding(building, role == "store" and "unload" or role))
|
|
||||||
row:getDescendantByName("option"):setState(chosen[building.id] and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false)
|
|
||||||
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 = rowKind, buildingId = id})
|
|
||||||
row:getDescendantByName("title"):setText(" " .. 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:addTextRow(self.i18n:getText("spu_section_store"))
|
|
||||||
self:fillBuildingRows(adEnv, run, "store")
|
|
||||||
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 ----
|
-- ---- callbacks ----
|
||||||
|
|
||||||
function SmartPickupFrame:onClickDirection(state)
|
function SmartPickupFrame:onClickDirection(state)
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ function SmartPickupFrame.new(i18n, getAdEnv)
|
|||||||
-- Marker: [Start nu/Stop], [Opsæt mark], [Markflåde] (gui/SmartPickupFieldGuide.lua); guidens knapper
|
-- Marker: [Start nu/Stop], [Opsæt mark], [Markflåde] (gui/SmartPickupFieldGuide.lua); guidens knapper
|
||||||
self:initFieldButtons(i18n)
|
self:initFieldButtons(i18n)
|
||||||
self:initGuideButtons(i18n)
|
self:initGuideButtons(i18n)
|
||||||
|
-- Flows: [Start/Stop] [Opsæt flow] [Nyt flow] [Slet] (gui/SmartPickupFlowGuide.lua)
|
||||||
|
self:initFlowButtons(i18n)
|
||||||
-- Egen reference til listen: fallback'en i refreshButtons skal kunne sende den igen.
|
-- Egen reference til listen: fallback'en i refreshButtons skal kunne sende den igen.
|
||||||
self.menuButtonInfo = self.overviewButtons
|
self.menuButtonInfo = self.overviewButtons
|
||||||
self:setMenuButtonInfo(self.menuButtonInfo)
|
self:setMenuButtonInfo(self.menuButtonInfo)
|
||||||
@@ -267,7 +269,8 @@ end
|
|||||||
-- De faste rækker: Flow-vælger + Wait-pulje for alle flows; Ture/kør hjem for forsyning; Sælg fra/
|
-- 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.
|
-- Kør til lager for udkørsel. Fanen Lagre viser ingen.
|
||||||
function SmartPickupFrame:setStaticRowsVisible(tab, run)
|
function SmartPickupFrame:setStaticRowsVisible(tab, run)
|
||||||
local isFlows = tab == SmartPickupFrame.TAB_FLOWS
|
-- Flows-fanen bruger tavle + resumé + guide (v1.32); de faste XML-rækker vises ikke længere
|
||||||
|
local isFlows = false
|
||||||
local hasRun = isFlows and run ~= nil
|
local hasRun = isFlows and run ~= nil
|
||||||
local isSupply, isOutbound = hasRun and run.kind ~= "outbound", hasRun and run.kind == "outbound"
|
local isSupply, isOutbound = hasRun and run.kind ~= "outbound", hasRun and run.kind == "outbound"
|
||||||
local visibility = {
|
local visibility = {
|
||||||
@@ -419,19 +422,8 @@ function SmartPickupFrame:rebuild()
|
|||||||
self:restoreFocusIfLost()
|
self:restoreFocusIfLost()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
self:fillRunOption(adEnv)
|
if adEnv ~= nil then
|
||||||
local hasRun = run ~= nil and adEnv ~= nil
|
self:fillFlowTabRows(adEnv)
|
||||||
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 hasRun then
|
|
||||||
self:fillWaitPoolOption(adEnv, run)
|
|
||||||
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
|
end
|
||||||
self.statusRow = self:addTextRow("")
|
self.statusRow = self:addTextRow("")
|
||||||
self:colorRows()
|
self:colorRows()
|
||||||
@@ -440,25 +432,6 @@ function SmartPickupFrame:rebuild()
|
|||||||
self:restoreFocusIfLost()
|
self:restoreFocusIfLost()
|
||||||
end
|
end
|
||||||
|
|
||||||
function SmartPickupFrame:fillRunOption(adEnv)
|
|
||||||
local texts, index = {}, 1
|
|
||||||
self.runIds = {}
|
|
||||||
for position, run in ipairs(self:getRunsForTab()) do
|
|
||||||
local status = adEnv ~= nil and ADRunsController.getRunStatus(adEnv, run) or {running = 0, total = #run.vehicleIds}
|
|
||||||
table.insert(texts, string.format("%s (%d/%d)", run.name, status.running, status.total))
|
|
||||||
table.insert(self.runIds, run.id)
|
|
||||||
if run.id == self.selectedRunId then
|
|
||||||
index = position
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if #texts == 0 then
|
|
||||||
texts = {self.i18n:getText("spu_noRuns")}
|
|
||||||
end
|
|
||||||
self.runOption:setTexts(texts)
|
|
||||||
self.runOption:setState(index, false)
|
|
||||||
self.runOption:setDisabled(#self.runIds == 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Overblik: "Cow 1 food: TMR 18.0k/27.8k · Silage 3.0k/…" (mest trængende først)
|
-- Overblik: "Cow 1 food: TMR 18.0k/27.8k · Silage 3.0k/…" (mest trængende først)
|
||||||
function SmartPickupFrame:describeTargetLevels(target)
|
function SmartPickupFrame:describeTargetLevels(target)
|
||||||
if #target.levels == 0 then
|
if #target.levels == 0 then
|
||||||
@@ -467,114 +440,6 @@ function SmartPickupFrame:describeTargetLevels(target)
|
|||||||
return string.format("%s: %s", target.name, self:formatLevels(target.levels))
|
return string.format("%s: %s", target.name, self:formatLevels(target.levels))
|
||||||
end
|
end
|
||||||
|
|
||||||
function SmartPickupFrame:fillWaitPoolOption(adEnv, run)
|
|
||||||
local texts, index = {self.i18n:getText("spu_waitPool_all")}, 1
|
|
||||||
self.poolGroups = {""}
|
|
||||||
for _, pool in ipairs(ADRunsController.getWaitPools(adEnv)) do
|
|
||||||
table.insert(texts, string.format("%s (%d)", pool.group, pool.count))
|
|
||||||
table.insert(self.poolGroups, pool.group)
|
|
||||||
if pool.group == run.waitPoolGroup then
|
|
||||||
index = #texts
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.waitPoolOption:setTexts(texts)
|
|
||||||
self.waitPoolOption:setState(index, false)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Traktorer: som standard flowets egne og de ledige der kan bruges; "Vis alle" tilføjer traktorer i andre
|
|
||||||
-- flows og låste (vognen passer ikke) — de er tit mange og gør listen uoverskuelig.
|
|
||||||
function SmartPickupFrame:fillVehicleRows(adEnv, run)
|
|
||||||
local inRun = {}
|
|
||||||
for _, id in ipairs(run.vehicleIds) do
|
|
||||||
inRun[id] = true
|
|
||||||
end
|
|
||||||
local entries = {}
|
|
||||||
for _, vehicle in ipairs(ADRunsController.getFarmVehicles(adEnv)) do
|
|
||||||
local id = ADRunsController.getVehicleId(vehicle)
|
|
||||||
local otherRun = ADRunsController.getRunForVehicle(vehicle)
|
|
||||||
local state = "free"
|
|
||||||
if inRun[id] then
|
|
||||||
state = "in"
|
|
||||||
elseif otherRun ~= nil and otherRun.id ~= run.id then
|
|
||||||
state = "other"
|
|
||||||
elseif SmartPickupFrame.VEHICLE_BLOCKING_REASONS[ADRunsController.validateVehicle(adEnv, run, vehicle).reason] then
|
|
||||||
state = "blocked"
|
|
||||||
end
|
|
||||||
table.insert(entries, {id = id, label = ADRunsController.getVehicleLabel(adEnv, vehicle), state = state, vehicle = vehicle, otherRun = otherRun})
|
|
||||||
end
|
|
||||||
local rows = ADFlowView.vehicleRows(entries, self.showAllVehicles == true)
|
|
||||||
local hidden = ADFlowView.hiddenCount(entries, rows)
|
|
||||||
if hidden > 0 or self.showAllVehicles then
|
|
||||||
self.showAllVehiclesOption = self:addToggleRow("showAllVehicles", string.format(self.i18n:getText("spu_showAllVehicles"), hidden), {self.i18n:getText("ui_no"), self.i18n:getText("ui_yes")}, self.showAllVehicles == true)
|
|
||||||
end
|
|
||||||
for _, entry in ipairs(rows) do
|
|
||||||
local vehicle = entry.vehicle
|
|
||||||
local row = self:addRow(self.vehiclePrefab, {kind = "vehicle", vehicle = vehicle, row = nil})
|
|
||||||
local option, title = row:getDescendantByName("option"), row:getDescendantByName("title")
|
|
||||||
self.rowData[option].row = row
|
|
||||||
option:setState(entry.state == "in" and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false)
|
|
||||||
if entry.state == "other" then
|
|
||||||
-- én traktor = ét flow; rækken viser hvor den er
|
|
||||||
option:setDisabled(true)
|
|
||||||
self.rowData[option].locked = true
|
|
||||||
title:setText(string.format("%s — %s", entry.label, string.format(self.i18n:getText("spu_inRun"), tostring(entry.otherRun.name))))
|
|
||||||
else
|
|
||||||
title:setText(self:describeVehicle(adEnv, run, vehicle, entry.state == "in"))
|
|
||||||
if entry.state == "blocked" then
|
|
||||||
option:setDisabled(true)
|
|
||||||
self.rowData[option].locked = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- "Fastrac 2135 4WS (#1) — 1.000.000 l — ✔" / "… — ✖ ingen vogn"; reservationsnote fra Start bevares.
|
|
||||||
-- "Fastrac 2135 4WS (#1) — 1.000.000 l — kan bære: Halm, TMR — ✔" / "… — ✖ ingen vogn".
|
|
||||||
-- Vises for ALLE traktorer, så man kan se om vognen passer, før man siger Ja.
|
|
||||||
function SmartPickupFrame:describeVehicle(adEnv, run, vehicle, isInRun)
|
|
||||||
local label = ADRunsController.getVehicleLabel(adEnv, vehicle)
|
|
||||||
local validation = ADRunsController.validateVehicle(adEnv, run, vehicle)
|
|
||||||
local parts = {label}
|
|
||||||
if validation.isAutoload and validation.capacity == nil then
|
|
||||||
table.insert(parts, self.i18n:getText("spu_capacity_pallets"))
|
|
||||||
elseif validation.capacity ~= nil then
|
|
||||||
table.insert(parts, string.format(self.i18n:getText("spu_capacity"), g_i18n:formatNumber(validation.capacity, 0)))
|
|
||||||
local carriable = self:describeCarriable(adEnv, run, vehicle, validation.carriable)
|
|
||||||
if carriable ~= nil then
|
|
||||||
table.insert(parts, carriable)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if validation.ok then
|
|
||||||
table.insert(parts, "OK")
|
|
||||||
else
|
|
||||||
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
|
|
||||||
|
|
||||||
-- Hvilke af run'ets varer vognen kan bære (titler); nil når ingen.
|
|
||||||
function SmartPickupFrame:describeCarriable(adEnv, run, vehicle, carriable)
|
|
||||||
local canCarry = {}
|
|
||||||
for _, fillType in ipairs(carriable or {}) do
|
|
||||||
canCarry[fillType] = true
|
|
||||||
end
|
|
||||||
local titles = {}
|
|
||||||
for _, fillType in ipairs(ADRunsController.getRunIngredients(adEnv, run, vehicle)) do
|
|
||||||
if canCarry[fillType] then
|
|
||||||
local title = g_fillTypeManager.getFillTypeTitleByIndex ~= nil and g_fillTypeManager:getFillTypeTitleByIndex(fillType) or nil
|
|
||||||
table.insert(titles, tostring(title or g_fillTypeManager:getFillTypeNameByIndex(fillType)))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if #titles == 0 then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
return string.format(self.i18n:getText("spu_canCarry"), table.concat(titles, ", "))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Felterne følger modellen: når et klik ikke er nået ind i run'et, springer feltet tilbage,
|
-- Felterne følger modellen: når et klik ikke er nået ind i run'et, springer feltet tilbage,
|
||||||
-- og loggen ("run '…': waitPoolGroup=…") viser om klikket blev registreret.
|
-- og loggen ("run '…': waitPoolGroup=…") viser om klikket blev registreret.
|
||||||
function SmartPickupFrame:syncOptionsFromModel(adEnv, run)
|
function SmartPickupFrame:syncOptionsFromModel(adEnv, run)
|
||||||
@@ -706,6 +571,13 @@ function SmartPickupFrame:refreshLive()
|
|||||||
self:refreshButtons()
|
self:refreshButtons()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
if self.currentTab == SmartPickupFrame.TAB_FLOWS then
|
||||||
|
self:refreshFlowTab(adEnv)
|
||||||
|
self.statusOverrideTicks = (self.statusOverrideTicks or 0) - 1
|
||||||
|
if self.statusOverrideTicks <= 0 then self.statusOverride = nil end
|
||||||
|
self:refreshButtons()
|
||||||
|
return
|
||||||
|
end
|
||||||
self:refreshStorageRows(adEnv)
|
self:refreshStorageRows(adEnv)
|
||||||
local isRunning = false
|
local isRunning = false
|
||||||
if isOverview then
|
if isOverview then
|
||||||
@@ -919,26 +791,9 @@ function SmartPickupFrame:openFlow(runId)
|
|||||||
self:switchTab(SmartPickupFrame.TAB_FLOWS)
|
self:switchTab(SmartPickupFrame.TAB_FLOWS)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Nyt run: navnet vælges FØR run'et oprettes (annulleres dialogen, oprettes intet).
|
-- Nyt flow: guiden "Opsæt flow" (gui/SmartPickupFlowGuide.lua) — intet oprettes før [Gem].
|
||||||
-- Forslaget tæller efter antal runs, ikke efter løbenummeret, så "Run 2" kan genbruges.
|
|
||||||
function SmartPickupFrame:onClickNew()
|
function SmartPickupFrame:onClickNew()
|
||||||
self:safe(function(frame)
|
self:onClickFlowNew()
|
||||||
local suggestion = string.format(frame.i18n:getText("spu_newRunName"), #ADRunsController.store.runs + 1)
|
|
||||||
TextInputDialog.show(SmartPickupFrame.onNewRunName, frame, suggestion, frame.i18n:getText("spu_renameTitle"), nil, 30, g_i18n:getText("button_ok"))
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
function SmartPickupFrame:onNewRunName(value, clickOk)
|
|
||||||
self:safe(function(frame)
|
|
||||||
if not clickOk or value == nil or not value:match("%S") then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local run = ADRunsController.createRun(value, "supply")
|
|
||||||
frame.selectedRunId = run.id
|
|
||||||
frame.statusOverride = nil
|
|
||||||
frame.currentTab = SmartPickupFrame.TAB_FLOWS
|
|
||||||
frame:switchTab(SmartPickupFrame.TAB_FLOWS)
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function SmartPickupFrame:onClickRename()
|
function SmartPickupFrame:onClickRename()
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ function SmartPickupFrame:fillGuideRows(adEnv)
|
|||||||
self.guide = ADGuide.withContext(self.guide, context)
|
self.guide = ADGuide.withContext(self.guide, context)
|
||||||
local guide, model = self.guide, hooks.model
|
local guide, model = self.guide, hooks.model
|
||||||
self:addTextRow(hooks.heading(self, guide))
|
self:addTextRow(hooks.heading(self, guide))
|
||||||
local stepTitle = self.i18n:getText(model.stepKey(guide.step))
|
local stepTitle = self.i18n:getText(model.stepKey(guide.step, guide))
|
||||||
local index, total = ADGuide.position(guide)
|
local index, total = ADGuide.position(guide)
|
||||||
local header
|
local header
|
||||||
if guide.single then
|
if guide.single then
|
||||||
@@ -114,7 +114,7 @@ function SmartPickupFrame:fillGuideRows(adEnv)
|
|||||||
header = string.format(self.i18n:getText("spu_guide_stepOne"), index or 1, stepTitle)
|
header = string.format(self.i18n:getText("spu_guide_stepOne"), index or 1, stepTitle)
|
||||||
end
|
end
|
||||||
self:addTextRow(header)
|
self:addTextRow(header)
|
||||||
self:addTextRow(" " .. self.i18n:getText(model.hintKey(guide.step)))
|
self:addTextRow(" " .. self.i18n:getText(model.hintKey(guide.step, guide)))
|
||||||
for _, row in ipairs(model.rows(guide)) do
|
for _, row in ipairs(model.rows(guide)) do
|
||||||
self:addGuideRow(row)
|
self:addGuideRow(row)
|
||||||
end
|
end
|
||||||
@@ -140,6 +140,8 @@ function SmartPickupFrame:onClickGuideRow(state, data)
|
|||||||
local option = row.options[state]
|
local option = row.options[state]
|
||||||
if option == nil then return end
|
if option == nil then return end
|
||||||
value = option.value
|
value = option.value
|
||||||
|
-- valg pr. vare (fx politik): modellen skal vide hvilken
|
||||||
|
if row.item ~= nil then value = {item = row.item, value = option.value} end
|
||||||
elseif row.type == "toggle" then
|
elseif row.type == "toggle" then
|
||||||
value = state == SmartPickupFrame.STATE_ON
|
value = state == SmartPickupFrame.STATE_ON
|
||||||
elseif row.type == "check" then
|
elseif row.type == "check" then
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<text name="spu_title" text="Smart Pickup"/>
|
<text name="spu_title" text="Smart Pickup"/>
|
||||||
<text name="spu_run" text="Run"/>
|
<text name="spu_run" text="Run"/>
|
||||||
<text name="spu_run_tooltip" text="Vælg det run du vil se eller ændre."/>
|
<text name="spu_run_tooltip" text="Vælg det run du vil se eller ændre."/>
|
||||||
<text name="spu_noRuns" text="Ingen runs — tryk 'Nyt run'"/>
|
<text name="spu_noRuns" text="Ingen flows — tryk 'Nyt flow'"/>
|
||||||
<text name="spu_target" text="Mål"/>
|
<text name="spu_target" text="Mål"/>
|
||||||
<text name="spu_target_tooltip" text="AutoDrive-markør ved stalden eller fabrikken der skal fyldes."/>
|
<text name="spu_target_tooltip" text="AutoDrive-markør ved stalden eller fabrikken der skal fyldes."/>
|
||||||
<text name="spu_noTarget" text="(vælg mål)"/>
|
<text name="spu_noTarget" text="(vælg mål)"/>
|
||||||
@@ -189,13 +189,13 @@
|
|||||||
<text name="spu_field_straw_chop" text="Hakkes (spredes på marken)"/>
|
<text name="spu_field_straw_chop" text="Hakkes (spredes på marken)"/>
|
||||||
<text name="spu_field_straw_collect" text="Samles op til silo"/>
|
<text name="spu_field_straw_collect" text="Samles op til silo"/>
|
||||||
<text name="spu_field_straw_bale" text="Presses til baller og samles"/>
|
<text name="spu_field_straw_bale" text="Presses til baller og samles"/>
|
||||||
<text name="spu_button_start" text="Start run"/>
|
<text name="spu_button_start" text="Start flow"/>
|
||||||
<text name="spu_button_stop" text="Stop run"/>
|
<text name="spu_button_stop" text="Stop flow"/>
|
||||||
<text name="spu_button_new" text="Nyt run"/>
|
<text name="spu_button_new" text="Nyt flow"/>
|
||||||
<text name="spu_button_rename" text="Omdøb"/>
|
<text name="spu_button_rename" text="Omdøb"/>
|
||||||
<text name="spu_button_delete" text="Slet run"/>
|
<text name="spu_button_delete" text="Slet flow"/>
|
||||||
<text name="spu_newRunName" text="Run %d"/>
|
<text name="spu_newRunName" text="Flow %d"/>
|
||||||
<text name="spu_renameTitle" text="Navn på run"/>
|
<text name="spu_renameTitle" text="Navn på flow"/>
|
||||||
<text name="spu_deleteConfirm" text="Slet run '%s'? Dets kørende traktorer stoppes. Kan ikke fortrydes."/>
|
<text name="spu_deleteConfirm" text="Slet run '%s'? Dets kørende traktorer stoppes. Kan ikke fortrydes."/>
|
||||||
<text name="spu_capacity" text="%s l"/>
|
<text name="spu_capacity" text="%s l"/>
|
||||||
<text name="spu_capacity_pallets" text="autoload (paller)"/>
|
<text name="spu_capacity_pallets" text="autoload (paller)"/>
|
||||||
@@ -390,5 +390,54 @@
|
|||||||
<text name="spu_guide_sum_unloaders" text="%d aflæsningsvogne"/>
|
<text name="spu_guide_sum_unloaders" text="%d aflæsningsvogne"/>
|
||||||
<text name="spu_guide_sum_fieldSilo" text="Marksilo"/>
|
<text name="spu_guide_sum_fieldSilo" text="Marksilo"/>
|
||||||
<text name="spu_guide_sum_stores" text="%d valgte siloer"/>
|
<text name="spu_guide_sum_stores" text="%d valgte siloer"/>
|
||||||
|
<text name="spu_guide_flow_open" text="Opsæt flow"/>
|
||||||
|
<text name="spu_guide_flow_title" text="— Opsæt flow: %s —"/>
|
||||||
|
<text name="spu_guide_flow_section" text="— Flow: %s —"/>
|
||||||
|
<text name="spu_guide_flow_saved" text="Flow '%s' gemt"/>
|
||||||
|
<text name="spu_guide_flow_start" text="Start flow"/>
|
||||||
|
<text name="spu_guide_flow_rename" text="Skift navn …"/>
|
||||||
|
<text name="spu_guide_flow_step_name" text="Navn"/>
|
||||||
|
<text name="spu_guide_flow_step_direction" text="Retning"/>
|
||||||
|
<text name="spu_guide_flow_step_from" text="Fra"/>
|
||||||
|
<text name="spu_guide_flow_step_to" text="Til"/>
|
||||||
|
<text name="spu_guide_flow_step_goods" text="Varer"/>
|
||||||
|
<text name="spu_guide_flow_step_store" text="Lager"/>
|
||||||
|
<text name="spu_guide_flow_step_rules" text="Regler"/>
|
||||||
|
<text name="spu_guide_flow_step_wait" text="Vent ved"/>
|
||||||
|
<text name="spu_guide_flow_step_tractors" text="Traktorer"/>
|
||||||
|
<text name="spu_guide_flow_step_summary" text="Oversigt"/>
|
||||||
|
<text name="spu_guide_flow_hint_name" text="Flowets navn (traktorerne får navn efter det)."/>
|
||||||
|
<text name="spu_guide_flow_hint_direction" text="Ind til bygning: fyld stalde/fabrikker. Ud fra bygning: tøm, lagr og sælg."/>
|
||||||
|
<text name="spu_guide_flow_hint_buildings_in" text="Hvilke stalde/fabrikker skal fyldes? Markøren findes automatisk."/>
|
||||||
|
<text name="spu_guide_flow_hint_buildings_out" text="Hvor skal der hentes fra? Markøren findes automatisk."/>
|
||||||
|
<text name="spu_guide_flow_hint_goods_in" text="Automatisk henter hver tur den vare målet mangler mest."/>
|
||||||
|
<text name="spu_guide_flow_hint_goods_out" text="For hver vare: Lager, Salg, eller Salg ellers lager."/>
|
||||||
|
<text name="spu_guide_flow_hint_store" text="Hvor skal lagervarerne hen?"/>
|
||||||
|
<text name="spu_guide_flow_hint_rules_in" text="Antal ture, og hvornår traktoren kører hjem fra Wait."/>
|
||||||
|
<text name="spu_guide_flow_hint_rules_out" text="Hvornår der sælges, og hvornår der køres til lager."/>
|
||||||
|
<text name="spu_guide_flow_hint_wait" text="Hvor venter traktoren med et rest-læs?"/>
|
||||||
|
<text name="spu_guide_flow_hint_tractors" text="Hvilke traktorer kører flowet? Traktorer i andre flows vises ikke."/>
|
||||||
|
<text name="spu_guide_flow_hint_summary" text="Tjek valgene. Tryk på en linje for at rette den."/>
|
||||||
|
<text name="spu_guide_flow_goods" text="Varer"/>
|
||||||
|
<text name="spu_guide_flow_goodsAutoChoice" text="Automatisk (det bygningen mangler)"/>
|
||||||
|
<text name="spu_guide_flow_goodsAutoText" text=" Hver tur hentes den vare målet mangler mest."/>
|
||||||
|
<text name="spu_guide_flow_noGoodsTo" text=" Vælg først hvor der skal køres til (trinnet Til)"/>
|
||||||
|
<text name="spu_guide_flow_noProducts" text=" Vælg først hvor der hentes fra (trinnet Fra)"/>
|
||||||
|
<text name="spu_guide_flow_storeAutoChoice" text="Automatisk (nærmeste med plads)"/>
|
||||||
|
<text name="spu_guide_flow_storeAutoText" text=" Lagerturene kører til nærmeste lager med plads."/>
|
||||||
|
<text name="spu_guide_flow_noTractors" text=" Ingen ledige traktorer"/>
|
||||||
|
<text name="spu_guide_flow_otherFlows" text=" %d traktorer kører i andre flows og vises ikke"/>
|
||||||
|
<text name="spu_guide_flow_blocked" text=" %d traktorer kan ikke bruges her (vognen passer ikke)"/>
|
||||||
|
<text name="spu_guide_flow_none" text="ingen valgt"/>
|
||||||
|
<text name="spu_guide_flow_goodsAuto" text="Automatisk"/>
|
||||||
|
<text name="spu_guide_flow_sumGoods" text="%d varer"/>
|
||||||
|
<text name="spu_guide_flow_sumSellAt" text="sælg fra %d %%"/>
|
||||||
|
<text name="spu_guide_flow_sumStoreAbove" text="lager over %d %%"/>
|
||||||
|
<text name="spu_guide_flow_sumRound" text="samlerunde"/>
|
||||||
|
<text name="spu_guide_flow_sumLoops" text="%d ture"/>
|
||||||
|
<text name="spu_guide_flow_sumLoopsInf" text="uendelige ture"/>
|
||||||
|
<text name="spu_guide_flow_sumReturn" text="hjem fra Wait under %d %%"/>
|
||||||
|
<text name="spu_guide_flow_sumNoTractors" text="ingen"/>
|
||||||
|
<text name="spu_guide_flow_sumTractors" text="%d traktorer"/>
|
||||||
</texts>
|
</texts>
|
||||||
</l10n>
|
</l10n>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<text name="spu_title" text="Smart Pickup"/>
|
<text name="spu_title" text="Smart Pickup"/>
|
||||||
<text name="spu_run" text="Run"/>
|
<text name="spu_run" text="Run"/>
|
||||||
<text name="spu_run_tooltip" text="Select the run to view or edit."/>
|
<text name="spu_run_tooltip" text="Select the run to view or edit."/>
|
||||||
<text name="spu_noRuns" text="No runs — press 'New run'"/>
|
<text name="spu_noRuns" text="No flows — press 'New flow'"/>
|
||||||
<text name="spu_target" text="Target"/>
|
<text name="spu_target" text="Target"/>
|
||||||
<text name="spu_target_tooltip" text="AutoDrive marker at the barn or factory to keep filled."/>
|
<text name="spu_target_tooltip" text="AutoDrive marker at the barn or factory to keep filled."/>
|
||||||
<text name="spu_noTarget" text="(choose target)"/>
|
<text name="spu_noTarget" text="(choose target)"/>
|
||||||
@@ -189,13 +189,13 @@
|
|||||||
<text name="spu_field_straw_chop" text="Chopped (spread on the field)"/>
|
<text name="spu_field_straw_chop" text="Chopped (spread on the field)"/>
|
||||||
<text name="spu_field_straw_collect" text="Collected to a silo"/>
|
<text name="spu_field_straw_collect" text="Collected to a silo"/>
|
||||||
<text name="spu_field_straw_bale" text="Baled and collected"/>
|
<text name="spu_field_straw_bale" text="Baled and collected"/>
|
||||||
<text name="spu_button_start" text="Start run"/>
|
<text name="spu_button_start" text="Start flow"/>
|
||||||
<text name="spu_button_stop" text="Stop run"/>
|
<text name="spu_button_stop" text="Stop flow"/>
|
||||||
<text name="spu_button_new" text="New run"/>
|
<text name="spu_button_new" text="New flow"/>
|
||||||
<text name="spu_button_rename" text="Rename"/>
|
<text name="spu_button_rename" text="Rename"/>
|
||||||
<text name="spu_button_delete" text="Delete run"/>
|
<text name="spu_button_delete" text="Delete flow"/>
|
||||||
<text name="spu_newRunName" text="Run %d"/>
|
<text name="spu_newRunName" text="Flow %d"/>
|
||||||
<text name="spu_renameTitle" text="Run name"/>
|
<text name="spu_renameTitle" text="Flow name"/>
|
||||||
<text name="spu_deleteConfirm" text="Delete run '%s'? Its running tractors will be stopped. This cannot be undone."/>
|
<text name="spu_deleteConfirm" text="Delete run '%s'? Its running tractors will be stopped. This cannot be undone."/>
|
||||||
<text name="spu_capacity" text="%s l"/>
|
<text name="spu_capacity" text="%s l"/>
|
||||||
<text name="spu_capacity_pallets" text="autoload (pallets)"/>
|
<text name="spu_capacity_pallets" text="autoload (pallets)"/>
|
||||||
@@ -390,5 +390,54 @@
|
|||||||
<text name="spu_guide_sum_unloaders" text="%d unloaders"/>
|
<text name="spu_guide_sum_unloaders" text="%d unloaders"/>
|
||||||
<text name="spu_guide_sum_fieldSilo" text="Field silo"/>
|
<text name="spu_guide_sum_fieldSilo" text="Field silo"/>
|
||||||
<text name="spu_guide_sum_stores" text="%d chosen silos"/>
|
<text name="spu_guide_sum_stores" text="%d chosen silos"/>
|
||||||
|
<text name="spu_guide_flow_open" text="Set up flow"/>
|
||||||
|
<text name="spu_guide_flow_title" text="— Set up flow: %s —"/>
|
||||||
|
<text name="spu_guide_flow_section" text="— Flow: %s —"/>
|
||||||
|
<text name="spu_guide_flow_saved" text="Flow '%s' saved"/>
|
||||||
|
<text name="spu_guide_flow_start" text="Start flow"/>
|
||||||
|
<text name="spu_guide_flow_rename" text="Change name …"/>
|
||||||
|
<text name="spu_guide_flow_step_name" text="Name"/>
|
||||||
|
<text name="spu_guide_flow_step_direction" text="Direction"/>
|
||||||
|
<text name="spu_guide_flow_step_from" text="From"/>
|
||||||
|
<text name="spu_guide_flow_step_to" text="To"/>
|
||||||
|
<text name="spu_guide_flow_step_goods" text="Goods"/>
|
||||||
|
<text name="spu_guide_flow_step_store" text="Storage"/>
|
||||||
|
<text name="spu_guide_flow_step_rules" text="Rules"/>
|
||||||
|
<text name="spu_guide_flow_step_wait" text="Wait at"/>
|
||||||
|
<text name="spu_guide_flow_step_tractors" text="Tractors"/>
|
||||||
|
<text name="spu_guide_flow_step_summary" text="Summary"/>
|
||||||
|
<text name="spu_guide_flow_hint_name" text="The flow's name (tractors are named after it)."/>
|
||||||
|
<text name="spu_guide_flow_hint_direction" text="Into building: keep barns/factories filled. Out of building: empty, store and sell."/>
|
||||||
|
<text name="spu_guide_flow_hint_buildings_in" text="Which barns/factories should be filled? The marker is found automatically."/>
|
||||||
|
<text name="spu_guide_flow_hint_buildings_out" text="Where should goods be picked up? The marker is found automatically."/>
|
||||||
|
<text name="spu_guide_flow_hint_goods_in" text="Automatic fetches whatever the target lacks most on each trip."/>
|
||||||
|
<text name="spu_guide_flow_hint_goods_out" text="For each good: Store, Sell, or Sell else store."/>
|
||||||
|
<text name="spu_guide_flow_hint_store" text="Where should stored goods go?"/>
|
||||||
|
<text name="spu_guide_flow_hint_rules_in" text="Number of trips, and when the tractor leaves Wait."/>
|
||||||
|
<text name="spu_guide_flow_hint_rules_out" text="When to sell, and when to drive to storage."/>
|
||||||
|
<text name="spu_guide_flow_hint_wait" text="Where does the tractor wait with a leftover load?"/>
|
||||||
|
<text name="spu_guide_flow_hint_tractors" text="Which tractors run this flow? Tractors in other flows are hidden."/>
|
||||||
|
<text name="spu_guide_flow_hint_summary" text="Check your choices. Click a line to change it."/>
|
||||||
|
<text name="spu_guide_flow_goods" text="Goods"/>
|
||||||
|
<text name="spu_guide_flow_goodsAutoChoice" text="Automatic (what the building needs)"/>
|
||||||
|
<text name="spu_guide_flow_goodsAutoText" text=" Each trip fetches the good the target lacks most."/>
|
||||||
|
<text name="spu_guide_flow_noGoodsTo" text=" First choose where to deliver (step To)"/>
|
||||||
|
<text name="spu_guide_flow_noProducts" text=" First choose where to pick up (step From)"/>
|
||||||
|
<text name="spu_guide_flow_storeAutoChoice" text="Automatic (nearest with room)"/>
|
||||||
|
<text name="spu_guide_flow_storeAutoText" text=" Storage trips go to the nearest store with room."/>
|
||||||
|
<text name="spu_guide_flow_noTractors" text=" No free tractors"/>
|
||||||
|
<text name="spu_guide_flow_otherFlows" text=" %d tractors run in other flows and are hidden"/>
|
||||||
|
<text name="spu_guide_flow_blocked" text=" %d tractors cannot be used here (trailer does not fit)"/>
|
||||||
|
<text name="spu_guide_flow_none" text="none chosen"/>
|
||||||
|
<text name="spu_guide_flow_goodsAuto" text="Automatic"/>
|
||||||
|
<text name="spu_guide_flow_sumGoods" text="%d goods"/>
|
||||||
|
<text name="spu_guide_flow_sumSellAt" text="sell from %d %%"/>
|
||||||
|
<text name="spu_guide_flow_sumStoreAbove" text="store above %d %%"/>
|
||||||
|
<text name="spu_guide_flow_sumRound" text="collection round"/>
|
||||||
|
<text name="spu_guide_flow_sumLoops" text="%d trips"/>
|
||||||
|
<text name="spu_guide_flow_sumLoopsInf" text="endless trips"/>
|
||||||
|
<text name="spu_guide_flow_sumReturn" text="leave Wait below %d %%"/>
|
||||||
|
<text name="spu_guide_flow_sumNoTractors" text="none"/>
|
||||||
|
<text name="spu_guide_flow_sumTractors" text="%d tractors"/>
|
||||||
</texts>
|
</texts>
|
||||||
</l10n>
|
</l10n>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<modDesc descVersion="107">
|
<modDesc descVersion="107">
|
||||||
<author>masterdraco</author>
|
<author>masterdraco</author>
|
||||||
<version>1.31.0.0</version>
|
<version>1.32.0.0</version>
|
||||||
<title>
|
<title>
|
||||||
<en>AD Smart Pickup</en>
|
<en>AD Smart Pickup</en>
|
||||||
</title>
|
</title>
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
<sourceFile filename="adStorageStats.lua"/>
|
<sourceFile filename="adStorageStats.lua"/>
|
||||||
<sourceFile filename="adFlowView.lua"/>
|
<sourceFile filename="adFlowView.lua"/>
|
||||||
<sourceFile filename="adGuide.lua"/>
|
<sourceFile filename="adGuide.lua"/>
|
||||||
|
<sourceFile filename="adFlowGuide.lua"/>
|
||||||
<sourceFile filename="adSources.lua"/>
|
<sourceFile filename="adSources.lua"/>
|
||||||
<sourceFile filename="adBuildings.lua"/>
|
<sourceFile filename="adBuildings.lua"/>
|
||||||
<sourceFile filename="adOutbound.lua"/>
|
<sourceFile filename="adOutbound.lua"/>
|
||||||
|
|||||||
+30
-7
@@ -159,12 +159,17 @@ samme rækkefølge.
|
|||||||
|
|
||||||
### Knapper
|
### Knapper
|
||||||
|
|
||||||
|
Fanen **Flows** viser tavlen (én linje pr. flow) og under den det valgte flows **resumé**: Navn, Retning,
|
||||||
|
Fra/Til, Varer, (Lager), Regler, Vent ved, Traktorer og Status. **Klik på et flow** i tavlen for at vælge det,
|
||||||
|
og **klik på en resumé-linje** for at rette netop det trin (**Gem** gemmer og går tilbage). Klik på
|
||||||
|
**Navn** for at omdøbe flowet.
|
||||||
|
|
||||||
| Knap | Funktion |
|
| Knap | Funktion |
|
||||||
|---|---|
|
|---|---|
|
||||||
| **Nyt run** | Opretter et nyt flow. Det starter som *Ind til bygning* |
|
| **Start flow / Stop flow** | Starter eller stopper alle traktorer i flowet |
|
||||||
| **Omdøb** | Giver det valgte flow et nyt navn |
|
| **Opsæt flow** | Guiden for det valgte flow med de nuværende valg |
|
||||||
| **Slet run** | Sletter flowet og stopper dets kørende traktorer. Kan ikke fortrydes |
|
| **Nyt flow** | Guiden for et nyt flow. Intet oprettes før du trykker **Start flow** eller **Gem uden at starte** |
|
||||||
| **Start run / Stop run** | Starter eller stopper alle traktorer i flowet |
|
| **Slet flow** | Sletter flowet og stopper dets kørende traktorer. Kan ikke fortrydes |
|
||||||
|
|
||||||
På fanen **Overblik** er knapperne i stedet:
|
På fanen **Overblik** er knapperne i stedet:
|
||||||
|
|
||||||
@@ -172,12 +177,30 @@ På fanen **Overblik** er knapperne i stedet:
|
|||||||
|---|---|
|
|---|---|
|
||||||
| **Start alle flows** | Starter alle flows med traktorer. Flows, der allerede kører, røres ikke, så ingen tur afbrydes |
|
| **Start alle flows** | Starter alle flows med traktorer. Flows, der allerede kører, røres ikke, så ingen tur afbrydes |
|
||||||
| **Stop alle flows** | Stopper alle aktive traktorer i alle flows |
|
| **Stop alle flows** | Stopper alle aktive traktorer i alle flows |
|
||||||
| **Nyt run** | Opretter et nyt flow og hopper til fanen **Flows** |
|
| **Nyt flow** | Hopper til fanen **Flows** og åbner guiden for et nyt flow |
|
||||||
|
|
||||||
Statuslinjen viser bagefter, hvor mange flows og traktorer der blev startet, og op til tre grunde til
|
Statuslinjen viser bagefter, hvor mange flows og traktorer der blev startet, og op til tre grunde til
|
||||||
traktorer, der blev sprunget over. Alle grunde står i `log.txt`.
|
traktorer, der blev sprunget over. Alle grunde står i `log.txt`.
|
||||||
|
|
||||||
Vælg flow i feltet **Run** øverst. Under tavlen står detaljerne for det valgte flow.
|
### Guiden "Opsæt flow"
|
||||||
|
|
||||||
|
Samme trin-skærm og knapper som markguiden (**Næste**, **Tilbage**, **Annullér**; ét spørgsmål ad gangen,
|
||||||
|
trin der ikke passer springes over):
|
||||||
|
|
||||||
|
| Trin | Vises når | Valg |
|
||||||
|
|---|---|---|
|
||||||
|
| **Navn** | nyt flow | *Skift navn …* åbner tekstfeltet |
|
||||||
|
| **Retning** | altid | *Ind til bygning* (forsyning) / *Ud fra bygning* (udkørsel) |
|
||||||
|
| **Til** / **Fra** | altid | Bygningerne (se *Bygningslisten* nedenfor) |
|
||||||
|
| **Varer** | altid | Forsyning: *Automatisk (det bygningen mangler)* eller *Vælg selv*. Udkørsel: pr. vare Fra / Lager / Salg / Salg, ellers lager |
|
||||||
|
| **Lager** | udkørsel med en vare på Lager | *Automatisk (nærmeste med plads)* eller *Vælg selv* blandt lagrene |
|
||||||
|
| **Regler** | altid | Udkørsel: sælg fra %, lager over %, saml op på én tur. Forsyning: ture, kør hjem fra Wait under % |
|
||||||
|
| **Vent ved** | altid | Wait-puljen |
|
||||||
|
| **Traktorer** | altid | Ledige traktorer og flowets egne. Traktorer i andre flows (og dem hvis vogn ikke passer) er skjult med en linje |
|
||||||
|
| **Oversigt** | altid | Resuméet; klik på en linje for at rette. **Start flow** eller **Gem uden at starte** |
|
||||||
|
|
||||||
|
Moden spørger aldrig om markøren ved bygningen, rutetjek, bedste foder først, læs-bytte, kildevalg,
|
||||||
|
fordeling på flere mål, hvile af tomme kilder eller traktornavne.
|
||||||
|
|
||||||
### Retning
|
### Retning
|
||||||
|
|
||||||
@@ -207,7 +230,7 @@ Et forsyningsflow holder én eller flere bygninger fyldt med alt det, de bruger.
|
|||||||
|
|
||||||
### Opsætning
|
### Opsætning
|
||||||
|
|
||||||
1. **Nyt run**, og lad Retning stå på **Ind til bygning**.
|
1. **Nyt flow** (guiden), og lad Retning stå på **Ind til bygning**.
|
||||||
2. Under **Til**: sæt Ja ud for de stalde og fabrikker, der skal fyldes. Listen viser også siloer,
|
2. Under **Til**: sæt Ja ud for de stalde og fabrikker, der skal fyldes. Listen viser også siloer,
|
||||||
der kun kan fyldes og ikke tømmes, fx Pumps & Hoses' drive-on-bunkere (DOB 115), der fodrer en
|
der kun kan fyldes og ikke tømmes, fx Pumps & Hoses' drive-on-bunkere (DOB 115), der fodrer en
|
||||||
biogas-fermentor. For dem er **Vælg** med de varer, fermentoren skal have, bedst.
|
biogas-fermentor. For dem er **Vælg** med de varer, fermentoren skal have, bedst.
|
||||||
|
|||||||
@@ -187,6 +187,22 @@ Afgrøde "Samme som høstet", Tromle Ja. Kalk Ja.
|
|||||||
**Til marker** går tilbage.
|
**Til marker** går tilbage.
|
||||||
- [ ] **10.12 Klik routes.** Alle Ja/Nej, valg og flueben i guiden reagerer (ingen "GUI-fejl" i statuslinjen/log).
|
- [ ] **10.12 Klik routes.** Alle Ja/Nej, valg og flueben i guiden reagerer (ingen "GUI-fejl" i statuslinjen/log).
|
||||||
|
|
||||||
|
## 11. Guiden "Opsæt flow" (v1.32)
|
||||||
|
|
||||||
|
- [ ] **11.1 Flows-fanen.** Tavlen øverst; valgt flow (">") har resumé-linjer med "[Ret]" og Status. Ingen
|
||||||
|
lang detaljeliste og ingen faste felter (Run, Wait-pulje …). Knapper: Start flow, Opsæt flow, Nyt flow, Slet flow.
|
||||||
|
- [ ] **11.2 Nyt flow.** Nyt flow → "Trin 1 af 8 — Navn" → *Skift navn …* → skriv navn → Næste gennem Retning
|
||||||
|
(Ud fra bygning), Fra (en fabrik), Varer (sæt en vare på Lager → trinnet Lager dukker op, tællingen bliver 9),
|
||||||
|
Regler, Vent ved, Traktorer (flueben) → Oversigt → **Start flow**. Forvent: flowet findes, kører, log
|
||||||
|
`run '…': …` med kind/products/storeBuildingIds.
|
||||||
|
- [ ] **11.3 Annullér nyt flow.** Nyt flow → Annullér (eller ESC) → intet nyt flow i tavlen.
|
||||||
|
- [ ] **11.4 Ret én linje.** Klik "Traktorer: …" → "Ret: Traktorer" → sæt flueben → **Gem** → tilbage; traktoren er i flowet.
|
||||||
|
- [ ] **11.5 Omdøb.** Klik "Navn: …" → tekstfeltet åbner → nyt navn står i tavlen.
|
||||||
|
- [ ] **11.6 Andre flows skjult.** Traktor i et andet flow vises ikke i trinnet Traktorer; linje "N traktorer kører i andre flows".
|
||||||
|
- [ ] **11.7 Forsyning.** Opsæt et forsyningsflow: Til = stald, Varer = Vælg selv → varerne stalden tager vises; Regler viser Ture og "Kør hjem fra Wait under".
|
||||||
|
- [ ] **11.8 Nyt flow fra Oversigt-fanen** hopper til Flows med guiden åben.
|
||||||
|
- [ ] **11.9 Klik routes.** Alle valg (også pr. vare-politik) reagerer; ingen "GUI-fejl".
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Kendte usikkerheder (første gang i spillet)
|
## Kendte usikkerheder (første gang i spillet)
|
||||||
|
|||||||
+30
-7
@@ -161,12 +161,17 @@ the same order.
|
|||||||
|
|
||||||
### Buttons
|
### Buttons
|
||||||
|
|
||||||
|
The **Flows** tab shows the board (one line per flow) and below it the selected flow's **summary**: Name,
|
||||||
|
Direction, From/To, Goods, (Storage), Rules, Wait at, Tractors and Status. **Click a flow** on the board to
|
||||||
|
select it, and **click a summary line** to change just that step (**Save** saves and returns). Click
|
||||||
|
**Name** to rename the flow.
|
||||||
|
|
||||||
| Button | What it does |
|
| Button | What it does |
|
||||||
|---|---|
|
|---|---|
|
||||||
| **New run** | Creates a new flow. It starts as *Into building* |
|
| **Start flow / Stop flow** | Starts or stops all tractors in the flow |
|
||||||
| **Rename** | Renames the selected flow |
|
| **Set up flow** | The guide for the selected flow with its current choices |
|
||||||
| **Delete run** | Deletes the flow and stops its running tractors. Cannot be undone |
|
| **New flow** | The guide for a new flow. Nothing is created before you press **Start flow** or **Save without starting** |
|
||||||
| **Start run / Stop run** | Starts or stops all tractors in the flow |
|
| **Delete flow** | Deletes the flow and stops its running tractors. Cannot be undone |
|
||||||
|
|
||||||
On the **Overview** tab the buttons are instead:
|
On the **Overview** tab the buttons are instead:
|
||||||
|
|
||||||
@@ -174,12 +179,30 @@ On the **Overview** tab the buttons are instead:
|
|||||||
|---|---|
|
|---|---|
|
||||||
| **Start all flows** | Starts every flow that has tractors. Flows already running are left alone, so no trip is interrupted |
|
| **Start all flows** | Starts every flow that has tractors. Flows already running are left alone, so no trip is interrupted |
|
||||||
| **Stop all flows** | Stops all active tractors in all flows |
|
| **Stop all flows** | Stops all active tractors in all flows |
|
||||||
| **New run** | Creates a new flow and jumps to the **Flows** tab |
|
| **New flow** | Jumps to the **Flows** tab and opens the guide for a new flow |
|
||||||
|
|
||||||
Afterwards the status line shows how many flows and tractors were started, plus up to three reasons for
|
Afterwards the status line shows how many flows and tractors were started, plus up to three reasons for
|
||||||
tractors that were skipped. All reasons are in `log.txt`.
|
tractors that were skipped. All reasons are in `log.txt`.
|
||||||
|
|
||||||
Pick a flow in the **Run** field at the top. The details of the selected flow are shown below the board.
|
### The "Set up flow" guide
|
||||||
|
|
||||||
|
Same step screen and buttons as the field guide (**Next**, **Back**, **Cancel**; one question at a time,
|
||||||
|
steps that do not apply are skipped):
|
||||||
|
|
||||||
|
| Step | Shown when | Choices |
|
||||||
|
|---|---|---|
|
||||||
|
| **Name** | new flow | *Change name …* opens the text field |
|
||||||
|
| **Direction** | always | *Into building* (supply) / *Out of building* (delivery) |
|
||||||
|
| **To** / **From** | always | The buildings (see *The building list* below) |
|
||||||
|
| **Goods** | always | Supply: *Automatic (what the building needs)* or *Choose myself*. Delivery: per good Off / Store / Sell / Sell, else store |
|
||||||
|
| **Storage** | delivery with a good on Store | *Automatic (nearest with room)* or *Choose myself* among the stores |
|
||||||
|
| **Rules** | always | Delivery: sell from %, store above %, collect in one trip. Supply: trips, leave Wait below % |
|
||||||
|
| **Wait at** | always | The Wait pool |
|
||||||
|
| **Tractors** | always | Free tractors and the flow's own. Tractors in other flows (and those whose trailer does not fit) are hidden with a line |
|
||||||
|
| **Summary** | always | The summary; click a line to change it. **Start flow** or **Save without starting** |
|
||||||
|
|
||||||
|
The mod never asks about the marker at the building, route checks, best feed first, load swap, source
|
||||||
|
choice, spreading over several targets, resting empty sources or tractor names.
|
||||||
|
|
||||||
### Direction
|
### Direction
|
||||||
|
|
||||||
@@ -209,7 +232,7 @@ A supply flow keeps one or more buildings filled with everything they use.
|
|||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
|
|
||||||
1. **New run**, and leave Direction on **Into building**.
|
1. **New flow** (the guide), and leave Direction on **Into building**.
|
||||||
2. Under **To**: set Yes on the barns and factories to fill. The list also shows silos that can only
|
2. Under **To**: set Yes on the barns and factories to fill. The list also shows silos that can only
|
||||||
be filled and not emptied, such as the Pumps & Hoses drive-on bunkers (DOB 115) that feed a biogas
|
be filled and not emptied, such as the Pumps & Hoses drive-on bunkers (DOB 115) that feed a biogas
|
||||||
fermenter. For those, **Select** with the goods the fermenter needs works best.
|
fermenter. For those, **Select** with the goods the fermenter needs works best.
|
||||||
|
|||||||
@@ -0,0 +1,232 @@
|
|||||||
|
-- Kør: luajit tests/test_adFlowGuide.lua (fra repo-roden)
|
||||||
|
dofile("FS25_ADSmartPickup/adGuide.lua")
|
||||||
|
dofile("FS25_ADSmartPickup/adRuns.lua")
|
||||||
|
dofile("FS25_ADSmartPickup/adFlowView.lua")
|
||||||
|
dofile("FS25_ADSmartPickup/adFlowGuide.lua")
|
||||||
|
local FG, G = ADFlowGuide, ADGuide
|
||||||
|
local failures = 0
|
||||||
|
local function check(name, actual, expected)
|
||||||
|
if actual == expected then print("OK " .. name) else
|
||||||
|
failures = failures + 1
|
||||||
|
print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function newRun(kind, changes)
|
||||||
|
local store, run = ADRuns.createRun(ADRuns.newStore(), "Mælk ud", kind)
|
||||||
|
if changes ~= nil then store = ADRuns.updateRun(store, run.id, changes) end
|
||||||
|
return ADRuns.getRun(store, run.id)
|
||||||
|
end
|
||||||
|
local function ctx(extra)
|
||||||
|
local context = {
|
||||||
|
buildings = {
|
||||||
|
{id = "b1", name = "Kostald", label = "Kostald M", hasMarker = true, text = "Kostald — M 5 m"},
|
||||||
|
{id = "b2", name = "Silo", label = "Silo M", hasMarker = true, text = "Silo — M 3 m"},
|
||||||
|
{id = "b3", name = "Lade", hasMarker = false, text = "Lade — INGEN MARKØR"},
|
||||||
|
},
|
||||||
|
fillTypes = {{name = "HAY", title = "Hø"}, {name = "STRAW", title = "Halm"}},
|
||||||
|
products = {{name = "MILK", title = "Mælk"}, {name = "EGG", title = "Æg"}},
|
||||||
|
storeBuildings = {{id = "s1", name = "Silo Nord", text = "Silo Nord — M 5 m"}, {id = "s2", name = "Palleskur", text = "Palleskur — M 2 m"}},
|
||||||
|
waitPools = {{group = "Wait A", count = 2}},
|
||||||
|
vehicles = {
|
||||||
|
{id = "t1", label = "Traktor 1", text = "Traktor 1 — 1000 l — OK"},
|
||||||
|
{id = "t2", text = "Traktor 2", otherRunId = 9, otherRunName = "Andet"},
|
||||||
|
{id = "t3", text = "Traktor 3", blocked = true},
|
||||||
|
{id = "t4", text = "Traktor 4"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for key, value in pairs(extra or {}) do context[key] = value end
|
||||||
|
return context
|
||||||
|
end
|
||||||
|
local function steps(guide) return table.concat(G.visibleSteps(guide), ",") end
|
||||||
|
local function findRow(rows, id, item)
|
||||||
|
for _, row in ipairs(rows) do
|
||||||
|
if row.id == id and (item == nil or row.item == item) then return row end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local function countType(rows, rowType)
|
||||||
|
local n = 0
|
||||||
|
for _, row in ipairs(rows) do if row.type == rowType then n = n + 1 end end
|
||||||
|
return n
|
||||||
|
end
|
||||||
|
local function textKeys(rows)
|
||||||
|
local keys = {}
|
||||||
|
for _, row in ipairs(rows) do if row.type == "text" and row.label.key ~= nil then table.insert(keys, row.label.key) end end
|
||||||
|
return table.concat(keys, ",")
|
||||||
|
end
|
||||||
|
local function at(guide, step) return G.jump(guide, step, "close") end
|
||||||
|
|
||||||
|
-- ---- trin ----
|
||||||
|
local supply = FG.open(newRun("supply"), ctx())
|
||||||
|
check("FL1 forsyning", steps(supply), "direction,buildings,goods,rules,wait,tractors,summary")
|
||||||
|
local out = FG.open(newRun("outbound"), ctx())
|
||||||
|
check("FL1 udkørsel uden lager-vare", steps(out), "direction,buildings,goods,rules,wait,tractors,summary")
|
||||||
|
local outStore = FG.open(newRun("outbound", {products = {{fillType = "MILK", policy = "sellElseStore"}}}), ctx())
|
||||||
|
check("FL1 udkørsel med lager", steps(outStore), "direction,buildings,goods,store,rules,wait,tractors,summary")
|
||||||
|
local new = FG.openNew("Flow 3", ctx())
|
||||||
|
check("FL1 nyt flow har navn", steps(new), "name,direction,buildings,goods,rules,wait,tractors,summary")
|
||||||
|
check("FL1 nyt flow starter på navn", new.step, "name")
|
||||||
|
check("FL1 nyt flow er forsyning", new.draft.kind, "supply")
|
||||||
|
check("FL1 nyt flow navn", new.draft.name, "Flow 3")
|
||||||
|
check("FL1 eksisterende starter på retning", supply.step, "direction")
|
||||||
|
|
||||||
|
-- ---- titler følger retningen ----
|
||||||
|
check("FL2 til", FG.stepKey("buildings", supply), "spu_guide_flow_step_to")
|
||||||
|
check("FL2 fra", FG.stepKey("buildings", out), "spu_guide_flow_step_from")
|
||||||
|
check("FL2 andre", FG.stepKey("rules", out), "spu_guide_flow_step_rules")
|
||||||
|
check("FL2 hint", FG.hintKey("goods", out), "spu_guide_flow_hint_goods_out")
|
||||||
|
check("FL2 hint forsyning", FG.hintKey("goods", supply), "spu_guide_flow_hint_goods_in")
|
||||||
|
|
||||||
|
-- ---- choose er immutabel ----
|
||||||
|
local toOut = FG.choose(supply, "kind", "outbound")
|
||||||
|
check("FL3 retning skiftet", toOut.draft.kind, "outbound")
|
||||||
|
check("FL3 gammel urørt", supply.draft.kind, "supply")
|
||||||
|
local withTarget = FG.choose(supply, "buildings", {item = "b1", on = true})
|
||||||
|
check("FL3 forsyning: mål", table.concat(withTarget.draft.targetBuildingIds, ","), "b1")
|
||||||
|
check("FL3 forsyning: kilder urørt", #withTarget.draft.sourceBuildingIds, 0)
|
||||||
|
local withSource = FG.choose(out, "buildings", {item = "b2", on = true})
|
||||||
|
check("FL3 udkørsel: kilde", table.concat(withSource.draft.sourceBuildingIds, ","), "b2")
|
||||||
|
local policy = FG.choose(out, "policy", {item = "MILK", value = "store"})
|
||||||
|
check("FL3 politik", policy.draft.products[1].policy, "store")
|
||||||
|
check("FL3 politik gør lager-trin synligt", FG.isVisible("store", policy), true)
|
||||||
|
local off = FG.choose(policy, "policy", {item = "MILK", value = "off"})
|
||||||
|
check("FL3 politik fra fjerner", #off.draft.products, 0)
|
||||||
|
local replaced = FG.choose(policy, "policy", {item = "MILK", value = "sell"})
|
||||||
|
check("FL3 politik erstattes", #replaced.draft.products, 1)
|
||||||
|
check("FL3 politik ny", replaced.draft.products[1].policy, "sell")
|
||||||
|
check("FL3 navn", FG.choose(new, "name", "Æg til salg").draft.name, "Æg til salg")
|
||||||
|
check("FL3 tomt navn ignoreres", FG.choose(new, "name", " ").draft.name, "Flow 3")
|
||||||
|
|
||||||
|
-- ---- rækker ----
|
||||||
|
local dirRows = FG.rows(supply)
|
||||||
|
check("FL4 retning valg", findRow(dirRows, "kind").type, "choice")
|
||||||
|
check("FL4 retning værdi", findRow(dirRows, "kind").value, "supply")
|
||||||
|
local buildingRows = FG.rows(at(supply, "buildings"))
|
||||||
|
check("FL4 bygninger med markør", countType(buildingRows, "check"), 2)
|
||||||
|
check("FL4 vis alle", findRow(buildingRows, "showAllBuildings").type, "toggle")
|
||||||
|
local allRows = FG.rows(at(FG.choose(supply, "showAllBuildings", true), "buildings"))
|
||||||
|
check("FL4 vis alle: tre", countType(allRows, "check"), 3)
|
||||||
|
local noHidden = FG.rows(at(FG.open(newRun("supply"), ctx({buildings = {{id = "b1", name = "Kostald", hasMarker = true, text = "K"}}})), "buildings"))
|
||||||
|
check("FL4 ingen vis-alle uden skjulte", findRow(noHidden, "showAllBuildings"), nil)
|
||||||
|
local chosenFirst = FG.rows(at(FG.choose(supply, "buildings", {item = "b2", on = true}), "buildings"))
|
||||||
|
local firstCheck
|
||||||
|
for _, row in ipairs(chosenFirst) do if row.type == "check" and firstCheck == nil then firstCheck = row end end
|
||||||
|
check("FL4 fast rækkefølge (navn)", firstCheck.item, "b1")
|
||||||
|
check("FL4 valgt markeret", findRow(chosenFirst, "buildings", "b2").value, true)
|
||||||
|
check("FL4 ingen bygninger", textKeys(FG.rows(at(FG.open(newRun("supply"), ctx({buildings = {}})), "buildings"))), "spu_noBuildings")
|
||||||
|
|
||||||
|
local goodsAuto = FG.rows(at(supply, "goods"))
|
||||||
|
check("FL5 forsyning auto", findRow(goodsAuto, "ingredientMode").value, "auto")
|
||||||
|
check("FL5 forsyning auto: ingen liste", countType(goodsAuto, "check"), 0)
|
||||||
|
local goodsManual = FG.rows(at(FG.choose(supply, "ingredientMode", "manual"), "goods"))
|
||||||
|
check("FL5 forsyning vælg: varer", countType(goodsManual, "check"), 2)
|
||||||
|
check("FL5 forsyning vælg uden varer", textKeys(FG.rows(at(FG.choose(FG.open(newRun("supply"), ctx({fillTypes = {}})), "ingredientMode", "manual"), "goods"))), "spu_guide_flow_noGoodsTo")
|
||||||
|
local goodsOut = FG.rows(at(policy, "goods"))
|
||||||
|
check("FL5 udkørsel: politik pr. vare", countType(goodsOut, "choice"), 2)
|
||||||
|
check("FL5 udkørsel: mælk lager", findRow(goodsOut, "policy", "MILK").value, "store")
|
||||||
|
check("FL5 udkørsel: æg fra", findRow(goodsOut, "policy", "EGG").value, "off")
|
||||||
|
check("FL5 udkørsel: politikker", #findRow(goodsOut, "policy", "EGG").options, 4)
|
||||||
|
check("FL5 udkørsel uden varer", textKeys(FG.rows(at(FG.open(newRun("outbound"), ctx({products = {}})), "goods"))), "spu_guide_flow_noProducts")
|
||||||
|
|
||||||
|
local storeRows = FG.rows(at(outStore, "store"))
|
||||||
|
check("FL6 lager auto", findRow(storeRows, "storeAuto").value, true)
|
||||||
|
check("FL6 lager auto: ingen liste", countType(storeRows, "check"), 0)
|
||||||
|
local storeManual = FG.rows(at(FG.choose(outStore, "storeAuto", false), "store"))
|
||||||
|
check("FL6 lager vælg selv", countType(storeManual, "check"), 2)
|
||||||
|
local chosenStore = FG.open(newRun("outbound", {products = {{fillType = "MILK", policy = "store"}}, storeBuildingIds = {"s2"}}), ctx())
|
||||||
|
check("FL6 valgt lager -> vælg selv", chosenStore.draft.storeAuto, false)
|
||||||
|
|
||||||
|
local rulesOut = FG.rows(at(out, "rules"))
|
||||||
|
check("FL7 udkørsel: sælg fra", findRow(rulesOut, "sellAtPercent").value, 90)
|
||||||
|
check("FL7 udkørsel: sælg-trin", #findRow(rulesOut, "sellAtPercent").options, 11)
|
||||||
|
check("FL7 udkørsel: lager over", findRow(rulesOut, "storeAbovePercent").value, 80)
|
||||||
|
check("FL7 udkørsel: samlerunde", findRow(rulesOut, "collectRound").type, "toggle")
|
||||||
|
check("FL7 udkørsel: ingen ture", findRow(rulesOut, "loops"), nil)
|
||||||
|
local rulesIn = FG.rows(at(supply, "rules"))
|
||||||
|
check("FL7 forsyning: ture", findRow(rulesIn, "loops").value, 0)
|
||||||
|
check("FL7 forsyning: ture-valg", #findRow(rulesIn, "loops").options, ADRuns.MAX_LOOPS + 1)
|
||||||
|
check("FL7 forsyning: 0 = uendeligt", findRow(rulesIn, "loops").options[1].label.key, "spu_loops_infinite")
|
||||||
|
check("FL7 forsyning: retur", findRow(rulesIn, "returnBelowPercent").value, 50)
|
||||||
|
check("FL7 forsyning: ingen salg", findRow(rulesIn, "sellAtPercent"), nil)
|
||||||
|
|
||||||
|
local waitRows = FG.rows(at(supply, "wait"))
|
||||||
|
check("FL8 vent: alle først", findRow(waitRows, "waitPoolGroup").options[1].value, "")
|
||||||
|
check("FL8 vent: puljer", #findRow(waitRows, "waitPoolGroup").options, 2)
|
||||||
|
|
||||||
|
local assigned = FG.open(newRun("supply", {}), ctx())
|
||||||
|
assigned = FG.choose(assigned, "vehicleIds", {item = "t4", on = true})
|
||||||
|
local tractorRows = FG.rows(at(assigned, "tractors"))
|
||||||
|
check("FL9 traktorer: kun egne + ledige", countType(tractorRows, "check"), 2)
|
||||||
|
check("FL9 egne først", (function() for _, r in ipairs(tractorRows) do if r.type == "check" then return r.item end end end)(), "t4")
|
||||||
|
check("FL9 skjult andre flows", findRow(tractorRows, "vehicleIds", "t2"), nil)
|
||||||
|
check("FL9 skjult blokeret", findRow(tractorRows, "vehicleIds", "t3"), nil)
|
||||||
|
check("FL9 linjer", textKeys(tractorRows), "spu_guide_flow_otherFlows,spu_guide_flow_blocked")
|
||||||
|
local inThisRun = FG.open(newRun("supply"), ctx({vehicles = {{id = "t2", text = "T2", otherRunId = 1, otherRunName = "Mælk ud"}}}))
|
||||||
|
check("FL9 traktor i dette flow er ikke 'andet flow'", countType(FG.rows(at(inThisRun, "tractors")), "check"), 1)
|
||||||
|
|
||||||
|
-- ---- resumé ----
|
||||||
|
local function lineFor(lines, step)
|
||||||
|
for _, line in ipairs(lines) do if line.step == step then return line end end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local function partText(line)
|
||||||
|
local parts = {}
|
||||||
|
for _, part in ipairs(line.parts) do table.insert(parts, part.key or ("=" .. tostring(part.text))) end
|
||||||
|
return table.concat(parts, ",")
|
||||||
|
end
|
||||||
|
local sumIn = FG.summary(FG.choose(withTarget, "vehicleIds", {item = "t1", on = true}))
|
||||||
|
check("FL10 navn", partText(lineFor(sumIn, "name")), "=Mælk ud")
|
||||||
|
check("FL10 retning", partText(lineFor(sumIn, "direction")), "spu_direction_in")
|
||||||
|
check("FL10 til", partText(lineFor(sumIn, "buildings")), "=Kostald")
|
||||||
|
check("FL10 til-titel", lineFor(sumIn, "buildings").title.key, "spu_guide_flow_step_to")
|
||||||
|
check("FL10 varer auto", partText(lineFor(sumIn, "goods")), "spu_guide_flow_goodsAuto")
|
||||||
|
check("FL10 regler", partText(lineFor(sumIn, "rules")), "spu_guide_flow_sumLoopsInf,spu_guide_flow_sumReturn")
|
||||||
|
check("FL10 vent", partText(lineFor(sumIn, "wait")), "spu_waitPool_all")
|
||||||
|
check("FL10 traktorer", partText(lineFor(sumIn, "tractors")), "=Traktor 1")
|
||||||
|
check("FL10 intet lager-trin", lineFor(sumIn, "store"), nil)
|
||||||
|
local sumOut = FG.summary(FG.choose(FG.choose(policy, "collectRound", true), "sellAtPercent", 95))
|
||||||
|
check("FL11 retning ud", partText(lineFor(sumOut, "direction")), "spu_direction_out")
|
||||||
|
check("FL11 fra tom", partText(lineFor(sumOut, "buildings")), "spu_guide_flow_none")
|
||||||
|
check("FL11 varer", partText(lineFor(sumOut, "goods")), "spu_policy_store")
|
||||||
|
check("FL11 varenavn foran", lineFor(sumOut, "goods").parts[1].prefix, "Mælk: ")
|
||||||
|
check("FL11 lager auto", partText(lineFor(sumOut, "store")), "spu_guide_sum_auto")
|
||||||
|
check("FL11 regler", partText(lineFor(sumOut, "rules")), "spu_guide_flow_sumSellAt,spu_guide_flow_sumStoreAbove,spu_guide_flow_sumRound")
|
||||||
|
check("FL11 sælg 95", lineFor(sumOut, "rules").parts[1].args[1], 95)
|
||||||
|
check("FL11 ingen traktorer", partText(lineFor(sumOut, "tractors")), "spu_guide_flow_sumNoTractors")
|
||||||
|
local manyGoods = FG.summary(FG.choose(FG.choose(FG.choose(supply, "ingredientMode", "manual"), "ingredients", {item = "HAY", on = true}), "ingredients", {item = "STRAW", on = true}))
|
||||||
|
check("FL12 valgte varer", partText(lineFor(manyGoods, "goods")), "=Hø + Halm")
|
||||||
|
check("FL12 oversigt: links", countType(FG.rows(G.new(supply.def, supply.draft, supply.context, {step = "summary"})), "link"), 7)
|
||||||
|
|
||||||
|
-- ---- resultat ----
|
||||||
|
local r = FG.result(FG.choose(FG.choose(policy, "storeAuto", false), "storeBuildingIds", {item = "s1", on = true}))
|
||||||
|
check("FL13 ikke ny", r.isNew, false)
|
||||||
|
check("FL13 run-id", r.runId, 1)
|
||||||
|
check("FL13 retning", r.changes.kind, "outbound")
|
||||||
|
check("FL13 lager valgt", table.concat(r.changes.storeBuildingIds, ","), "s1")
|
||||||
|
check("FL13 varer", r.changes.products[1].fillType, "MILK")
|
||||||
|
check("FL13 lager-resumé bruger navn", (function() for _, l in ipairs(FG.summary(FG.choose(FG.choose(policy, "storeAuto", false), "storeBuildingIds", {item = "s1", on = true}))) do if l.step == "store" then return l.parts[1].text end end end)(), "Silo Nord")
|
||||||
|
local rAuto = FG.result(FG.choose(chosenStore, "storeAuto", true))
|
||||||
|
check("FL13 lager auto -> tom", #rAuto.changes.storeBuildingIds, 0)
|
||||||
|
local hiddenStore = FG.open(newRun("outbound", {storeBuildingIds = {"s2"}}), ctx())
|
||||||
|
check("FL13 lager usynligt: bevares", table.concat(FG.result(hiddenStore).changes.storeBuildingIds, ","), "s2")
|
||||||
|
local rNew = FG.result(FG.choose(new, "vehicleIds", {item = "t1", on = true}))
|
||||||
|
check("FL14 nyt", rNew.isNew, true)
|
||||||
|
check("FL14 navn", rNew.name, "Flow 3")
|
||||||
|
check("FL14 traktorer", table.concat(rNew.vehicleIds, ","), "t1")
|
||||||
|
check("FL14 ingen run-id", rNew.runId, nil)
|
||||||
|
local added, removed = FG.vehicleChanges({"a", "b"}, {"b", "c"})
|
||||||
|
check("FL15 tilføjet", table.concat(added, ","), "c")
|
||||||
|
check("FL15 fjernet", table.concat(removed, ","), "a")
|
||||||
|
|
||||||
|
-- ---- forhåndsvisning (til controllerens opslag) ----
|
||||||
|
local markers = {b1 = {101, 102}, b2 = {201}}
|
||||||
|
local preview = FG.previewRun(withSource.draft, function(id) return markers[id] or {} end)
|
||||||
|
check("FL16 kilder -> markører", table.concat(preview.sourceWayPointIds, ","), "201")
|
||||||
|
local previewIn = FG.previewRun(withTarget.draft, function(id) return markers[id] or {} end)
|
||||||
|
check("FL16 mål -> markører", table.concat(previewIn.targetWayPointIds, ","), "101,102")
|
||||||
|
check("FL16 primært mål", previewIn.targetWayPointId, 101)
|
||||||
|
check("FL16 kladde urørt", withTarget.draft.targetWayPointIds == nil or #withTarget.draft.targetWayPointIds == 0, true)
|
||||||
|
|
||||||
|
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
||||||
|
os.exit(failures == 0 and 0 or 1)
|
||||||
@@ -104,6 +104,7 @@ local function T(key) return texts[key] or key end
|
|||||||
check("G14 label args", G.labelText({key = "greet", args = {"Bo"}}, T), "Hej Bo")
|
check("G14 label args", G.labelText({key = "greet", args = {"Bo"}}, T), "Hej Bo")
|
||||||
check("G14 label tekst", G.labelText({text = "rå"}, T), "rå")
|
check("G14 label tekst", G.labelText({text = "rå"}, T), "rå")
|
||||||
check("G14 manglende args fejler ikke", G.labelText({key = "n"}, T), "%d stk")
|
check("G14 manglende args fejler ikke", G.labelText({key = "n"}, T), "%d stk")
|
||||||
|
check("G14 prefix", G.labelText({key = "plain", prefix = "Mælk: "}, T), "Mælk: Ingen")
|
||||||
check("G14 nil label", G.labelText(nil, T), "")
|
check("G14 nil label", G.labelText(nil, T), "")
|
||||||
check("G15 linje", G.lineText({step = "crop", title = {key = "title"}, parts = {{key = "plain"}, {key = "n", args = {3}}}}, T), "Afgrøde: Ingen, 3 stk")
|
check("G15 linje", G.lineText({step = "crop", title = {key = "title"}, parts = {{key = "plain"}, {key = "n", args = {3}}}}, T), "Afgrøde: Ingen, 3 stk")
|
||||||
check("G15 linje uden dele", G.lineText({step = "crop", title = {key = "title"}, parts = {}}, T), "Afgrøde: -")
|
check("G15 linje uden dele", G.lineText({step = "crop", title = {key = "title"}, parts = {}}, T), "Afgrøde: -")
|
||||||
|
|||||||
Reference in New Issue
Block a user