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
+131 -37
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(9, "Departing...", COLORS.ERROR)
drawLeft(y, string.format("Departing in: %02d:%02d", mm, ss), COLORS.WARNING)
end
contentBottom = 9
else
contentBottom = 7
drawLeft(y, "DEPARTING...", COLORS.ERROR)
end
y = y + 1
contentBottom = y - 1
else
contentBottom = y - 1
end
if rightX then
@@ -3313,40 +3378,57 @@ function Display.render()
end
end
else
if layoutMode == "S" then
if Peripherals.station then
drawLeft(5, "NO TRAIN AT PLATFORM", COLORS.WARNING)
drawLeft(y, "NO TRAIN", COLORS.WARNING)
else
drawLeft(5, "SLAVE NODE MODE", COLORS.WARNING)
drawLeft(y, "SLAVE MODE", COLORS.WARNING)
end
contentBottom = 5
else
if Peripherals.station then
drawLeft(y, "NO TRAIN AT PLATFORM", COLORS.WARNING)
else
drawLeft(y, "SLAVE NODE MODE", COLORS.WARNING)
end
end
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,6 +3441,7 @@ function Display.render()
end
end
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
@@ -3368,6 +3451,17 @@ function Display.render()
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
function Display.renderTerminal()