feat(web,backend,lua,installer): 新增Lua脚本版本管理功能及相关优化
- 升级售票机、检票机内置Lua脚本版本至v1.5.8 - 新增后端配置的lua_versions字段,统一管理售票机、检票机的Lua脚本版本 - 前端新增版本管理配置页面,支持版本号配置和一键补丁升级 - 为售票机、检票机添加远程版本检测功能,屏幕显示版本匹配状态标记 - 简化installer配置交互流程,优化站点代码输入方式 - 重构后端配置规范化处理逻辑,统一配置初始化与存储流程 - 优化售票机外设检测、支付检测逻辑,修复部分已知问题
This commit is contained in:
+286
-56
@@ -1,6 +1,6 @@
|
||||
local CURRENT_STATION_CODE = 'Ticket-Machine'
|
||||
local API_BASE = 'http://ticket.fse-media.group/api'
|
||||
local VERSION = 'v1.5.7'
|
||||
local VERSION = 'v1.5.8'
|
||||
|
||||
-- ###########################
|
||||
-- Core HTTP & JSON Utilities
|
||||
@@ -13,6 +13,17 @@ end
|
||||
|
||||
local serverConnected = nil
|
||||
local serverLastChangeTs = 0
|
||||
local expectedMachineVersion = nil
|
||||
local versionMismatch = nil
|
||||
|
||||
local function normalizeVersionTag(v)
|
||||
local s = tostring(v or ''):gsub('^%s+', ''):gsub('%s+$', '')
|
||||
if #s == 0 then return '' end
|
||||
if s:sub(1, 1):lower() ~= 'v' then
|
||||
s = 'v' .. s
|
||||
end
|
||||
return s:lower()
|
||||
end
|
||||
|
||||
local function setServerConnected(ok)
|
||||
if serverConnected == ok then return end
|
||||
@@ -314,22 +325,93 @@ end
|
||||
-- ###########################
|
||||
-- Peripheral discovery
|
||||
-- ###########################
|
||||
local monitor = peripheral.find('monitor')
|
||||
local ticketVendingMachine = peripheral.find('ticket_vending_machine')
|
||||
local speaker = peripheral.find('speaker')
|
||||
local SIDE_PRIORITY = { top = 1, bottom = 2, left = 3, right = 4, front = 5, back = 6 }
|
||||
local REDSTONE_SIDES = { 'right', 'left', 'top', 'bottom', 'front', 'back' }
|
||||
local monitor = nil
|
||||
local monitorName = nil
|
||||
local ticketVendingMachine = nil
|
||||
local ticketVendingMachineName = nil
|
||||
local speaker = nil
|
||||
local speakerName = nil
|
||||
local detectedPaymentSide = nil
|
||||
local MOD_DEBUG = true
|
||||
|
||||
pcall(math.randomseed, (os.epoch and os.epoch('utc')) or os.time())
|
||||
|
||||
local function safe(term)
|
||||
if monitor then return peripheral.wrap(peripheral.getName(monitor)) end
|
||||
return term
|
||||
local function comparePeripheralName(a, b)
|
||||
local pa = SIDE_PRIORITY[tostring(a or '')] or 99
|
||||
local pb = SIDE_PRIORITY[tostring(b or '')] or 99
|
||||
if pa ~= pb then return pa < pb end
|
||||
return tostring(a or '') < tostring(b or '')
|
||||
end
|
||||
|
||||
local termDev = safe(term)
|
||||
if monitor then pcall(monitor.setTextScale, 0.5) end
|
||||
local function peripheralTypeMatches(name, typeName)
|
||||
if not peripheral or type(peripheral.getType) ~= 'function' then return false end
|
||||
local got = peripheral.getType(name)
|
||||
if type(got) == 'string' then return got == typeName end
|
||||
if type(got) == 'table' then
|
||||
for _, item in ipairs(got) do
|
||||
if item == typeName then return true end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function findPeripheralByType(typeName)
|
||||
if not peripheral then return nil, nil end
|
||||
if type(peripheral.getNames) == 'function' and type(peripheral.wrap) == 'function' then
|
||||
local names = peripheral.getNames() or {}
|
||||
table.sort(names, comparePeripheralName)
|
||||
for _, name in ipairs(names) do
|
||||
if peripheralTypeMatches(name, typeName) then
|
||||
local okWrap, dev = pcall(peripheral.wrap, name)
|
||||
if okWrap and type(dev) == 'table' then
|
||||
return dev, name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if type(peripheral.find) == 'function' then
|
||||
local dev = peripheral.find(typeName)
|
||||
if type(dev) == 'table' then
|
||||
local name = nil
|
||||
if type(peripheral.getName) == 'function' then
|
||||
local okName, gotName = pcall(peripheral.getName, dev)
|
||||
if okName then name = gotName end
|
||||
end
|
||||
return dev, name
|
||||
end
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local termDev = term
|
||||
local w, h = termDev.getSize()
|
||||
|
||||
local function refreshDevices()
|
||||
local prevSignature = table.concat({
|
||||
tostring(monitorName or ''),
|
||||
tostring(ticketVendingMachineName or ''),
|
||||
tostring(speakerName or '')
|
||||
}, '|')
|
||||
monitor, monitorName = findPeripheralByType('monitor')
|
||||
ticketVendingMachine, ticketVendingMachineName = findPeripheralByType('ticket_vending_machine')
|
||||
speaker, speakerName = findPeripheralByType('speaker')
|
||||
termDev = monitor or term
|
||||
if monitor then pcall(monitor.setTextScale, 0.5) end
|
||||
w, h = termDev.getSize()
|
||||
local nextSignature = table.concat({
|
||||
tostring(monitorName or ''),
|
||||
tostring(ticketVendingMachineName or ''),
|
||||
tostring(speakerName or '')
|
||||
}, '|')
|
||||
if prevSignature ~= nextSignature then
|
||||
os.queueEvent('config_updated')
|
||||
end
|
||||
end
|
||||
|
||||
refreshDevices()
|
||||
|
||||
local function saveCardIssueSnapshot(cardData)
|
||||
pcall(function()
|
||||
ensureDir('logs/last_card_issue.json')
|
||||
@@ -356,6 +438,14 @@ local function peripheralCallSucceeded(r1)
|
||||
return r1 ~= nil and r1 ~= false
|
||||
end
|
||||
|
||||
local function getTicketVendingMachine()
|
||||
refreshDevices()
|
||||
if type(ticketVendingMachine) == 'table' then
|
||||
return ticketVendingMachine
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function callPeripheralMethods(dev, methodNames, variants)
|
||||
if type(dev) ~= 'table' then return false, 'peripheral_unavailable' end
|
||||
for _, methodName in ipairs(methodNames) do
|
||||
@@ -373,7 +463,7 @@ local function callPeripheralMethods(dev, methodNames, variants)
|
||||
end
|
||||
|
||||
local function issueBlankICCard(holderName, initialBalance)
|
||||
local dev = ticketVendingMachine
|
||||
local dev = getTicketVendingMachine()
|
||||
if type(dev) ~= 'table' then return false, '', 'peripheral_unavailable' end
|
||||
local safeHolderName = firstString(holderName, 'CARD USER')
|
||||
local safeInitialBalance = math.max(0, math.floor(tonumber(initialBalance) or 0))
|
||||
@@ -395,8 +485,9 @@ local function issueBlankICCard(holderName, initialBalance)
|
||||
return false, '', 'unsupported_method'
|
||||
end
|
||||
|
||||
local function writeICCard(cardData)
|
||||
local dev = ticketVendingMachine
|
||||
local function writeICCard(cardData, opts)
|
||||
local dev = getTicketVendingMachine()
|
||||
local options = opts or {}
|
||||
local payload = {}
|
||||
for k, v in pairs(cardData or {}) do payload[k] = v end
|
||||
payload.media = payload.media or 'ic_card'
|
||||
@@ -409,7 +500,9 @@ local function writeICCard(cardData)
|
||||
saveCardIssueSnapshot(payload)
|
||||
|
||||
local okWrite, methodName, r1, r2, r3 = callPeripheralMethods(dev,
|
||||
{ 'issueCard', 'writeCard', 'writeICCard', 'issueTicketData', 'writeTicketData', 'issueICCard' },
|
||||
options.writeOnly
|
||||
and { 'writeCard', 'writeICCard', 'writeTicketData', 'issueTicketData' }
|
||||
or { 'issueCard', 'writeCard', 'writeICCard', 'issueTicketData', 'writeTicketData', 'issueICCard' },
|
||||
{
|
||||
{ payload },
|
||||
{ tostring(payload.holder_name or ''), tonumber(payload.initial_balance) or 0 },
|
||||
@@ -430,6 +523,75 @@ local function submitCardOpen(payload)
|
||||
return postJSON(API_BASE .. '/cards/open', payload)
|
||||
end
|
||||
|
||||
local function buildFinalCardData(payload, respData)
|
||||
local data = (type(respData) == 'table') and respData or {}
|
||||
return {
|
||||
card_id = firstString(data.card_id, data.id, payload.card_id),
|
||||
holder_name = payload.holder_name,
|
||||
balance = firstNumber(data.balance, data.stored_value, payload.balance) or payload.balance,
|
||||
deposit = firstNumber(data.deposit, payload.deposit) or payload.deposit,
|
||||
topup = firstNumber(data.topup, data.first_topup, payload.topup) or payload.topup,
|
||||
station_code = payload.station_code,
|
||||
device = payload.device,
|
||||
voucher_code = payload.voucher_code,
|
||||
media = 'ic_card',
|
||||
product_type = 'stored_value',
|
||||
order_value = payload.order_value,
|
||||
initial_balance = firstNumber(data.first_topup, data.topup, payload.topup, payload.balance) or payload.topup or payload.balance or 0
|
||||
}
|
||||
end
|
||||
|
||||
local function generateCardId()
|
||||
local num = string.format('%06d', math.random(0, 999999))
|
||||
return 'IC-' .. num
|
||||
end
|
||||
|
||||
local function issueTicketFromPeripheral(fromNameEnArg, toNameEnArg, apiType, rides, cost, startStationArg, terminalStationArg, fromNameCnUArg, toNameCnUArg, fallbackTicketId)
|
||||
local dev = getTicketVendingMachine()
|
||||
if type(dev) ~= 'table' then
|
||||
return false, '', 'peripheral_unavailable'
|
||||
end
|
||||
local fn = dev.issueTicket
|
||||
if type(fn) ~= 'function' then
|
||||
return false, '', 'unsupported_method'
|
||||
end
|
||||
|
||||
local function normalizeIssuedTicketId(id)
|
||||
if id == nil then return '' end
|
||||
local s = tostring(id):gsub('%s+', '')
|
||||
if #s == 0 then return '' end
|
||||
local prefix, num = s:match('^([A-Za-z][A-Za-z])%-?([0-9]+)$')
|
||||
if prefix and num then
|
||||
prefix = prefix:upper()
|
||||
if #num < 8 then
|
||||
num = string.rep('0', 8 - #num) .. num
|
||||
end
|
||||
return prefix .. '-' .. num
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function tryIssue(...)
|
||||
local okCall, r1, r2, r3 = pcall(fn, ...)
|
||||
if not (okCall and peripheralCallSucceeded(r1)) then
|
||||
return false, '', okCall and 'issue_failed' or 'issue_call_failed'
|
||||
end
|
||||
local issuedId = extractPeripheralId(r2, r3, r1, fallbackTicketId)
|
||||
local normalizedId = normalizeIssuedTicketId(issuedId)
|
||||
if #normalizedId == 0 then
|
||||
return false, '', 'invalid_ticket_id'
|
||||
end
|
||||
return true, normalizedId, 'issueTicket'
|
||||
end
|
||||
|
||||
local okIssue, ticketId, issueErr = tryIssue(fromNameEnArg, toNameEnArg, apiType, rides, cost, startStationArg, terminalStationArg, fromNameCnUArg, toNameCnUArg)
|
||||
if okIssue then
|
||||
return true, ticketId, 'issueTicket'
|
||||
end
|
||||
|
||||
return tryIssue(fromNameEnArg, toNameEnArg, apiType, rides, cost, startStationArg, terminalStationArg)
|
||||
end
|
||||
|
||||
-- ###########################
|
||||
-- Audio Utilities & Playback
|
||||
-- ###########################
|
||||
@@ -489,6 +651,15 @@ local function backgroundSyncTask()
|
||||
end
|
||||
end
|
||||
|
||||
local function backgroundPeripheralTask()
|
||||
while true do
|
||||
local ev = os.pullEvent()
|
||||
if ev == 'peripheral' or ev == 'peripheral_detach' then
|
||||
pcall(refreshDevices)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function backgroundTicketUploadTask()
|
||||
loadPendingUploadsOnce()
|
||||
local backoff = 2
|
||||
@@ -516,6 +687,17 @@ local stationByCode = {}
|
||||
local adjacency_regular, adjacency_express = {}, {}
|
||||
local transferGroupByCode = {}
|
||||
|
||||
local function updateVersionStateFromConfig()
|
||||
local remote = normalizeVersionTag(type(CFG.lua_versions) == 'table' and CFG.lua_versions.ticketmachine or nil)
|
||||
if #remote == 0 then
|
||||
expectedMachineVersion = nil
|
||||
versionMismatch = nil
|
||||
return
|
||||
end
|
||||
expectedMachineVersion = remote
|
||||
versionMismatch = (remote ~= normalizeVersionTag(VERSION))
|
||||
end
|
||||
|
||||
local function normalizeCode(s)
|
||||
s = tostring(s or '')
|
||||
s = s:gsub('[\239\187\191]', ''):gsub('%s+', '')
|
||||
@@ -644,6 +826,7 @@ local function refreshConfigOnce()
|
||||
if f then f.write(textutils.serializeJSON(cfg)); f.close() end
|
||||
CFG = cfg
|
||||
rebuildMaps()
|
||||
updateVersionStateFromConfig()
|
||||
os.queueEvent('config_updated')
|
||||
return true
|
||||
end
|
||||
@@ -665,6 +848,7 @@ end
|
||||
|
||||
CFG = loadConfig() or CFG
|
||||
rebuildMaps()
|
||||
updateVersionStateFromConfig()
|
||||
|
||||
|
||||
-- ###########################
|
||||
@@ -889,10 +1073,20 @@ end
|
||||
|
||||
local function drawVersionIndicator()
|
||||
if w < 1 then return end
|
||||
local markerColor = colors.yellow
|
||||
if versionMismatch == true then
|
||||
markerColor = colors.red
|
||||
elseif versionMismatch == false then
|
||||
markerColor = colors.lime
|
||||
end
|
||||
termDev.setBackgroundColor(colors.black)
|
||||
termDev.setTextColor(colors.gray)
|
||||
termDev.setCursorPos(1, 1)
|
||||
termDev.write(tostring(VERSION))
|
||||
if w >= (#tostring(VERSION) + 1) then
|
||||
termDev.setTextColor(markerColor)
|
||||
termDev.write('*')
|
||||
end
|
||||
termDev.setTextColor(colors.white)
|
||||
end
|
||||
|
||||
@@ -1457,6 +1651,33 @@ local function computeCost(src, dst, trainType)
|
||||
return nil
|
||||
end
|
||||
|
||||
local function paymentHintText()
|
||||
if detectedPaymentSide and #tostring(detectedPaymentSide) > 0 then
|
||||
return 'Payment side: ' .. tostring(detectedPaymentSide):upper()
|
||||
end
|
||||
return 'Insert payment on any side'
|
||||
end
|
||||
|
||||
local function snapshotPaymentInputs()
|
||||
local states = {}
|
||||
if not redstone or type(redstone.getInput) ~= 'function' then return states end
|
||||
for _, side in ipairs(REDSTONE_SIDES) do
|
||||
states[side] = redstone.getInput(side) and true or false
|
||||
end
|
||||
return states
|
||||
end
|
||||
|
||||
local function detectPaymentPulse(prevStates)
|
||||
local nowStates = snapshotPaymentInputs()
|
||||
for _, side in ipairs(REDSTONE_SIDES) do
|
||||
if nowStates[side] and not prevStates[side] then
|
||||
detectedPaymentSide = side
|
||||
return true, side, nowStates
|
||||
end
|
||||
end
|
||||
return false, nil, nowStates
|
||||
end
|
||||
|
||||
local function drawOrder()
|
||||
if state.productMode == 'card' then
|
||||
local cardSub = (state.cardMode == 'redeem') and 'Redeem IC card order' or 'Open new stored-value card'
|
||||
@@ -1536,7 +1757,7 @@ local function drawOrder()
|
||||
if total <= 0 then
|
||||
centerText(statusY + 1, 'Ready to confirm', colors.lightGray)
|
||||
else
|
||||
centerText(statusY + 1, 'Insert payment on RIGHT side', colors.lightGray)
|
||||
centerText(statusY + 1, paymentHintText(), colors.lightGray)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1603,6 +1824,8 @@ local function showOrderAndAudio()
|
||||
local reuseBlankCardId = firstString(state.pendingBlankCardId)
|
||||
if #reuseBlankCardId > 0 then
|
||||
payload.card_id = reuseBlankCardId
|
||||
else
|
||||
payload.card_id = generateCardId()
|
||||
end
|
||||
local okIssueBlank, blankCardId, issueMethod = false, '', 'reuse_pending'
|
||||
if #reuseBlankCardId == 0 then
|
||||
@@ -1616,13 +1839,19 @@ local function showOrderAndAudio()
|
||||
local okReq, code, parsed, err = submitCardOpen(payload)
|
||||
if okReq then
|
||||
local respData = (type(parsed) == 'table' and (parsed.data or parsed.card or parsed)) or {}
|
||||
state.card_id = firstString(respData.card_id, respData.id, payload.card_id)
|
||||
state.cardBalance = firstNumber(respData.balance, respData.stored_value, payload.balance) or payload.balance
|
||||
state.card_server_data = respData
|
||||
state.pendingBlankCardId = nil
|
||||
confirmed = true
|
||||
statusMsg, statusCol = 'Card ready', colors.green
|
||||
if not state.doneAudioPlayed then playConfirmTicketMelody(); state.doneAudioPlayed = true end
|
||||
local finalCard = buildFinalCardData(payload, respData)
|
||||
local okWrite, writtenCard, writeMethod = writeICCard(finalCard, { writeOnly = true })
|
||||
if okWrite then
|
||||
state.card_id = firstString(writtenCard.card_id, finalCard.card_id)
|
||||
state.cardBalance = tonumber(writtenCard.balance) or finalCard.balance
|
||||
state.card_server_data = respData
|
||||
state.pendingBlankCardId = nil
|
||||
confirmed = true
|
||||
statusMsg, statusCol = 'Card ready', colors.green
|
||||
if not state.doneAudioPlayed then playConfirmTicketMelody(); state.doneAudioPlayed = true end
|
||||
else
|
||||
statusMsg, statusCol = 'Write failed: ' .. tostring(writeMethod), colors.red
|
||||
end
|
||||
else
|
||||
local errorMsg = 'Card API Err'
|
||||
if code == 409 then
|
||||
@@ -1640,20 +1869,7 @@ local function showOrderAndAudio()
|
||||
local okReq, code, parsed, err = submitCardOpen(payload)
|
||||
if okReq then
|
||||
local respData = (type(parsed) == 'table' and (parsed.data or parsed.card or parsed)) or {}
|
||||
local finalCard = {
|
||||
card_id = firstString(respData.card_id, respData.id, payload.card_id),
|
||||
holder_name = payload.holder_name,
|
||||
balance = firstNumber(respData.balance, respData.stored_value, payload.balance) or payload.balance,
|
||||
deposit = firstNumber(respData.deposit, payload.deposit) or payload.deposit,
|
||||
topup = firstNumber(respData.topup, respData.first_topup, payload.topup) or payload.topup,
|
||||
station_code = payload.station_code,
|
||||
device = payload.device,
|
||||
voucher_code = payload.voucher_code,
|
||||
media = 'ic_card',
|
||||
product_type = 'stored_value',
|
||||
order_value = payload.order_value,
|
||||
initial_balance = payload.topup
|
||||
}
|
||||
local finalCard = buildFinalCardData(payload, respData)
|
||||
local okWrite, writtenCard, writeMethod = writeICCard(finalCard)
|
||||
if okWrite then
|
||||
state.card_id = firstString(writtenCard.card_id, finalCard.card_id)
|
||||
@@ -1736,12 +1952,12 @@ local function showOrderAndAudio()
|
||||
confirmAction()
|
||||
end
|
||||
|
||||
local prev = redstone.getInput('right')
|
||||
local prevInputs = snapshotPaymentInputs()
|
||||
while state.page == 'order' do
|
||||
local ev, p1, p2, p3 = os.pullEvent()
|
||||
if ev == 'redstone' then
|
||||
local now = redstone.getInput('right')
|
||||
if now and not prev then
|
||||
local pulsed, _, nextInputs = detectPaymentPulse(prevInputs)
|
||||
if pulsed then
|
||||
playNote('hat', 20, 1, 0.01)
|
||||
state.paid = (state.paid or 0) + 1; render()
|
||||
if state.paid >= (state.cost or 0) then
|
||||
@@ -1752,7 +1968,8 @@ local function showOrderAndAudio()
|
||||
sleep(0.5) -- Wait for UI/Audio slightly
|
||||
confirmAction()
|
||||
end
|
||||
end; prev = now
|
||||
end
|
||||
prevInputs = nextInputs
|
||||
elseif ev == 'mouse_click' or ev == 'monitor_touch' then
|
||||
-- For mouse_click: p1=button, p2=x, p3=y
|
||||
-- For monitor_touch: p1=side, p2=x, p3=y
|
||||
@@ -1794,9 +2011,10 @@ local function generateTicketId()
|
||||
end
|
||||
|
||||
local function ensureTicketIdFormat(id)
|
||||
if id == nil then return generateTicketId() end
|
||||
if id == nil then return '' end
|
||||
local s = tostring(id)
|
||||
s = s:gsub('%s+', '')
|
||||
if #s == 0 then return '' end
|
||||
local prefix, num = s:match('^([A-Za-z][A-Za-z])%-?([0-9]+)$')
|
||||
if prefix and num then
|
||||
prefix = prefix:upper()
|
||||
@@ -1890,22 +2108,35 @@ local function showDone()
|
||||
start_name = startObj and unicodeEscape(startObj.name) or nil,
|
||||
terminal_name = terminalObj and unicodeEscape(terminalObj.name) or nil,
|
||||
start_name_en = fromNameEn,
|
||||
terminal_name_en = toNameEn
|
||||
terminal_name_en = toNameEn,
|
||||
ts = (os.epoch and os.epoch('utc')) or (os.time() * 1000)
|
||||
}
|
||||
if ticketVendingMachine and ticketVendingMachine.issueTicket then
|
||||
local apiType = (state.trainType == 'Express') and 'limited_express' or 'local'
|
||||
local okCall, okIssue, ticketId = pcall(ticketVendingMachine.issueTicket, fromNameEnArg, toNameEnArg, apiType, rides, cost, startStationArg, terminalStationArg, fromNameCnUArg, toNameCnUArg)
|
||||
if not (okCall and okIssue and ticketId) then
|
||||
okCall, okIssue, ticketId = pcall(ticketVendingMachine.issueTicket, fromNameEnArg, toNameEnArg, apiType, rides, cost, startStationArg, terminalStationArg)
|
||||
end
|
||||
if okCall and okIssue and ticketId then
|
||||
state.ticket_id = ensureTicketIdFormat(ticketId)
|
||||
issueSource = 'ticket_vending_machine'
|
||||
else
|
||||
state.ticket_id = generateTicketId()
|
||||
end
|
||||
|
||||
local apiType = (state.trainType == 'Express') and 'limited_express' or 'local'
|
||||
local localGeneratedTicketId = generateTicketId()
|
||||
local okIssueTicket, issuedTicketId, issueMethod = issueTicketFromPeripheral(
|
||||
fromNameEnArg,
|
||||
toNameEnArg,
|
||||
apiType,
|
||||
rides,
|
||||
cost,
|
||||
startStationArg,
|
||||
terminalStationArg,
|
||||
fromNameCnUArg,
|
||||
toNameCnUArg,
|
||||
localGeneratedTicketId
|
||||
)
|
||||
if okIssueTicket then
|
||||
state.ticket_id = issuedTicketId
|
||||
issueSource = 'ticket_vending_machine'
|
||||
else
|
||||
state.ticket_id = generateTicketId()
|
||||
local issueError = tostring(issueMethod or 'ticket_issue_failed')
|
||||
print('Ticket issue failed: ' .. issueError)
|
||||
_G.TICKET_MACHINE_LAST_TICKET.ticket_issue_error = issueError
|
||||
showAlert('Ticket issue failed')
|
||||
resetTicketFlow()
|
||||
state.page = 'home'
|
||||
return
|
||||
end
|
||||
|
||||
pcall(function()
|
||||
@@ -2074,7 +2305,6 @@ local function showOnlineVoucher()
|
||||
elseif ev == 'key' and p1 == keys.backspace then code = code:sub(1, -2)
|
||||
elseif ev == 'key' and (p1 == keys.enter or p1 == keys.numPadEnter) then submitCode()
|
||||
elseif ev == 'config_updated' then
|
||||
-- Config updated in background, continue to redraw
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2101,4 +2331,4 @@ local function mainPageLoop()
|
||||
end
|
||||
end
|
||||
|
||||
parallel.waitForAny(mainPageLoop, backgroundSyncTask, backgroundTicketUploadTask)
|
||||
parallel.waitForAny(mainPageLoop, backgroundSyncTask, backgroundTicketUploadTask, backgroundPeripheralTask)
|
||||
|
||||
Reference in New Issue
Block a user