fix: 修复检票机首次加载黑屏及完善亮灯模块对接

- 增加外设初始化重试机制,等待monitor和检票机外设就绪(最多20秒),
  在等待期间显示进度提示,避免区块加载时脚本提前退出导致屏幕无显示
- 修复Socket.IO服务端未强制websocket传输配置,避免负载均衡环境下轮询产生400错误
- 所有业务拒绝(deny)场景下调用markFault触发红灯,包括:
  * 无效票/卡、卡ID缺失、票ID缺失
  * 站点不匹配、未进站/已出站、已进站/已出站
  * 余额不足、票价查询失败、网络错误
  * 外设写入/扣费失败等所有错误路径
- 版本号升级至v1.5.29
This commit is contained in:
2026-08-11 14:29:03 +08:00
parent ca77d762c9
commit e76d0188db
3 changed files with 479 additions and 24 deletions
+181 -23
View File
@@ -1,8 +1,10 @@
local DEFAULT_SERVER_BASE = "https://ticket.fse-media.group"
local DEFAULT_SERVER_PATH = "/api/tickets/check"
local GATE_OPEN_SECONDS = 2
local VERSION = "v1.5.28"
local VERSION = "v1.5.29"
local VERSION_CHECK_INTERVAL = 5
local PERIPHERAL_WAIT_INTERVAL = 0.5
local PERIPHERAL_WAIT_MAX_RETRIES = 40
local serverURL = nil
@@ -87,11 +89,93 @@ local function stationSetFromList(list)
end
local nativeTerm = (term and type(term.current) == "function" and term.current()) or term
local monitor = peripheral.find("monitor")
local speaker = peripheral.find("speaker")
local inspection = peripheral.find("ticket_inspection_machine")
local monitor = nil
local speaker = nil
local inspection = nil
local monitorSide = nil
local function safeFindPeripheral(peripheralType, sideHint)
if not peripheral or type(peripheral.find) ~= "function" then return nil, nil end
if sideHint and type(peripheral.wrap) == "function" and type(peripheral.isPresent) == "function" then
local okPres, pres = pcall(peripheral.isPresent, sideHint)
if okPres and pres then
local okW, wrapped = pcall(peripheral.wrap, sideHint)
if okW and wrapped then return wrapped, sideHint end
end
end
local foundName = nil
local found = nil
pcall(function()
found = peripheral.find(peripheralType, function(name, wrapped)
foundName = name
return true
end)
end)
if found then return found, foundName end
return nil, nil
end
local function waitForPeripherals(requireInspection)
local attempts = 0
local lastDrawMsg = nil
while attempts < PERIPHERAL_WAIT_MAX_RETRIES do
local hadChange = false
if not monitor then
local m, ms = safeFindPeripheral("monitor", monitorSide)
if m then monitor = m; monitorSide = ms; hadChange = true end
end
if not speaker then
local s, _ = safeFindPeripheral("speaker")
if s then speaker = s; hadChange = true end
end
if not inspection and requireInspection then
local i, _ = safeFindPeripheral("ticket_inspection_machine")
if i then inspection = i; hadChange = true end
end
local ok = true
if not monitor then ok = false end
if requireInspection and not inspection then ok = false end
if ok then return true end
local msg = nil
if not monitor and requireInspection and not inspection then
msg = "Waiting for monitor + gate..."
elseif not monitor then
msg = "Waiting for monitor..."
elseif requireInspection and not inspection then
msg = "Waiting for gate peripheral..."
end
if msg and msg ~= lastDrawMsg then
lastDrawMsg = msg
pcall(function()
local tmpTerm = nativeTerm or term
if tmpTerm and type(tmpTerm.getSize) == "function" then
tmpTerm.setBackgroundColor(colors.black)
tmpTerm.setTextColor(colors.yellow)
tmpTerm.clear()
tmpTerm.setCursorPos(1, 1)
local w, _ = tmpTerm.getSize()
local pad = string.rep(" ", math.max(0, math.floor((w - #VERSION) / 2)))
tmpTerm.write(pad .. VERSION)
local msg2 = tostring(msg)
tmpTerm.setCursorPos(1, 3)
local pad2 = string.rep(" ", math.max(0, math.floor((w - #msg2) / 2)))
tmpTerm.write(pad2 .. msg2)
tmpTerm.setCursorPos(1, 5)
tmpTerm.setTextColor(colors.gray)
local prog = "Attempt " .. tostring(attempts + 1) .. "/" .. tostring(PERIPHERAL_WAIT_MAX_RETRIES)
local pad3 = string.rep(" ", math.max(0, math.floor((w - #prog) / 2)))
tmpTerm.write(pad3 .. prog)
end
end)
end
sleep(PERIPHERAL_WAIT_INTERVAL)
attempts = attempts + 1
end
return false
end
pcall(function() waitForPeripherals(false) end)
local serverConnected = nil
local isUpdating = false
@@ -832,8 +916,32 @@ pcall(function()
refreshRemoteLuaVersion(guessBaseFromStatusURL(serverURL))
end)
if not inspection then
pcall(function()
if modeBySide == nil then
waitForPeripherals(true)
else
for side, _ in pairs(modeBySide) do
local dev = resolveInspection(side)
local retryCount = 0
while not dev and retryCount < PERIPHERAL_WAIT_MAX_RETRIES do
sleep(PERIPHERAL_WAIT_INTERVAL)
dev = resolveInspection(side)
retryCount = retryCount + 1
end
end
if not monitor then waitForPeripherals(false) end
end
end)
if not inspection and modeBySide == nil then
local okDev = false
if peripheral and type(peripheral.find) == "function" then
pcall(function()
inspection = peripheral.find("ticket_inspection_machine")
if inspection then okDev = true end
end)
end
if not okDev then
draw("Missing peripheral:", "ticket_inspection_machine", colors.red)
error("ticket_inspection_machine not found")
end
@@ -1192,9 +1300,35 @@ local function queryFare(fromStation, toStation)
return toMoney(fare), nil
end
local function denyCard(reason, detail)
local function triggerFaultLights(devs)
local targets = {}
if type(devs) == "table" then
for _, d in ipairs(devs) do table.insert(targets, d) end
end
if inspection and not targets[1] then table.insert(targets, inspection) end
if modeBySide then
for side, _ in pairs(modeBySide) do
local d = resolveInspection(side)
if d then
local found = false
for _, t in ipairs(targets) do if t == d then found = true; break end end
if not found then table.insert(targets, d) end
end
end
end
for _, dev in ipairs(targets) do
if type(dev) == "table" and type(dev.markFault) == "function" then
pcall(dev.markFault)
end
end
end
local function denyCard(reason, detail, inspectionDevs)
draw("DENIED", tostring(detail or reason or "deny"), colors.red)
playDfpwm("error.dfpwm")
parallel.waitForAll(
function() playDfpwm("error.dfpwm") end,
function() triggerFaultLights(inspectionDevs) end
)
end
local function deductICCardBalance(dev, amount)
@@ -1240,7 +1374,10 @@ local function handleICCardScan(scan, side, scanDev)
local cardId = getCardId(scan)
if #cardId == 0 then
draw("Invalid card.", "Missing card_id.", colors.red)
playDfpwm("error.dfpwm")
parallel.waitForAll(
function() playDfpwm("error.dfpwm") end,
function() triggerFaultLights(inspectionDevs) end
)
return
end
@@ -1251,13 +1388,13 @@ local function handleICCardScan(scan, side, scanDev)
local usedAction = action
if #exitStation == 0 then
denyCard("missing_station", "Missing gate station")
denyCard("missing_station", "Missing gate station", inspectionDevs)
return
end
if usedAction == "entry" then
if getCardEntered(scan) and not getCardExited(scan) then
denyCard("already_entered", "Already entered")
denyCard("already_entered", "Already entered", inspectionDevs)
return
end
local okWrite = true
@@ -1273,36 +1410,42 @@ local function handleICCardScan(scan, side, scanDev)
end
if not okWrite then
draw("WRITE ERROR", tostring(writeErr or "Failed to update card"), colors.red)
playDfpwm("error.dfpwm")
parallel.waitForAll(
function() playDfpwm("error.dfpwm") end,
function() triggerFaultLights(inspectionDevs) end
)
return
end
entryStation = exitStation
else
if not getCardEntered(scan) then
denyCard("not_entered", "Not entered")
denyCard("not_entered", "Not entered", inspectionDevs)
return
end
if getCardExited(scan) then
denyCard("already_exited", "Already exited")
denyCard("already_exited", "Already exited", inspectionDevs)
return
end
if #entryStation == 0 then
denyCard("missing_entry_station", "Missing entry station")
denyCard("missing_entry_station", "Missing entry station", inspectionDevs)
return
end
local fareValue, fareErr = queryFare(entryStation, exitStation)
if fareValue == nil then
if fareErr == "net_error" then
draw("NET ERROR", "Fare lookup failed", colors.red)
playDfpwm("error.dfpwm")
parallel.waitForAll(
function() playDfpwm("error.dfpwm") end,
function() triggerFaultLights(inspectionDevs) end
)
else
denyCard("fare_not_found", fareErr)
denyCard("fare_not_found", fareErr, inspectionDevs)
end
return
end
fare = fareValue
if balance < fare then
denyCard("insufficient_balance", "Fare: " .. tostring(fare) .. " Bal: " .. tostring(balance))
denyCard("insufficient_balance", "Fare: " .. tostring(fare) .. " Bal: " .. tostring(balance), inspectionDevs)
return
end
local okWrite = true
@@ -1325,10 +1468,13 @@ local function handleICCardScan(scan, side, scanDev)
else
okWrite = false
if tostring(newBalanceOrErr) == "insufficient" then
denyCard("insufficient_balance", "-" .. tostring(fare) .. " Left: " .. tostring(balance))
denyCard("insufficient_balance", "-" .. tostring(fare) .. " Left: " .. tostring(balance), inspectionDevs)
else
draw("WRITE ERROR", tostring(newBalanceOrErr or "deduct_failed"), colors.red)
playDfpwm("error.dfpwm")
parallel.waitForAll(
function() playDfpwm("error.dfpwm") end,
function() triggerFaultLights(inspectionDevs) end
)
end
break
end
@@ -1336,7 +1482,10 @@ local function handleICCardScan(scan, side, scanDev)
if not okWrite then
if writeErr then
draw("WRITE ERROR", tostring(writeErr), colors.red)
playDfpwm("error.dfpwm")
parallel.waitForAll(
function() playDfpwm("error.dfpwm") end,
function() triggerFaultLights(inspectionDevs) end
)
end
return
end
@@ -1402,7 +1551,10 @@ local function handleScan(scan, side, scanDev)
local ticketId = getTicketId(scan)
if not ticketId then
draw("Invalid ticket.", "Missing ticketId.", colors.red)
playDfpwm("error.dfpwm")
parallel.waitForAll(
function() playDfpwm("error.dfpwm") end,
function() triggerFaultLights(inspectionDevs) end
)
return
end
@@ -1440,13 +1592,19 @@ local function handleScan(scan, side, scanDev)
if not ok or type(resp) ~= "table" then
draw("NET ERROR", "Server check failed.", colors.red)
playDfpwm("error.dfpwm")
parallel.waitForAll(
function() playDfpwm("error.dfpwm") end,
function() triggerFaultLights(inspectionDevs) end
)
return
end
if resp.result ~= "pass" then
draw("DENIED", tostring(resp.reason or "deny"), colors.red)
playDfpwm("error.dfpwm")
parallel.waitForAll(
function() playDfpwm("error.dfpwm") end,
function() triggerFaultLights(inspectionDevs) end
)
return
end