fix: use NBT ETA and clean monitor debug output

This commit is contained in:
2026-08-02 17:33:05 +08:00
parent 9060e79006
commit b7be4cd00d
+51 -126
View File
@@ -2,7 +2,7 @@
-- ============================================================================ -- ============================================================================
-- 1. Configuration & Constants -- 1. Configuration & Constants
-- ============================================================================ -- ============================================================================
local APP_VERSION_TEXT = "v1.0.34" local APP_VERSION_TEXT = "v1.0.35"
local CONFIG = { local CONFIG = {
CLOUD_URL = "ws://stabadmin.fse-media.group", CLOUD_URL = "ws://stabadmin.fse-media.group",
@@ -51,7 +51,6 @@ local State = {
currentPlaying = nil, currentPlaying = nil,
currentPriority = -1, currentPriority = -1,
interruptSeq = 0, interruptSeq = 0,
debugUpcoming = nil,
tickRate = 20 tickRate = 20
} }
@@ -2135,65 +2134,6 @@ function Display.render()
mon.setTextColor(COLORS.DEBUG) mon.setTextColor(COLORS.DEBUG)
mon.setCursorPos(1, upcomingStartY) mon.setCursorPos(1, upcomingStartY)
mon.write("UPCOMING TRAINS:") mon.write("UPCOMING TRAINS:")
if CONFIG.DEBUG and State.debugUpcoming and upcomingStartY + 1 <= h then
mon.setCursorPos(1, upcomingStartY + 1)
mon.setTextColor(COLORS.DEBUG)
local dbg = State.debugUpcoming
local dbgLine = string.format(
"SRC BR:%s CT:%s OK:%s TS:%s TPS:%s",
dbg.hasBR and "Y" or "N",
dbg.hasCT and "Y" or "N",
dbg.ok and "Y" or "N",
tostring(dbg.trainStopsCount or 0),
tostring(math.floor((dbg.tickRate or 20) * 10) / 10)
)
if #dbgLine > w then dbgLine = dbgLine:sub(1, w) end
mon.write(dbgLine)
if upcomingStartY + 2 <= h then
mon.setCursorPos(1, upcomingStartY + 2)
local keyLine = string.format(
"TK:%s NK:%s",
tostring(dbg.textKey or "-"),
tostring(dbg.nbtKey or "-")
)
if #keyLine > w then keyLine = keyLine:sub(1, w) end
mon.write(keyLine)
end
if upcomingStartY + 3 <= h then
mon.setCursorPos(1, upcomingStartY + 3)
local etaLine = string.format(
"TXT:%s NBT:%s USE:%s",
tostring(dbg.textEta or "-"),
tostring(dbg.nbtEta or "-"),
tostring(dbg.usedEta or "-")
)
if #etaLine > w then etaLine = etaLine:sub(1, w) end
mon.write(etaLine)
end
if upcomingStartY + 4 <= h then
mon.setCursorPos(1, upcomingStartY + 4)
local rawLine = "ETASTR:" .. tostring(dbg.textEtaRaw or "-")
if #rawLine > w then rawLine = rawLine:sub(1, w) end
mon.write(rawLine)
end
if upcomingStartY + 5 <= h then
mon.setCursorPos(1, upcomingStartY + 5)
local partsLine = string.format(
"VAL:%s UNIT:%s",
tostring(dbg.textEtaValue or "-"),
tostring(dbg.textEtaUnit or "-")
)
if #partsLine > w then partsLine = partsLine:sub(1, w) end
mon.write(partsLine)
end
if upcomingStartY + 6 <= h then
mon.setCursorPos(1, upcomingStartY + 6)
local lineRaw = "RAW:" .. tostring(dbg.rawLine or "-")
if #lineRaw > w then lineRaw = lineRaw:sub(1, w) end
mon.write(lineRaw)
end
logsStartY = upcomingStartY + 7
end
if hasUpcoming then if hasUpcoming then
mon.setTextColor(COLORS.INFO) mon.setTextColor(COLORS.INFO)
local visibleCount = math.min(3, #State.upcomingTrains, math.max(0, maxUpcomingRows - 1)) local visibleCount = math.min(3, #State.upcomingTrains, math.max(0, maxUpcomingRows - 1))
@@ -2213,7 +2153,8 @@ function Display.render()
if t.platform and tostring(t.platform) ~= "" then if t.platform and tostring(t.platform) ~= "" then
pfx = "P" .. tostring(t.platform) .. " " pfx = "P" .. tostring(t.platform) .. " "
end end
local lineText = string.format("%s%s->%s ETA:%ds%s", pfx, t.trainName, t.dest, t.eta, suffix) local etaLabel = tostring(t.etaLabel or (tostring(t.eta or 0) .. "s"))
local lineText = string.format("%s%s->%s ETA:%s%s", pfx, t.trainName, t.dest, etaLabel, suffix)
if #lineText > w then lineText = lineText:sub(1, w) end if #lineText > w then lineText = lineText:sub(1, w) end
mon.write(lineText) mon.write(lineText)
end end
@@ -2221,7 +2162,6 @@ function Display.render()
else else
mon.setTextColor(COLORS.WARNING) mon.setTextColor(COLORS.WARNING)
local msgY = upcomingStartY + 1 local msgY = upcomingStartY + 1
if CONFIG.DEBUG and State.debugUpcoming then msgY = upcomingStartY + 2 end
if msgY <= h then if msgY <= h then
mon.setCursorPos(1, msgY) mon.setCursorPos(1, msgY)
mon.write("NONE / WAITING DATA") mon.write("NONE / WAITING DATA")
@@ -2436,38 +2376,52 @@ local function pickEarlierTick(a, b)
return ta or tb or 0 return ta or tb or 0
end end
local function buildEtaLabel(etaSeconds, textEta)
if type(textEta) == "table" then
local value = tonumber(textEta.value)
local unit = tostring(textEta.unit or "")
if value and unit == "??" and value <= 10 then
return "~" .. tostring(value) .. "m"
end
end
return tostring(math.max(0, tonumber(etaSeconds) or 0)) .. "s"
end
local function mapTrainStatusReason(statusId)
local key = tostring(statusId or "")
if key == "" then
return nil
end
local tail = string.match(key, ":(.+)$") or key
local known = {
delay_from_previous_journey = "prev",
previous_train_delayed = "train",
priority_other_train = "prio",
red_signal = "signal",
track_closed = "track",
staff_shortage = "staff",
operational_disruption = "ops",
special_journey = "special",
out_of_service = "oos",
default_delay = "delay",
cancelled = "cancel"
}
return known[tail] or tail
end
function Main.loop() function Main.loop()
while true do while true do
local now = os.epoch("utc") local now = os.epoch("utc")
local newUpcoming = {} local newUpcoming = {}
local etaByText = {} local etaTextByKey = {}
local dbg = {
hasBR = Peripherals.blockReader ~= nil,
hasCT = Peripherals.createTarget ~= nil,
ok = false,
trainStopsCount = 0,
tickRate = State.tickRate or 20,
rawLine = nil,
textKey = nil,
textEtaRaw = nil,
textEtaValue = nil,
textEtaUnit = nil,
nbtKey = nil,
textEta = nil,
nbtEta = nil,
usedEta = nil
}
-- Prefer CRN's own ETA display text (create_target) when available. -- Read CRN text ETA only for display wording.
-- This avoids drift when TPS fluctuates, while still using NBT for delay/status. -- Timing decisions and numeric ETA still come from NBT.
if Peripherals.createTarget then if Peripherals.createTarget then
local okLines, lines = pcall(Peripherals.createTarget.dump) local okLines, lines = pcall(Peripherals.createTarget.dump)
if okLines and type(lines) == "table" then if okLines and type(lines) == "table" then
for _, lineText in pairs(lines) do for _, lineText in pairs(lines) do
if lineText and lineText ~= "" and lineText:match("[%w%?%[]") then if lineText and lineText ~= "" and lineText:match("[%w%?%[]") then
if not dbg.rawLine then
dbg.rawLine = lineText
end
local cleanLine = string.gsub(lineText, "%s*%(?%+%s*%d+.-%)?", "") local cleanLine = string.gsub(lineText, "%s*%(?%+%s*%d+.-%)?", "")
local etaValue, etaUnit, tName, dest local etaValue, etaUnit, tName, dest
etaValue, etaUnit, tName, dest = string.match(cleanLine, "^(%S+)%s+(.-)%s+(%[.-%][^%s]*)%s+(.*)$") etaValue, etaUnit, tName, dest = string.match(cleanLine, "^(%S+)%s+(.-)%s+(%[.-%][^%s]*)%s+(.*)$")
@@ -2475,21 +2429,13 @@ function Main.loop()
etaValue, etaUnit, tName, dest = string.match(cleanLine, "^([^%s]+)%s+(.-)%s+([^%s]+)%s+(.*)$") etaValue, etaUnit, tName, dest = string.match(cleanLine, "^([^%s]+)%s+(.-)%s+([^%s]+)%s+(.*)$")
end end
if tName and etaValue then if tName and etaValue then
local etaStr = tostring(etaValue or "")
if etaUnit and etaUnit ~= "" then
etaStr = etaStr .. " " .. tostring(etaUnit)
end
local etaSeconds = parseEtaText(etaStr)
if dest then if dest then
local textKey = normalizeUpcomingKey(tName, dest) local textKey = normalizeUpcomingKey(tName, dest)
etaByText[textKey] = etaSeconds etaTextByKey[textKey] = {
if not dbg.textKey then value = etaValue,
dbg.textKey = textKey unit = etaUnit,
dbg.textEtaRaw = etaStr raw = tostring(etaValue or "") .. ((etaUnit and etaUnit ~= "") and (" " .. tostring(etaUnit)) or "")
dbg.textEtaValue = etaValue }
dbg.textEtaUnit = etaUnit
dbg.textEta = etaSeconds
end
end end
end end
end end
@@ -2500,14 +2446,7 @@ function Main.loop()
-- Check Block Reader (Advanced Peripherals) first for precise ETA and Delay -- Check Block Reader (Advanced Peripherals) first for precise ETA and Delay
if Peripherals.blockReader then if Peripherals.blockReader then
local ok, data = pcall(Peripherals.blockReader.getBlockData) local ok, data = pcall(Peripherals.blockReader.getBlockData)
dbg.ok = ok and true or false
if ok and data and data.TrainStops then if ok and data and data.TrainStops then
local c = 0
for _ in ipairs(data.TrainStops) do c = c + 1 end
if c == 0 then
for _ in pairs(data.TrainStops) do c = c + 1 end
end
dbg.trainStopsCount = c
-- Real-time interpolation: estimate actual tick rate (TPS) from CRN refreshes -- Real-time interpolation: estimate actual tick rate (TPS) from CRN refreshes
if not State.lastRefreshed or State.lastRefreshed ~= data.LastRefreshed then if not State.lastRefreshed or State.lastRefreshed ~= data.LastRefreshed then
if State.lastRefreshed and State.lastRefreshedEpoch then if State.lastRefreshed and State.lastRefreshedEpoch then
@@ -2523,7 +2462,6 @@ function Main.loop()
State.lastRefreshed = data.LastRefreshed State.lastRefreshed = data.LastRefreshed
State.lastRefreshedEpoch = now State.lastRefreshedEpoch = now
end end
dbg.tickRate = State.tickRate or 20
local elapsedSeconds = (now - (State.lastRefreshedEpoch or now)) / 1000 local elapsedSeconds = (now - (State.lastRefreshedEpoch or now)) / 1000
local currentTick = (data.LastRefreshed or 0) + math.floor(elapsedSeconds * (State.tickRate or 20)) local currentTick = (data.LastRefreshed or 0) + math.floor(elapsedSeconds * (State.tickRate or 20))
@@ -2552,14 +2490,7 @@ function Main.loop()
if type(status) == "table" then if type(status) == "table" then
for _, s in ipairs(status) do for _, s in ipairs(status) do
if type(s) == "string" then if type(s) == "string" then
if string.find(s, "delay_from_previous_journey", 1, true) then reason = mapTrainStatusReason(s) or reason
reason = "prev"
elseif string.find(s, "cancel", 1, true) then
reason = "cancel"
else
local tail = string.match(s, ":(.+)$")
reason = tail or s
end
end end
end end
end end
@@ -2575,17 +2506,7 @@ function Main.loop()
local etaSeconds = math.floor(etaTicks / (State.tickRate or 20)) local etaSeconds = math.floor(etaTicks / (State.tickRate or 20))
if etaSeconds < 0 then etaSeconds = 0 end if etaSeconds < 0 then etaSeconds = 0 end
local announceKey = normalizeUpcomingKey(trainName, destination) local announceKey = normalizeUpcomingKey(trainName, destination)
if not dbg.nbtKey then local etaLabel = buildEtaLabel(etaSeconds, etaTextByKey[announceKey])
dbg.nbtKey = announceKey
dbg.nbtEta = etaSeconds
end
local textEta = etaByText[announceKey]
if type(textEta) == "number" and textEta >= 0 then
etaSeconds = textEta
end
if not dbg.usedEta then
dbg.usedEta = etaSeconds
end
-- Calculate Delay in minutes -- Calculate Delay in minutes
local delayTicks = realArrival - scheduledArrival local delayTicks = realArrival - scheduledArrival
@@ -2597,6 +2518,7 @@ function Main.loop()
dest = destination, dest = destination,
platform = platform, platform = platform,
eta = etaSeconds, eta = etaSeconds,
etaLabel = etaLabel,
delay = delayMinutes, delay = delayMinutes,
cancelled = cancelled, cancelled = cancelled,
reason = reason reason = reason
@@ -2678,6 +2600,10 @@ function Main.loop()
dest = dest, dest = dest,
platform = platform or State.stationPlatform or "1", platform = platform or State.stationPlatform or "1",
eta = etaSeconds >= 0 and etaSeconds or 0, eta = etaSeconds >= 0 and etaSeconds or 0,
etaLabel = buildEtaLabel(etaSeconds >= 0 and etaSeconds or 0, {
value = string.match(etaStr, "(%d+)"),
unit = string.match(etaStr, "%d+%s*(.*)")
}),
delay = delay, delay = delay,
cancelled = false, cancelled = false,
reason = nil reason = nil
@@ -2708,7 +2634,6 @@ function Main.loop()
-- Update Upcoming Trains globally for the monitor -- Update Upcoming Trains globally for the monitor
State.upcomingTrains = newUpcoming State.upcomingTrains = newUpcoming
State.debugUpcoming = dbg
if not Peripherals.station then if not Peripherals.station then
State.currentTrain = nil State.currentTrain = nil