feat: use block reader for accurate ETA and delay data

This commit is contained in:
2026-08-02 15:57:14 +08:00
parent c573260e5d
commit 13dc054a01
+109 -54
View File
@@ -108,7 +108,7 @@ function Peripherals.init()
Peripherals.station = station Peripherals.station = station
State.hasStationPeripheral = station ~= nil State.hasStationPeripheral = station ~= nil
State.stationPeripheralSide = stationSide State.stationPeripheralSide = stationSide
-- Monitors -- monitors
local monitors = { peripheral.find("monitor") } local monitors = { peripheral.find("monitor") }
if #monitors > 0 then if #monitors > 0 then
Peripherals.primaryMonitor = monitors[1] Peripherals.primaryMonitor = monitors[1]
@@ -125,6 +125,8 @@ function Peripherals.init()
if Peripherals.createTarget then if Peripherals.createTarget then
pcall(Peripherals.createTarget.resize, 80, 10) pcall(Peripherals.createTarget.resize, 80, 10)
end end
-- Block Reader (Advanced Peripherals)
Peripherals.blockReader = peripheral.find("blockReader")
end end
-- ============================================================================ -- ============================================================================
@@ -2239,72 +2241,125 @@ State.pendingApproaching = {}
function Main.loop() function Main.loop()
while true do while true do
if Peripherals.createTarget then local now = os.epoch("utc")
local lines = Peripherals.createTarget.dump()
for _, lineText in pairs(lines) do -- Check Block Reader (Advanced Peripherals) first for precise ETA and Delay
if lineText and lineText ~= "" and lineText:match("[%w%?%[]") then if Peripherals.blockReader then
local etaVal, etaUnit, tName, dest local ok, data = pcall(Peripherals.blockReader.getBlockData)
etaVal, etaUnit, tName, dest = string.match(lineText, "^(%d+)%s+(.-)%s+(%[.-%][^%s]*)%s+(.*)$") if ok and data and data.TrainStops then
if not tName then for k, v in pairs(State.announcedApproaching) do
etaVal, etaUnit, tName, dest = string.match(lineText, "^([^%s]+)%s+(.-)%s+(%[.-%][^%s]*)%s+(.*)$") if now - v > 300000 then
State.announcedApproaching[k] = nil
end end
if not tName then end
etaVal, etaUnit, tName, dest = string.match(lineText, "^([^%s]+)%s+(.-)%s+([^%s]+)%s+(.*)$")
end for i, stop in ipairs(data.TrainStops) do
local st = stop.Station
if tName and etaVal then if st then
local announceKey = tName .. "_" .. dest local trainName = st.TrainName or "Unknown"
local now = os.epoch("utc") local destination = st.Destination or "Unknown"
local platform = stop.StationInfo and stop.StationInfo.Platform or State.stationPlatform or "1"
-- 45秒预告逻辑:当读取到1分钟时,延迟15秒再播报 -- Calculate ETA in seconds
if etaVal == "1" then local scheduledArrival = st.ScheduledArrival or 0
if not State.announcedApproaching[announceKey] or (now - State.announcedApproaching[announceKey] > 120000) then local realArrival = st.RealTimeArrival or 0
if not State.pendingApproaching[announceKey] then local etaTicks = realArrival - (data.LastRefreshed or 0)
State.pendingApproaching[announceKey] = { local etaSeconds = math.floor(etaTicks / 20)
triggerTime = now + 15000, -- 延迟15秒
tName = tName, -- Calculate Delay in minutes
dest = dest, local delayTicks = realArrival - scheduledArrival
etaVal = etaVal local delayMinutes = math.floor(delayTicks / (20 * 60))
} if delayMinutes < 0 then delayMinutes = 0 end
end
end if etaSeconds >= 0 and etaSeconds <= 45 then
elseif etaVal == "现在" or etaVal == "0" then local announceKey = trainName .. "_" .. destination
-- 如果列车已经进站(0或现在),并且没有被播报过,立即播报 if not State.announcedApproaching[announceKey] then
if not State.announcedApproaching[announceKey] or (now - State.announcedApproaching[announceKey] > 120000) then
State.announcedApproaching[announceKey] = now State.announcedApproaching[announceKey] = now
State.pendingApproaching[announceKey] = nil Utils.log("INFO", string.format("Train Approaching: %s (ETA: %ds, Delay: %dm)", trainName, etaSeconds, delayMinutes))
Utils.log("INFO", "Train Approaching (Now): " .. tName)
Network.send({ Network.send({
type = "train_approaching", type = "train_approaching",
stationName = State.stationName, stationName = State.stationName,
stationCode = State.stationCode, stationCode = State.stationCode,
stationPlatform = State.stationPlatform or "1", stationPlatform = platform,
trainId = tName, trainId = trainName,
destination = dest, destination = destination,
eta = etaVal eta = tostring(etaSeconds) .. "s",
etaSeconds = etaSeconds,
delay = delayMinutes
}) })
end end
end end
end end
end end
end end
-- Fallback to Display Link text parsing if Block Reader is not available
-- 检查是否有到期的延迟播报(45秒预告) elseif Peripherals.createTarget then
local now = os.epoch("utc") local lines = Peripherals.createTarget.dump()
for key, info in pairs(State.pendingApproaching) do
if now >= info.triggerTime then for k, v in pairs(State.announcedApproaching) do
State.announcedApproaching[key] = now if now - v > 300000 then
State.pendingApproaching[key] = nil State.announcedApproaching[k] = nil
Utils.log("INFO", "Train Approaching (45s mark): " .. info.tName) end
Network.send({ end
type = "train_approaching",
stationName = State.stationName, for _, lineText in pairs(lines) do
stationCode = State.stationCode, if lineText and lineText ~= "" and lineText:match("[%w%?%[]") then
stationPlatform = State.stationPlatform or "1", local delayMatch = string.match(lineText, "%+%s*(%d+)")
trainId = info.tName, local delay = tonumber(delayMatch) or 0
destination = info.dest,
eta = info.etaVal -- 从文本中移除延误相关的字眼,防止干扰后续的正则表达式匹配
}) local cleanLine = string.gsub(lineText, "%s*%(?%+%s*%d+.-%)?", "")
local platform, etaStr, tName, dest
platform, etaStr, tName, dest = string.match(cleanLine, "^(%d+)%s+(.-)%s+(%[.-%][^%s]*)%s+(.*)$")
if not tName then
platform, etaStr, tName, dest = string.match(cleanLine, "^([^%s]+)%s+(.-)%s+(%[.-%][^%s]*)%s+(.*)$")
end
if not tName then
platform, etaStr, tName, dest = string.match(cleanLine, "^([^%s]+)%s+(.-)%s+([^%s]+)%s+(.*)$")
end
if tName and etaStr then
local announceKey = tName .. "_" .. dest
-- 将 ETA 转换为秒数进行精准判断
local etaSeconds = -1
local isNow = string.find(etaStr, "现在") or string.find(etaStr, "0")
if isNow then
etaSeconds = 0
else
local num, unit = string.match(etaStr, "(%d+)%s*(.*)")
if num then
num = tonumber(num)
-- 如果单位包含秒的字眼,或者是乱码但数值>10(如 45 ??, 30 ??),则认为是秒
if string.find(unit, "秒") or string.find(unit, "s") or string.find(unit, "sec") or (unit == "??" and num > 10) then
etaSeconds = num
else
etaSeconds = num * 60
end
end
end
-- 触发逻辑:当 ETA 已经明确 <= 45秒(包括30秒、15秒、现在等),且之前没有播报过
if etaSeconds >= 0 and etaSeconds <= 45 then
if 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({
type = "train_approaching",
stationName = State.stationName,
stationCode = State.stationCode,
stationPlatform = platform or State.stationPlatform or "1",
trainId = tName,
destination = dest,
eta = etaStr,
etaSeconds = etaSeconds,
delay = delay
})
end
end
end
end end
end end
end end