fix: 修复检票机首次加载黑屏及完善亮灯模块对接
- 增加外设初始化重试机制,等待monitor和检票机外设就绪(最多20秒), 在等待期间显示进度提示,避免区块加载时脚本提前退出导致屏幕无显示 - 修复Socket.IO服务端未强制websocket传输配置,避免负载均衡环境下轮询产生400错误 - 所有业务拒绝(deny)场景下调用markFault触发红灯,包括: * 无效票/卡、卡ID缺失、票ID缺失 * 站点不匹配、未进站/已出站、已进站/已出站 * 余额不足、票价查询失败、网络错误 * 外设写入/扣费失败等所有错误路径 - 版本号升级至v1.5.29
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user