diff --git a/station_broadcast.lua b/station_broadcast.lua index 069013e..11a7b49 100644 --- a/station_broadcast.lua +++ b/station_broadcast.lua @@ -2,7 +2,7 @@ -- ============================================================================ -- 1. Configuration & Constants -- ============================================================================ -local APP_VERSION_TEXT = "v1.0.41" +local APP_VERSION_TEXT = "v1.0.42" local CONFIG = { CLOUD_URL = "ws://stabadmin.fse-media.group", @@ -18,6 +18,7 @@ local CONFIG = { ALERT_CONFIRM_MS = 60000, ALERT_COOLDOWN_MS = 300000, APPROACHING_COOLDOWN_MS = 120000, + APPROACHING_STABLE_MS = 15000, DEBUG = true } @@ -2406,7 +2407,15 @@ local function buildEtaLabel(etaSeconds, textEta) return "~" .. tostring(value) .. "m" end end - return tostring(math.max(0, tonumber(etaSeconds) or 0)) .. "s" + local secs = math.max(0, tonumber(etaSeconds) or 0) + local whole = math.floor(secs + 0.0001) + if whole >= 60 then + return string.format("%02d:%02d", math.floor(whole / 60), whole % 60) + end + if secs >= 10 then + return string.format("%02ds", whole) + end + return string.format("%.1fs", secs) end local function mapTrainStatusReason(statusId) @@ -2494,6 +2503,7 @@ function Main.loop() local elapsedSeconds = (now - (State.lastRefreshedEpoch or now)) / 1000 local currentTick = (data.LastRefreshed or 0) + math.floor(elapsedSeconds * (State.tickRate or 20)) local seenAlertKeys = {} + local seenApproachKeys = {} cleanupTimestampMap(State.announcedApproaching, now, tonumber(CONFIG.APPROACHING_COOLDOWN_MS) or 120000) cleanupTimestampMap(State.announcedAlerts, now, tonumber(CONFIG.ALERT_COOLDOWN_MS) or 300000) @@ -2529,7 +2539,7 @@ function Main.loop() local scheduledArrival = pickEarlierTick(st.ScheduledArrival, st.ScheduledDeparture) local realArrival = pickEarlierTick(st.RealTimeArrival, st.RealTimeDeparture) local etaTicks = realArrival - currentTick - local etaSeconds = math.floor(etaTicks / (State.tickRate or 20)) + local etaSeconds = etaTicks / (State.tickRate or 20) if etaSeconds < 0 then etaSeconds = 0 end local announceKey = normalizeUpcomingKey(trainName, destination) local etaLabel = buildEtaLabel(etaSeconds, etaTextByKey[announceKey]) @@ -2575,9 +2585,22 @@ function Main.loop() end if etaSeconds >= 0 and etaSeconds < 30 then - if not State.announcedApproaching[announceKey] then + seenApproachKeys[announceKey] = true + local pending = State.pendingApproaching[announceKey] + if not pending then + State.pendingApproaching[announceKey] = { since = now, lastEta = etaSeconds } + else + if etaSeconds > (tonumber(pending.lastEta) or etaSeconds) + 0.5 then + pending.since = now + end + pending.lastEta = etaSeconds + end + + local stableMs = tonumber(CONFIG.APPROACHING_STABLE_MS) or 15000 + local currentPending = State.pendingApproaching[announceKey] + if currentPending and (now - (currentPending.since or now)) >= stableMs and not State.announcedApproaching[announceKey] then State.announcedApproaching[announceKey] = now - Utils.log("INFO", string.format("Train Approaching: %s (ETA: %ds, Delay: %dm)", trainName, etaSeconds, delayMinutes)) + Utils.log("INFO", string.format("Train Approaching: %s (ETA: %.1fs, Delay: %dm)", trainName, etaSeconds, delayMinutes)) Network.send({ type = "train_approaching", stationName = State.stationName, @@ -2585,13 +2608,15 @@ function Main.loop() stationPlatform = platform, trainId = trainName, destination = destination, - eta = tostring(etaSeconds) .. "s", + eta = string.format("%.1fs", etaSeconds), etaSeconds = etaSeconds, delay = displayDelayMinutes, cancelled = cancelled, reason = reason }) end + else + State.pendingApproaching[announceKey] = nil end end end @@ -2601,6 +2626,11 @@ function Main.loop() State.pendingAlerts[k] = nil end end + for k, _ in pairs(State.pendingApproaching) do + if not seenApproachKeys[k] then + State.pendingApproaching[k] = nil + end + end end end @@ -2608,6 +2638,7 @@ function Main.loop() if not Peripherals.blockReader and Peripherals.createTarget then local lines = Peripherals.createTarget.dump() local seenAlertKeys = {} + local seenApproachKeys = {} cleanupTimestampMap(State.announcedApproaching, now, tonumber(CONFIG.APPROACHING_COOLDOWN_MS) or 120000) cleanupTimestampMap(State.announcedAlerts, now, tonumber(CONFIG.ALERT_COOLDOWN_MS) or 300000) @@ -2687,7 +2718,20 @@ function Main.loop() -- 触发逻辑:当 ETA 已经明确小于30秒(包括15秒、现在等),且之前没有播报过 if etaSeconds >= 0 and etaSeconds < 30 then - if not State.announcedApproaching[announceKey] then + seenApproachKeys[announceKey] = true + local pending = State.pendingApproaching[announceKey] + if not pending then + State.pendingApproaching[announceKey] = { since = now, lastEta = etaSeconds } + else + if etaSeconds > (tonumber(pending.lastEta) or etaSeconds) + 0.5 then + pending.since = now + end + pending.lastEta = etaSeconds + end + + local stableMs = tonumber(CONFIG.APPROACHING_STABLE_MS) or 15000 + local currentPending = State.pendingApproaching[announceKey] + if currentPending and (now - (currentPending.since or now)) >= stableMs and not State.announcedApproaching[announceKey] then State.announcedApproaching[announceKey] = now Utils.log("INFO", string.format("Train Approaching: %s (ETA: %ds, Delay: %dm)", tName, etaSeconds, delay)) Network.send({ @@ -2702,6 +2746,8 @@ function Main.loop() delay = delay }) end + else + State.pendingApproaching[announceKey] = nil end end end @@ -2712,6 +2758,11 @@ function Main.loop() State.pendingAlerts[k] = nil end end + for k, _ in pairs(State.pendingApproaching) do + if not seenApproachKeys[k] then + State.pendingApproaching[k] = nil + end + end end -- Update Upcoming Trains globally for the monitor