feat(ticketmachine): 新增自定义TM GPU外设支持并更新版本至v1.5.26
- 新增table.pack兼容函数适配旧版Lua环境 - 实现TM GPU设备检测、终端仿真与坐标颜色转换逻辑 - 添加事件兼容层转换自定义触摸事件为标准事件并修正坐标 - 更新外设发现逻辑优先使用TM GPU终端 - 将版本从v1.5.25升级至v1.5.26
This commit is contained in:
+197
-3
@@ -1,8 +1,12 @@
|
|||||||
local CURRENT_STATION_CODE = 'Ticket-Machine'
|
local CURRENT_STATION_CODE = 'Ticket-Machine'
|
||||||
local API_BASE = 'https://ticket.fse-media.group/api'
|
local API_BASE = 'https://ticket.fse-media.group/api'
|
||||||
local VERSION = 'v1.5.25'
|
local VERSION = 'v1.5.26'
|
||||||
local UPDATE_CHECK_INTERVAL = 5
|
local UPDATE_CHECK_INTERVAL = 5
|
||||||
|
|
||||||
|
local pack = table.pack or function(...)
|
||||||
|
return { n = select('#', ...), ... }
|
||||||
|
end
|
||||||
|
|
||||||
-- ###########################
|
-- ###########################
|
||||||
-- Core HTTP & JSON Utilities
|
-- Core HTTP & JSON Utilities
|
||||||
-- ###########################
|
-- ###########################
|
||||||
@@ -340,13 +344,172 @@ local function reportIssue(errMsg, detail)
|
|||||||
postJSONResult(url, payload)
|
postJSONResult(url, payload)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local GPU_CHAR_WIDTH = 4
|
||||||
|
local GPU_CHAR_HEIGHT = 6
|
||||||
|
|
||||||
|
local GPU_COLOR_MAP = {
|
||||||
|
[colors.white] = 0xFFFFFFFF,
|
||||||
|
[colors.orange] = 0xFFFFA500,
|
||||||
|
[colors.magenta] = 0xFFFF00FF,
|
||||||
|
[colors.lightBlue] = 0xFF66CCFF,
|
||||||
|
[colors.yellow] = 0xFFFFFF00,
|
||||||
|
[colors.lime] = 0xFF00FF00,
|
||||||
|
[colors.pink] = 0xFFFFB6C1,
|
||||||
|
[colors.gray] = 0xFF808080,
|
||||||
|
[colors.lightGray] = 0xFFD3D3D3,
|
||||||
|
[colors.cyan] = 0xFF00FFFF,
|
||||||
|
[colors.purple] = 0xFF800080,
|
||||||
|
[colors.blue] = 0xFF0000FF,
|
||||||
|
[colors.brown] = 0xFF8B4513,
|
||||||
|
[colors.green] = 0xFF008000,
|
||||||
|
[colors.red] = 0xFFFF0000,
|
||||||
|
[colors.black] = 0xFF000000,
|
||||||
|
}
|
||||||
|
|
||||||
|
local function ccColorToARGB(col)
|
||||||
|
return GPU_COLOR_MAP[col] or GPU_COLOR_MAP[colors.white]
|
||||||
|
end
|
||||||
|
|
||||||
|
local function isTmGpu(dev)
|
||||||
|
return type(dev) == 'table'
|
||||||
|
and type(dev.getSize) == 'function'
|
||||||
|
and type(dev.drawText) == 'function'
|
||||||
|
and type(dev.fill) == 'function'
|
||||||
|
and type(dev.sync) == 'function'
|
||||||
|
end
|
||||||
|
|
||||||
|
local function buildTmGpuTerm(dev)
|
||||||
|
local gpuTerm = {
|
||||||
|
kind = 'tm_gpu',
|
||||||
|
device = dev,
|
||||||
|
cursorX = 1,
|
||||||
|
cursorY = 1,
|
||||||
|
textColor = colors.white,
|
||||||
|
backgroundColor = colors.black,
|
||||||
|
width = 1,
|
||||||
|
height = 1,
|
||||||
|
pixelWidth = 0,
|
||||||
|
pixelHeight = 0,
|
||||||
|
resolutionConfigured = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
local function syncSize()
|
||||||
|
if type(gpuTerm.device.refreshSize) == 'function' then
|
||||||
|
pcall(gpuTerm.device.refreshSize)
|
||||||
|
end
|
||||||
|
|
||||||
|
for _ = 1, 3 do
|
||||||
|
local size = pack(pcall(gpuTerm.device.getSize))
|
||||||
|
local px = tonumber(size[2]) or 0
|
||||||
|
local py = tonumber(size[3]) or 0
|
||||||
|
if size[1] and px > 0 and py > 0 then
|
||||||
|
if not gpuTerm.resolutionConfigured and type(gpuTerm.device.setSize) == 'function' then
|
||||||
|
gpuTerm.resolutionConfigured = true
|
||||||
|
local resolution = tonumber(size[6]) or 0
|
||||||
|
if resolution > 0 and resolution < 32 then
|
||||||
|
pcall(gpuTerm.device.setSize, 32)
|
||||||
|
os.sleep(0)
|
||||||
|
size = pack(pcall(gpuTerm.device.getSize))
|
||||||
|
px = tonumber(size[2]) or px
|
||||||
|
py = tonumber(size[3]) or py
|
||||||
|
end
|
||||||
|
end
|
||||||
|
gpuTerm.pixelWidth = px
|
||||||
|
gpuTerm.pixelHeight = py
|
||||||
|
gpuTerm.width = math.max(1, math.floor(px / GPU_CHAR_WIDTH))
|
||||||
|
gpuTerm.height = math.max(1, math.floor(py / GPU_CHAR_HEIGHT))
|
||||||
|
return gpuTerm.width, gpuTerm.height
|
||||||
|
end
|
||||||
|
os.sleep(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
if gpuTerm.pixelWidth <= 0 then gpuTerm.pixelWidth = 64 end
|
||||||
|
if gpuTerm.pixelHeight <= 0 then gpuTerm.pixelHeight = 32 end
|
||||||
|
gpuTerm.width = math.max(1, math.floor(gpuTerm.pixelWidth / GPU_CHAR_WIDTH))
|
||||||
|
gpuTerm.height = math.max(1, math.floor(gpuTerm.pixelHeight / GPU_CHAR_HEIGHT))
|
||||||
|
return gpuTerm.width, gpuTerm.height
|
||||||
|
end
|
||||||
|
|
||||||
|
function gpuTerm.getSize()
|
||||||
|
return syncSize()
|
||||||
|
end
|
||||||
|
|
||||||
|
function gpuTerm.setCursorPos(x, y)
|
||||||
|
gpuTerm.cursorX = math.max(1, math.floor(tonumber(x) or 1))
|
||||||
|
gpuTerm.cursorY = math.max(1, math.floor(tonumber(y) or 1))
|
||||||
|
end
|
||||||
|
|
||||||
|
function gpuTerm.setTextColor(col)
|
||||||
|
gpuTerm.textColor = GPU_COLOR_MAP[col] and col or colors.white
|
||||||
|
end
|
||||||
|
|
||||||
|
function gpuTerm.setBackgroundColor(col)
|
||||||
|
gpuTerm.backgroundColor = GPU_COLOR_MAP[col] and col or colors.black
|
||||||
|
end
|
||||||
|
|
||||||
|
function gpuTerm.clear()
|
||||||
|
syncSize()
|
||||||
|
pcall(gpuTerm.device.fill, ccColorToARGB(gpuTerm.backgroundColor))
|
||||||
|
gpuTerm.cursorX = 1
|
||||||
|
gpuTerm.cursorY = 1
|
||||||
|
pcall(gpuTerm.device.sync)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gpuTerm.write(text)
|
||||||
|
text = tostring(text or '')
|
||||||
|
if #text == 0 then return end
|
||||||
|
|
||||||
|
local width, height = syncSize()
|
||||||
|
local originalLen = #text
|
||||||
|
local x = gpuTerm.cursorX
|
||||||
|
local y = gpuTerm.cursorY
|
||||||
|
|
||||||
|
if y < 1 or y > height or x > width then
|
||||||
|
gpuTerm.cursorX = x + originalLen
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if x < 1 then
|
||||||
|
local shift = 1 - x
|
||||||
|
if shift >= #text then
|
||||||
|
gpuTerm.cursorX = x + originalLen
|
||||||
|
return
|
||||||
|
end
|
||||||
|
text = text:sub(shift + 1)
|
||||||
|
x = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local visible = text:sub(1, math.max(0, width - x + 1))
|
||||||
|
if #visible > 0 then
|
||||||
|
local px = ((x - 1) * GPU_CHAR_WIDTH) + 1
|
||||||
|
local py = ((y - 1) * GPU_CHAR_HEIGHT) + 1
|
||||||
|
local bg = ccColorToARGB(gpuTerm.backgroundColor)
|
||||||
|
local fg = ccColorToARGB(gpuTerm.textColor)
|
||||||
|
pcall(gpuTerm.device.filledRectangle, px, py, #visible * GPU_CHAR_WIDTH, GPU_CHAR_HEIGHT, bg)
|
||||||
|
pcall(gpuTerm.device.drawText, px, py, visible, fg, bg, 1, 0)
|
||||||
|
pcall(gpuTerm.device.sync)
|
||||||
|
end
|
||||||
|
|
||||||
|
gpuTerm.cursorX = gpuTerm.cursorX + originalLen
|
||||||
|
end
|
||||||
|
|
||||||
|
function gpuTerm.sync()
|
||||||
|
pcall(gpuTerm.device.sync)
|
||||||
|
end
|
||||||
|
|
||||||
|
return gpuTerm
|
||||||
|
end
|
||||||
|
|
||||||
-- ###########################
|
-- ###########################
|
||||||
-- Peripheral discovery
|
-- Peripheral discovery
|
||||||
-- ###########################
|
-- ###########################
|
||||||
local SIDE_PRIORITY = { top = 1, bottom = 2, left = 3, right = 4, front = 5, back = 6 }
|
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 REDSTONE_SIDES = { 'right', 'left', 'top', 'bottom', 'front', 'back' }
|
||||||
|
local nativeTerm = (term and type(term.current) == 'function' and term.current()) or term
|
||||||
local monitor = nil
|
local monitor = nil
|
||||||
local monitorName = nil
|
local monitorName = nil
|
||||||
|
local tmGpu = nil
|
||||||
|
local tmGpuName = nil
|
||||||
local ticketVendingMachine = nil
|
local ticketVendingMachine = nil
|
||||||
local ticketVendingMachineName = nil
|
local ticketVendingMachineName = nil
|
||||||
local speaker = nil
|
local speaker = nil
|
||||||
@@ -403,23 +566,30 @@ local function findPeripheralByType(typeName)
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local termDev = term
|
local termDev = nativeTerm or term
|
||||||
local w, h = termDev.getSize()
|
local w, h = termDev.getSize()
|
||||||
|
|
||||||
local function refreshDevices()
|
local function refreshDevices()
|
||||||
local prevSignature = table.concat({
|
local prevSignature = table.concat({
|
||||||
tostring(monitorName or ''),
|
tostring(monitorName or ''),
|
||||||
|
tostring(tmGpuName or ''),
|
||||||
tostring(ticketVendingMachineName or ''),
|
tostring(ticketVendingMachineName or ''),
|
||||||
tostring(speakerName or '')
|
tostring(speakerName or '')
|
||||||
}, '|')
|
}, '|')
|
||||||
monitor, monitorName = findPeripheralByType('monitor')
|
monitor, monitorName = findPeripheralByType('monitor')
|
||||||
|
tmGpu, tmGpuName = findPeripheralByType('tm_gpu')
|
||||||
ticketVendingMachine, ticketVendingMachineName = findPeripheralByType('ticket_vending_machine')
|
ticketVendingMachine, ticketVendingMachineName = findPeripheralByType('ticket_vending_machine')
|
||||||
speaker, speakerName = findPeripheralByType('speaker')
|
speaker, speakerName = findPeripheralByType('speaker')
|
||||||
termDev = monitor or term
|
if tmGpu and isTmGpu(tmGpu) then
|
||||||
|
termDev = buildTmGpuTerm(tmGpu)
|
||||||
|
else
|
||||||
|
termDev = monitor or nativeTerm or term
|
||||||
if monitor then pcall(monitor.setTextScale, 0.5) end
|
if monitor then pcall(monitor.setTextScale, 0.5) end
|
||||||
|
end
|
||||||
w, h = termDev.getSize()
|
w, h = termDev.getSize()
|
||||||
local nextSignature = table.concat({
|
local nextSignature = table.concat({
|
||||||
tostring(monitorName or ''),
|
tostring(monitorName or ''),
|
||||||
|
tostring(tmGpuName or ''),
|
||||||
tostring(ticketVendingMachineName or ''),
|
tostring(ticketVendingMachineName or ''),
|
||||||
tostring(speakerName or '')
|
tostring(speakerName or '')
|
||||||
}, '|')
|
}, '|')
|
||||||
@@ -430,6 +600,30 @@ end
|
|||||||
|
|
||||||
refreshDevices()
|
refreshDevices()
|
||||||
|
|
||||||
|
local nativePullEvent = os.pullEvent
|
||||||
|
local function pullEventCompat(filter)
|
||||||
|
while true do
|
||||||
|
local ev = pack(nativePullEvent())
|
||||||
|
local eventName = ev[1]
|
||||||
|
|
||||||
|
if eventName == 'tm_monitor_touch' then
|
||||||
|
local px = tonumber(ev[3]) or 1
|
||||||
|
local py = tonumber(ev[4]) or 1
|
||||||
|
ev[1] = 'monitor_touch'
|
||||||
|
ev[2] = tostring(ev[2] or tmGpuName or '')
|
||||||
|
ev[3] = math.max(1, math.floor((px - 1) / GPU_CHAR_WIDTH) + 1)
|
||||||
|
ev[4] = math.max(1, math.floor((py - 1) / GPU_CHAR_HEIGHT) + 1)
|
||||||
|
eventName = ev[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
if not filter or filter == eventName then
|
||||||
|
return table.unpack(ev, 1, ev.n or #ev)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
os.pullEvent = pullEventCompat
|
||||||
|
|
||||||
local function saveCardIssueSnapshot(cardData)
|
local function saveCardIssueSnapshot(cardData)
|
||||||
pcall(function()
|
pcall(function()
|
||||||
ensureDir('logs/last_card_issue.json')
|
ensureDir('logs/last_card_issue.json')
|
||||||
|
|||||||
Reference in New Issue
Block a user