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:
+131
-37
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user