fix(data): 修复NBT(BlockReader/CreateTarget)与车站时刻表数据源不一致导致终点站广播漏播的问题
- 新增 looksLikeValidDestinationText: 检测目的地/车次名是否有效(过滤Unknown和高比例问号乱码) - 新增 tryCorrectUpcomingEntryWithCurrentTrain: 用权威State.currentTrain数据校正 upcoming 条目中的乱码车次/目的地 - 新增 checkIsAtStationWithCurrentTrain: 增强 isAtStation 匹配鲁棒性, 优先站台号+车次ID匹配, 乱码时降级到目的地模糊匹配 - 提前预校正: 在生成 announceKey 前就校正乱码字段, 确保 approaching/alarm 冷却去重 key 一致 - 后置全局校正: 在赋值 State.upcomingTrains 前再次对所有条目做权威校正, 保证显示和广播使用的终点站信息一致 版本升级至 v1.0.70
This commit is contained in:
+118
-88
@@ -3209,110 +3209,90 @@ function Main.loop()
|
|||||||
if type(stop.Stopovers) == "table" and #stop.Stopovers > 0 then
|
if type(stop.Stopovers) == "table" and #stop.Stopovers > 0 then
|
||||||
destination = stop.Stopovers[#stop.Stopovers] or destination
|
destination = stop.Stopovers[#stop.Stopovers] or destination
|
||||||
end
|
end
|
||||||
|
|
||||||
if isStationDestination(destination) then
|
local platform = "1"
|
||||||
local platform = "1"
|
if st.ScheduledStation and st.ScheduledStation.StationInfo and st.ScheduledStation.StationInfo.Platform then
|
||||||
if st.ScheduledStation and st.ScheduledStation.StationInfo and st.ScheduledStation.StationInfo.Platform then
|
platform = st.ScheduledStation.StationInfo.Platform
|
||||||
platform = st.ScheduledStation.StationInfo.Platform
|
else
|
||||||
else
|
platform = State.stationPlatform or "1"
|
||||||
platform = State.stationPlatform or "1"
|
|
||||||
end
|
|
||||||
local cancelled = false
|
|
||||||
local reason = nil
|
|
||||||
local tr = stop.Train
|
|
||||||
if tr then
|
|
||||||
cancelled = tr.Cancelled == 1 or tr.Cancelled == true
|
|
||||||
local status = tr.Status
|
|
||||||
if type(status) == "table" then
|
|
||||||
for _, s in ipairs(status) do
|
|
||||||
if type(s) == "string" then
|
|
||||||
reason = mapTrainStatusReason(s) or reason
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if cancelled then
|
|
||||||
reason = "cancelled"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local scheduledArrival = pickEarlierTick(st.ScheduledArrival, st.ScheduledDeparture)
|
local preCorrected = tryCorrectUpcomingEntryWithCurrentTrain({
|
||||||
local realArrival = pickEarlierTick(st.RealTimeArrival, st.RealTimeDeparture)
|
|
||||||
local etaTicks = realArrival - currentTick
|
|
||||||
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])
|
|
||||||
|
|
||||||
local delayTicks = realArrival - scheduledArrival
|
|
||||||
local delayMinutes = math.floor(delayTicks / ((State.tickRate or 20) * 60))
|
|
||||||
if delayMinutes < 0 then delayMinutes = 0 end
|
|
||||||
local displayDelayMinutes = math.max(delayMinutes, tonumber(delayTextByKey[announceKey]) or 0)
|
|
||||||
|
|
||||||
if etaSeconds <= 60 and not State.announcedApproaching[announceKey] then
|
|
||||||
Utils.log("DEBUG", string.format("Train: %s, ETA: %.1fs, Ticks: %d, Rate: %.1f", trainName, etaSeconds, etaTicks, State.tickRate or 20))
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(newUpcoming, {
|
|
||||||
trainName = trainName,
|
trainName = trainName,
|
||||||
dest = destination,
|
dest = destination,
|
||||||
platform = platform,
|
platform = platform
|
||||||
eta = etaSeconds,
|
|
||||||
etaLabel = etaLabel,
|
|
||||||
delay = displayDelayMinutes,
|
|
||||||
cancelled = cancelled,
|
|
||||||
reason = reason
|
|
||||||
})
|
})
|
||||||
|
if preCorrected then
|
||||||
local alertType = cancelled and "cancelled" or ((displayDelayMinutes > 0) and "delayed" or nil)
|
if not looksLikeValidDestinationText(trainName) and preCorrected.trainName then
|
||||||
local alertKey = alertType and (announceKey .. "::" .. alertType) or nil
|
trainName = preCorrected.trainName
|
||||||
if alertKey then
|
end
|
||||||
seenAlertKeys[alertKey] = true
|
if not looksLikeValidDestinationText(destination) and preCorrected.dest then
|
||||||
if isPendingAlertReady(alertKey, now) and not State.announcedAlerts[alertKey] then
|
destination = preCorrected.dest
|
||||||
State.announcedAlerts[alertKey] = now
|
|
||||||
Utils.log("INFO", string.format("Train Alert: %s (ETA: %ds, Delay: %dm, Cancelled: %s, Reason: %s)", trainName, etaSeconds, displayDelayMinutes, tostring(cancelled), tostring(reason or "")))
|
|
||||||
Network.send({
|
|
||||||
type = "train_alert",
|
|
||||||
stationName = State.stationName,
|
|
||||||
stationCode = State.stationCode,
|
|
||||||
stationPlatform = platform,
|
|
||||||
trainId = trainName,
|
|
||||||
destination = destination,
|
|
||||||
eta = tostring(etaSeconds) .. "s",
|
|
||||||
etaSeconds = etaSeconds,
|
|
||||||
delay = displayDelayMinutes,
|
|
||||||
cancelled = cancelled,
|
|
||||||
reason = reason
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local isAtStation = checkIsAtStationWithCurrentTrain(trainName, destination, platform)
|
if isStationDestination(destination) then
|
||||||
if not isAtStation and etaSeconds > 0 and etaSeconds <= 45 then
|
local cancelled = false
|
||||||
if not seenApproachKeys[announceKey] then
|
local reason = nil
|
||||||
seenApproachKeys[announceKey] = true
|
local tr = stop.Train
|
||||||
local pending = State.pendingApproaching[announceKey]
|
if tr then
|
||||||
if not pending then
|
cancelled = tr.Cancelled == 1 or tr.Cancelled == true
|
||||||
State.pendingApproaching[announceKey] = { since = now, lastEta = etaSeconds }
|
local status = tr.Status
|
||||||
else
|
if type(status) == "table" then
|
||||||
if etaSeconds > (tonumber(pending.lastEta) or etaSeconds) + 15 then
|
for _, s in ipairs(status) do
|
||||||
pending.since = now
|
if type(s) == "string" then
|
||||||
|
reason = mapTrainStatusReason(s) or reason
|
||||||
|
end
|
||||||
end
|
end
|
||||||
pending.lastEta = etaSeconds
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
if cancelled then
|
||||||
|
reason = "cancelled"
|
||||||
|
end
|
||||||
|
|
||||||
local stableMs = tonumber(CONFIG.APPROACHING_STABLE_MS) or 15000
|
local scheduledArrival = pickEarlierTick(st.ScheduledArrival, st.ScheduledDeparture)
|
||||||
local currentPending = State.pendingApproaching[announceKey]
|
local realArrival = pickEarlierTick(st.RealTimeArrival, st.RealTimeDeparture)
|
||||||
if currentPending and (now - (currentPending.since or now)) >= stableMs and not State.announcedApproaching[announceKey] then
|
local etaTicks = realArrival - currentTick
|
||||||
State.announcedApproaching[announceKey] = now
|
local etaSeconds = etaTicks / (State.tickRate or 20)
|
||||||
Utils.log("INFO", string.format("Train Approaching: %s (ETA: %.1fs, Delay: %dm)", trainName, etaSeconds, delayMinutes))
|
if etaSeconds < 0 then etaSeconds = 0 end
|
||||||
|
local announceKey = normalizeUpcomingKey(trainName, destination)
|
||||||
|
local etaLabel = buildEtaLabel(etaSeconds, etaTextByKey[announceKey])
|
||||||
|
|
||||||
|
local delayTicks = realArrival - scheduledArrival
|
||||||
|
local delayMinutes = math.floor(delayTicks / ((State.tickRate or 20) * 60))
|
||||||
|
if delayMinutes < 0 then delayMinutes = 0 end
|
||||||
|
local displayDelayMinutes = math.max(delayMinutes, tonumber(delayTextByKey[announceKey]) or 0)
|
||||||
|
|
||||||
|
if etaSeconds <= 60 and not State.announcedApproaching[announceKey] then
|
||||||
|
Utils.log("DEBUG", string.format("Train: %s, ETA: %.1fs, Ticks: %d, Rate: %.1f", trainName, etaSeconds, etaTicks, State.tickRate or 20))
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(newUpcoming, {
|
||||||
|
trainName = trainName,
|
||||||
|
dest = destination,
|
||||||
|
platform = platform,
|
||||||
|
eta = etaSeconds,
|
||||||
|
etaLabel = etaLabel,
|
||||||
|
delay = displayDelayMinutes,
|
||||||
|
cancelled = cancelled,
|
||||||
|
reason = reason
|
||||||
|
})
|
||||||
|
|
||||||
|
local alertType = cancelled and "cancelled" or ((displayDelayMinutes > 0) and "delayed" or nil)
|
||||||
|
local alertKey = alertType and (announceKey .. "::" .. alertType) or nil
|
||||||
|
if alertKey then
|
||||||
|
seenAlertKeys[alertKey] = true
|
||||||
|
if isPendingAlertReady(alertKey, now) and not State.announcedAlerts[alertKey] then
|
||||||
|
State.announcedAlerts[alertKey] = now
|
||||||
|
Utils.log("INFO", string.format("Train Alert: %s (ETA: %ds, Delay: %dm, Cancelled: %s, Reason: %s)", trainName, etaSeconds, displayDelayMinutes, tostring(cancelled), tostring(reason or "")))
|
||||||
Network.send({
|
Network.send({
|
||||||
type = "train_approaching",
|
type = "train_alert",
|
||||||
stationName = State.stationName,
|
stationName = State.stationName,
|
||||||
stationCode = State.stationCode,
|
stationCode = State.stationCode,
|
||||||
stationPlatform = platform,
|
stationPlatform = platform,
|
||||||
trainId = trainName,
|
trainId = trainName,
|
||||||
destination = destination,
|
destination = destination,
|
||||||
eta = string.format("%.1fs", etaSeconds),
|
eta = tostring(etaSeconds) .. "s",
|
||||||
etaSeconds = etaSeconds,
|
etaSeconds = etaSeconds,
|
||||||
delay = displayDelayMinutes,
|
delay = displayDelayMinutes,
|
||||||
cancelled = cancelled,
|
cancelled = cancelled,
|
||||||
@@ -3320,7 +3300,42 @@ function Main.loop()
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
local isAtStation = checkIsAtStationWithCurrentTrain(trainName, destination, platform)
|
||||||
|
if not isAtStation and etaSeconds > 0 and etaSeconds <= 45 then
|
||||||
|
if not seenApproachKeys[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) + 15 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: %.1fs, Delay: %dm)", trainName, etaSeconds, delayMinutes))
|
||||||
|
Network.send({
|
||||||
|
type = "train_approaching",
|
||||||
|
stationName = State.stationName,
|
||||||
|
stationCode = State.stationCode,
|
||||||
|
stationPlatform = platform,
|
||||||
|
trainId = trainName,
|
||||||
|
destination = destination,
|
||||||
|
eta = string.format("%.1fs", etaSeconds),
|
||||||
|
etaSeconds = etaSeconds,
|
||||||
|
delay = displayDelayMinutes,
|
||||||
|
cancelled = cancelled,
|
||||||
|
reason = reason
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -3361,6 +3376,21 @@ function Main.loop()
|
|||||||
if not tName then
|
if not tName then
|
||||||
platform, etaStr, tName, dest = string.match(cleanLine, "^([^%s]+)%s+(.-)%s+([^%s]+)%s+(.*)$")
|
platform, etaStr, tName, dest = string.match(cleanLine, "^([^%s]+)%s+(.-)%s+([^%s]+)%s+(.*)$")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local entryPlatform = platform or State.stationPlatform or "1"
|
||||||
|
local preCorrectedCT = tryCorrectUpcomingEntryWithCurrentTrain({
|
||||||
|
trainName = tName or "",
|
||||||
|
dest = dest or "",
|
||||||
|
platform = entryPlatform
|
||||||
|
})
|
||||||
|
if preCorrectedCT then
|
||||||
|
if tName and not looksLikeValidDestinationText(tName) and preCorrectedCT.trainName then
|
||||||
|
tName = preCorrectedCT.trainName
|
||||||
|
end
|
||||||
|
if dest and not looksLikeValidDestinationText(dest) and preCorrectedCT.dest then
|
||||||
|
dest = preCorrectedCT.dest
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if tName and etaStr and isStationDestination(dest) then
|
if tName and etaStr and isStationDestination(dest) then
|
||||||
local announceKey = normalizeUpcomingKey(tName, dest)
|
local announceKey = normalizeUpcomingKey(tName, dest)
|
||||||
|
|||||||
Reference in New Issue
Block a user