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:
2026-07-31 11:15:31 +08:00
parent 200dbda9bd
commit 879445b9f6
+198 -4
View File
@@ -1,8 +1,12 @@
local CURRENT_STATION_CODE = 'Ticket-Machine'
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 pack = table.pack or function(...)
return { n = select('#', ...), ... }
end
-- ###########################
-- Core HTTP & JSON Utilities
-- ###########################
@@ -340,13 +344,172 @@ local function reportIssue(errMsg, detail)
postJSONResult(url, payload)
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
-- ###########################
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 nativeTerm = (term and type(term.current) == 'function' and term.current()) or term
local monitor = nil
local monitorName = nil
local tmGpu = nil
local tmGpuName = nil
local ticketVendingMachine = nil
local ticketVendingMachineName = nil
local speaker = nil
@@ -403,23 +566,30 @@ local function findPeripheralByType(typeName)
return nil, nil
end
local termDev = term
local termDev = nativeTerm or term
local w, h = termDev.getSize()
local function refreshDevices()
local prevSignature = table.concat({
tostring(monitorName or ''),
tostring(tmGpuName or ''),
tostring(ticketVendingMachineName or ''),
tostring(speakerName or '')
}, '|')
monitor, monitorName = findPeripheralByType('monitor')
tmGpu, tmGpuName = findPeripheralByType('tm_gpu')
ticketVendingMachine, ticketVendingMachineName = findPeripheralByType('ticket_vending_machine')
speaker, speakerName = findPeripheralByType('speaker')
termDev = monitor or term
if monitor then pcall(monitor.setTextScale, 0.5) end
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
end
w, h = termDev.getSize()
local nextSignature = table.concat({
tostring(monitorName or ''),
tostring(tmGpuName or ''),
tostring(ticketVendingMachineName or ''),
tostring(speakerName or '')
}, '|')
@@ -430,6 +600,30 @@ end
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)
pcall(function()
ensureDir('logs/last_card_issue.json')