diff --git a/station_broadcast.lua b/station_broadcast.lua index 2106d72..d0ff0c4 100644 --- a/station_broadcast.lua +++ b/station_broadcast.lua @@ -2,7 +2,7 @@ -- ============================================================================ -- 1. Configuration & Constants -- ============================================================================ -local APP_VERSION_TEXT = "v1.0.34" +local APP_VERSION_TEXT = "v1.0.35" local CONFIG = { CLOUD_URL = "ws://stabadmin.fse-media.group", @@ -51,7 +51,6 @@ local State = { currentPlaying = nil, currentPriority = -1, interruptSeq = 0, - debugUpcoming = nil, tickRate = 20 } @@ -2135,65 +2134,6 @@ function Display.render() mon.setTextColor(COLORS.DEBUG) mon.setCursorPos(1, upcomingStartY) 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 mon.setTextColor(COLORS.INFO) 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 pfx = "P" .. tostring(t.platform) .. " " 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 mon.write(lineText) end @@ -2221,7 +2162,6 @@ function Display.render() else mon.setTextColor(COLORS.WARNING) local msgY = upcomingStartY + 1 - if CONFIG.DEBUG and State.debugUpcoming then msgY = upcomingStartY + 2 end if msgY <= h then mon.setCursorPos(1, msgY) mon.write("NONE / WAITING DATA") @@ -2436,38 +2376,52 @@ local function pickEarlierTick(a, b) return ta or tb or 0 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() while true do local now = os.epoch("utc") local newUpcoming = {} - local etaByText = {} - 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 - } + local etaTextByKey = {} - -- Prefer CRN's own ETA display text (create_target) when available. - -- This avoids drift when TPS fluctuates, while still using NBT for delay/status. + -- Read CRN text ETA only for display wording. + -- Timing decisions and numeric ETA still come from NBT. if Peripherals.createTarget then local okLines, lines = pcall(Peripherals.createTarget.dump) if okLines and type(lines) == "table" then for _, lineText in pairs(lines) do 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 etaValue, etaUnit, tName, dest 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+(.*)$") end 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 local textKey = normalizeUpcomingKey(tName, dest) - etaByText[textKey] = etaSeconds - if not dbg.textKey then - dbg.textKey = textKey - dbg.textEtaRaw = etaStr - dbg.textEtaValue = etaValue - dbg.textEtaUnit = etaUnit - dbg.textEta = etaSeconds - end + etaTextByKey[textKey] = { + value = etaValue, + unit = etaUnit, + raw = tostring(etaValue or "") .. ((etaUnit and etaUnit ~= "") and (" " .. tostring(etaUnit)) or "") + } end end end @@ -2500,14 +2446,7 @@ function Main.loop() -- Check Block Reader (Advanced Peripherals) first for precise ETA and Delay if Peripherals.blockReader then local ok, data = pcall(Peripherals.blockReader.getBlockData) - dbg.ok = ok and true or false 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 if not State.lastRefreshed or State.lastRefreshed ~= data.LastRefreshed then if State.lastRefreshed and State.lastRefreshedEpoch then @@ -2523,7 +2462,6 @@ function Main.loop() State.lastRefreshed = data.LastRefreshed State.lastRefreshedEpoch = now end - dbg.tickRate = State.tickRate or 20 local elapsedSeconds = (now - (State.lastRefreshedEpoch or now)) / 1000 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 for _, s in ipairs(status) do if type(s) == "string" then - if string.find(s, "delay_from_previous_journey", 1, true) then - reason = "prev" - elseif string.find(s, "cancel", 1, true) then - reason = "cancel" - else - local tail = string.match(s, ":(.+)$") - reason = tail or s - end + reason = mapTrainStatusReason(s) or reason end end end @@ -2575,17 +2506,7 @@ function Main.loop() local etaSeconds = math.floor(etaTicks / (State.tickRate or 20)) if etaSeconds < 0 then etaSeconds = 0 end local announceKey = normalizeUpcomingKey(trainName, destination) - if not dbg.nbtKey then - 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 + local etaLabel = buildEtaLabel(etaSeconds, etaTextByKey[announceKey]) -- Calculate Delay in minutes local delayTicks = realArrival - scheduledArrival @@ -2597,6 +2518,7 @@ function Main.loop() dest = destination, platform = platform, eta = etaSeconds, + etaLabel = etaLabel, delay = delayMinutes, cancelled = cancelled, reason = reason @@ -2678,6 +2600,10 @@ function Main.loop() dest = dest, platform = platform or State.stationPlatform or "1", 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, cancelled = false, reason = nil @@ -2708,7 +2634,6 @@ function Main.loop() -- Update Upcoming Trains globally for the monitor State.upcomingTrains = newUpcoming - State.debugUpcoming = dbg if not Peripherals.station then State.currentTrain = nil