feat(display): 优化外接显示器小屏自适应显示方案

- 新增 textScale 自动分级:根据显示器实际尺寸在 1.0/0.75/0.5 间自适应选择
- 实现 L/M/S 三档布局模式:L(大屏双栏完整)、M(中屏单栏精简)、S(小屏极简核心信息)
- 小屏模式隐藏日志、即将到达列车等次要信息,优先展示当前列车和发车倒计时
- 音频状态标签在小屏下进一步压缩缩写 (Sta Ann / Sp Ann)
- 版本升级至 v1.0.113
This commit is contained in:
2026-08-17 19:17:40 +08:00
parent 0335e245e1
commit 8c9c72fcba
+139 -45
View File
@@ -1,6 +1,6 @@
-- 1. Configuration & Constants
local APP_VERSION_TEXT = "v1.0.112"
local APP_VERSION_TEXT = "v1.0.113"
local CONFIG = {
CLOUD_URL = "ws://stabadmin.fse-media.group",
@@ -125,7 +125,23 @@ function Peripherals.init()
local monitors = { peripheral.find("monitor") }
if #monitors > 0 then
Peripherals.primaryMonitor = monitors[1]
Peripherals.primaryMonitor.setTextScale(0.5)
local mw, mh = Peripherals.primaryMonitor.getSize()
local scale = 0.5
if mw >= 80 and mh >= 25 then
scale = 1.0
elseif mw >= 55 and mh >= 18 then
scale = 0.75
elseif mw >= 38 and mh >= 13 then
scale = 0.5
else
scale = 0.5
pcall(function() Peripherals.primaryMonitor.setTextScale(1) end)
local testW, testH = Peripherals.primaryMonitor.getSize()
if testW >= 26 and testH >= 9 then
scale = 1
end
end
pcall(function() Peripherals.primaryMonitor.setTextScale(scale) end)
State.hasPrimaryMonitor = true
else
Peripherals.primaryMonitor = nil
@@ -3089,16 +3105,23 @@ function Display.render()
local w, h = mon.getSize()
mon.clear()
local hasSchedulePanel = State.currentTrain ~= nil
local layoutMode = "S"
if w >= 38 and h >= 16 then
layoutMode = "L"
elseif w >= 26 and h >= 10 then
layoutMode = "M"
end
local hasSchedulePanel = State.currentTrain ~= nil and layoutMode == "L"
local leftW = w
local sepX = nil
local rightX = nil
local rightW = 0
if hasSchedulePanel and w >= 30 then
if hasSchedulePanel then
rightW = math.min(math.max(math.floor(w * 0.38), 18), 40)
leftW = w - rightW - 1
if leftW < 12 then
if leftW < 14 then
leftW = w
rightW = 0
else
@@ -3107,14 +3130,14 @@ function Display.render()
end
end
Utils.log("DEBUG", string.format("Monitor size W=%d H=%d, schedulePanel=%s leftW=%d rightW=%d", w, h, tostring(rightX ~= nil), leftW, rightW))
Utils.log("DEBUG", string.format("Monitor W=%d H=%d Mode=%s Panel=%s LW=%d RW=%d", w, h, layoutMode, tostring(rightX ~= nil), leftW, rightW))
local function classifyAudioSource(source, priority)
local s = tostring(source or ""):lower()
if s == "train" or s == "station" or s == "broadcast" then return "Station Announcement" end
if s ~= "" then return "Special Announcement(" .. tostring(source) .. ")" end
if (tonumber(priority) or 0) >= 2 then return "Special Announcement" end
return "Special Announcement"
if s == "train" or s == "station" or s == "broadcast" then return "Sta Ann" end
if s ~= "" then return "Sp Ann(" .. tostring(source) .. ")" end
if (tonumber(priority) or 0) >= 2 then return "Sp Ann" end
return "Sp Ann"
end
local function truncL(str)
@@ -3151,14 +3174,30 @@ function Display.render()
end
end
drawLeft(1, "STATION: " .. State.stationName .. " " .. APP_VERSION_TEXT, COLORS.INFO)
drawLeft(2, "CODE: " .. tostring(State.stationCode) .. " P:" .. tostring(State.stationPlatform) .. " " .. tostring(State.stationIdentityMode), COLORS.INFO)
drawLeft(3, "TIME: " .. os.date("%H:%M:%S"), COLORS.INFO)
local y = 1
if layoutMode == "L" then
drawLeft(y, "STATION: " .. State.stationName .. " " .. APP_VERSION_TEXT, COLORS.INFO)
y = y + 1
drawLeft(y, "CODE: " .. tostring(State.stationCode) .. " P:" .. tostring(State.stationPlatform) .. " " .. tostring(State.stationIdentityMode), COLORS.INFO)
y = y + 1
drawLeft(y, "TIME: " .. os.date("%H:%M:%S"), COLORS.INFO)
y = y + 1
elseif layoutMode == "M" then
drawLeft(y, State.stationName .. " [" .. tostring(State.stationCode) .. "/P" .. tostring(State.stationPlatform) .. "] " .. os.date("%H:%M:%S"), COLORS.INFO)
y = y + 1
else
drawLeft(y, tostring(State.stationCode) .. "/P" .. tostring(State.stationPlatform) .. " " .. os.date("%H:%M"), COLORS.INFO)
y = y + 1
end
local audioLine = "AUDIO: FREE(Q" .. tostring(#State.audioQueue) .. ")"
local audioColor = COLORS.INFO
if State.currentPlaying then
local p = tonumber(State.currentPlaying.priority) or 0
local kind = classifyAudioSource(State.currentPlaying.source, p)
drawLeft(4, "AUDIO: " .. kind .. " Playing (P=" .. tostring(p) .. ", Q=" .. tostring(#State.audioQueue) .. ")", COLORS.SUCCESS)
audioLine = "PLAY: " .. kind .. "(P" .. tostring(p) .. " Q" .. tostring(#State.audioQueue) .. ")"
audioColor = COLORS.SUCCESS
elseif State.audioRx then
local p = tonumber(State.audioRx.priority) or 0
local kind = classifyAudioSource(State.audioRx.source, p)
@@ -3166,28 +3205,54 @@ function Display.render()
local total = tonumber(State.audioRx.expectedBytes) or 0
local pct = ""
if total > 0 then pct = " " .. tostring(math.floor((rx / total) * 100)) .. "%" end
drawLeft(4, "AUDIO: " .. kind .. " Receiving" .. pct .. " (Q=" .. tostring(#State.audioQueue) .. ")", COLORS.DEBUG)
else
drawLeft(4, "AUDIO: FREE (Q=" .. tostring(#State.audioQueue) .. ")", COLORS.INFO)
audioLine = "RX: " .. kind .. pct .. "(Q" .. tostring(#State.audioQueue) .. ")"
audioColor = COLORS.DEBUG
end
if layoutMode == "S" then
audioLine = audioLine:gsub("AUDIO: ", ""):gsub("PLAY: ", ">"):gsub("RX: ", ">>")
end
drawLeft(y, audioLine, audioColor)
y = y + 1
local contentBottom = 4
local contentBottom = y - 1
if State.currentTrain then
drawLeft(5, "CURRENT TRAIN:", COLORS.SUCCESS)
drawLeft(6, State.currentTrain.id, COLORS.INFO)
drawLeft(7, "Dest: " .. State.currentTrain.dest, COLORS.INFO)
if layoutMode == "L" then
drawLeft(y, "CURRENT TRAIN:", COLORS.SUCCESS)
y = y + 1
drawLeft(y, State.currentTrain.id, COLORS.INFO)
y = y + 1
drawLeft(y, "Dest: " .. State.currentTrain.dest, COLORS.INFO)
y = y + 1
elseif layoutMode == "M" then
drawLeft(y, "TRAIN: " .. State.currentTrain.id, COLORS.SUCCESS)
y = y + 1
drawLeft(y, ">> " .. State.currentTrain.dest, COLORS.INFO)
y = y + 1
else
drawLeft(y, State.currentTrain.id, COLORS.SUCCESS)
y = y + 1
drawLeft(y, ">" .. Utils.sanitizeDisplay(State.currentTrain.dest), COLORS.INFO)
y = y + 1
end
if State.currentTrain.departureTime then
local remaining = math.max(0, State.currentTrain.departureTime - os.epoch("utc") / 1000)
if remaining > 0 then
drawLeft(9, string.format("Departing in: %02d:%02d", math.floor(remaining/60), math.floor(remaining%60)), COLORS.WARNING)
local mm = math.floor(remaining / 60)
local ss = math.floor(remaining % 60)
if layoutMode == "S" then
drawLeft(y, string.format("DEP %02d:%02d", mm, ss), COLORS.WARNING)
else
drawLeft(y, string.format("Departing in: %02d:%02d", mm, ss), COLORS.WARNING)
end
else
drawLeft(9, "Departing...", COLORS.ERROR)
drawLeft(y, "DEPARTING...", COLORS.ERROR)
end
contentBottom = 9
y = y + 1
contentBottom = y - 1
else
contentBottom = 7
contentBottom = y - 1
end
if rightX then
@@ -3313,40 +3378,57 @@ function Display.render()
end
end
else
if Peripherals.station then
drawLeft(5, "NO TRAIN AT PLATFORM", COLORS.WARNING)
if layoutMode == "S" then
if Peripherals.station then
drawLeft(y, "NO TRAIN", COLORS.WARNING)
else
drawLeft(y, "SLAVE MODE", COLORS.WARNING)
end
else
drawLeft(5, "SLAVE NODE MODE", COLORS.WARNING)
if Peripherals.station then
drawLeft(y, "NO TRAIN AT PLATFORM", COLORS.WARNING)
else
drawLeft(y, "SLAVE NODE MODE", COLORS.WARNING)
end
end
contentBottom = 5
y = y + 1
contentBottom = y - 1
end
local logsStartY = h
local upcomingStartY = contentBottom + 1
if Peripherals.blockReader or Peripherals.createTarget then
local allowUpcoming = (Peripherals.blockReader or Peripherals.createTarget)
if allowUpcoming and layoutMode ~= "S" then
local hasUpcoming = State.upcomingTrains and #State.upcomingTrains > 0
drawLeft(upcomingStartY, "UPCOMING TRAINS:", COLORS.DEBUG)
local upcomingLabel = "UPCOMING:"
if layoutMode == "M" then upcomingLabel = "NEXT:" end
drawLeft(upcomingStartY, upcomingLabel, COLORS.DEBUG)
if hasUpcoming then
local visibleCount = math.min(3, #State.upcomingTrains)
local maxUpcoming = 3
if layoutMode == "M" then maxUpcoming = 2 end
local visibleCount = math.min(maxUpcoming, #State.upcomingTrains)
for i = 1, visibleCount do
local lineY = upcomingStartY + i
if lineY > h then break end
local t = State.upcomingTrains[i]
local suffix = ""
if t.cancelled then
suffix = " [CANCEL]"
suffix = " [X]"
elseif (tonumber(t.delay) or 0) > 0 then
local reason = t.reason and (" " .. tostring(t.reason)) or ""
suffix = " [+" .. tostring(t.delay) .. "m" .. reason .. "]"
elseif t.reason then
suffix = " [" .. tostring(t.reason) .. "]"
suffix = " [+" .. tostring(t.delay) .. "m]"
end
local pfx = ""
if t.platform and tostring(t.platform) ~= "" then
pfx = "P" .. tostring(t.platform) .. " "
end
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)
local lineText
if layoutMode == "M" then
lineText = string.format("%s%s>%s %s%s", pfx, Utils.sanitizeDisplay(t.trainName), Utils.sanitizeDisplay(t.dest), etaLabel, suffix)
else
lineText = string.format("%s%s->%s ETA:%s%s", pfx, t.trainName, t.dest, etaLabel, suffix)
end
drawLeft(lineY, lineText, COLORS.INFO)
end
logsStartY = math.max(logsStartY, upcomingStartY + visibleCount + 1)
@@ -3359,13 +3441,25 @@ function Display.render()
end
end
local maxLogLines = math.min(3, math.max(0, h - logsStartY + 1))
for i = 1, maxLogLines do
local logIdx = i + State.logScrollOffset
local entry = State.logs[logIdx]
if entry then
local lineY = logsStartY + i - 1
drawLeft(lineY, Utils.sanitizeDisplay(entry.text), COLORS[entry.level] or colors.white)
if layoutMode == "L" then
local maxLogLines = math.min(3, math.max(0, h - logsStartY + 1))
for i = 1, maxLogLines do
local logIdx = i + State.logScrollOffset
local entry = State.logs[logIdx]
if entry then
local lineY = logsStartY + i - 1
drawLeft(lineY, Utils.sanitizeDisplay(entry.text), COLORS[entry.level] or colors.white)
end
end
elseif layoutMode == "M" then
local maxLogLines = math.min(1, math.max(0, h - logsStartY + 1))
for i = 1, maxLogLines do
local logIdx = i + State.logScrollOffset
local entry = State.logs[logIdx]
if entry then
local lineY = logsStartY + i - 1
drawLeft(lineY, Utils.sanitizeDisplay(entry.text), COLORS[entry.level] or colors.white)
end
end
end
end