fix: 修复检票机首次加载黑屏及完善亮灯模块对接
- 增加外设初始化重试机制,等待monitor和检票机外设就绪(最多20秒), 在等待期间显示进度提示,避免区块加载时脚本提前退出导致屏幕无显示 - 修复Socket.IO服务端未强制websocket传输配置,避免负载均衡环境下轮询产生400错误 - 所有业务拒绝(deny)场景下调用markFault触发红灯,包括: * 无效票/卡、卡ID缺失、票ID缺失 * 站点不匹配、未进站/已出站、已进站/已出站 * 余额不足、票价查询失败、网络错误 * 外设写入/扣费失败等所有错误路径 - 版本号升级至v1.5.29
This commit is contained in:
@@ -0,0 +1,296 @@
|
||||
# FSE Ticketing CC API
|
||||
|
||||
## 通用说明
|
||||
|
||||
- 外设类型通过 `peripheral.getType(name)` 返回,或在 `peripheral.wrap(name)` 后通过 `peripheral.call`/方法调用使用。
|
||||
- 返回值约定:
|
||||
- 成功通常返回 `true`(部分方法额外返回数据)。
|
||||
- 失败通常返回 `false, "error_message"` 或 `nil, "error_message"`(取决于具体方法)。
|
||||
- 票/卡信息表(Lua table)中的字段 key 与模组 NBT key 一致,详见“数据字段”章节。
|
||||
|
||||
## 外设:ticket\_vending\_machine(售票机)
|
||||
|
||||
### 方法:issueTicket
|
||||
|
||||
发放一张票并从售票机正面吐出(生成掉落物)。
|
||||
|
||||
```lua
|
||||
local ok, ticketId = vending.issueTicket(
|
||||
startNameEn,
|
||||
terminalNameEn,
|
||||
type,
|
||||
rides,
|
||||
cost,
|
||||
startStation,
|
||||
terminalStation,
|
||||
fromNameCnU,
|
||||
toNameCnU
|
||||
)
|
||||
```
|
||||
|
||||
- 参数(均为可选,未提供时使用默认值)
|
||||
- `startNameEn`:起点(英文/标识),默认 `"???"`
|
||||
- `terminalNameEn`:终点(英文/标识),默认 `"???"`
|
||||
- `type`:票种,默认 `"local"`
|
||||
- `"local"`:普通票(Local Ticket)
|
||||
- `"limited_express"`:特急票(Exp Ticket)
|
||||
- `"single"`:单程票(Single-trip Ticket)
|
||||
- `rides`:可乘坐次数,默认 `1`,最小会被钳制为 `1`
|
||||
- `cost`:金额,默认 `0`
|
||||
- `startStation`:起点站 ID/代码,默认 `""`
|
||||
- `terminalStation`:终点站 ID/代码,默认 `""`
|
||||
- `fromNameCnU`:中文起点名(可能包含 `\u` 转义),默认 `""`
|
||||
- `toNameCnU`:中文终点名(可能包含 `\u` 转义),默认 `""`
|
||||
- 返回
|
||||
- `ok`:`true`
|
||||
- `ticketId`:生成的票 ID(形如 `AB-12345678`)
|
||||
- 方块状态 / 模型切换(对应票种)
|
||||
- 当 `type` 为 `"local"` 时:成功调用后,方块模型暂时切换为 `ticket_vending_machine_success_localticket`(露出绿票 / 本地票图案)。
|
||||
- 当 `type` 为 `"limited_express"` 或 `"single"` 时:暂时切换为 `ticket_vending_machine_success_expticket`(露出橙红特急票 / 单程票图案)。
|
||||
- 两种状态持续约 20 tick(1 秒)后自动恢复基础款模型 `ticket_vending_machine`。
|
||||
|
||||
### 方法:issueICCard
|
||||
|
||||
发放一张 IC 卡并从售票机正面吐出(生成掉落物)。
|
||||
|
||||
```lua
|
||||
local ok, cardId = vending.issueICCard(ownerName, balance)
|
||||
```
|
||||
|
||||
- 参数(可选)
|
||||
- `ownerName`:持有人名字,默认 `""`
|
||||
- `balance`:初始余额,默认 `0`
|
||||
- 返回
|
||||
- `ok`:`true`
|
||||
- `cardId`:生成的卡号(形如 `IC-xxxxxxxx`)
|
||||
- 方块状态 / 模型切换
|
||||
- 方法成功返回时,方块模型暂时切换到 `ticket_vending_machine_success_ic`(正面露出 IC 卡图案),20 tick(1 秒)后自动恢复基础款模型。
|
||||
|
||||
### 方法:issueFSEPass
|
||||
|
||||
```lua
|
||||
local ok, ticketId = vending.issueFSEPass(ownerName, startNameEn, terminalNameEn, cost)
|
||||
```
|
||||
|
||||
发放一张 FSE Pass 通票(紫色 FSE_PASS 物品)并从售票机正面吐出。参数从左到右依次为:
|
||||
|
||||
- 参数(均为可选)
|
||||
- `ownerName`:持有人名字,默认 `""`
|
||||
- `startNameEn`:起点(英文/标识),默认 `"???"`
|
||||
- `terminalNameEn`:终点(英文/标识),默认 `"???"`
|
||||
- `cost`:金额,默认 `0`
|
||||
- 返回
|
||||
- `ok`:`true`
|
||||
- `ticketId`:生成的通票 ID(同样形如 `AB-12345678`)
|
||||
- 方块状态 / 模型切换
|
||||
- 方法成功返回时,方块模型暂时切换为 `ticket_vending_machine_success_expticket`(与特急票款外观),20 tick 后恢复基础款模型。
|
||||
|
||||
## 外设:ticket\_inspection\_machine(检票机)
|
||||
|
||||
检票机通过玩家右键扫描票/卡,保存“最近一次扫描”的数据,并向连接的 CC 电脑推送事件。
|
||||
|
||||
### 方法:getLastScanned
|
||||
|
||||
获取最近一次扫描到的票/卡信息表。
|
||||
|
||||
```lua
|
||||
local infoOrNil, err = gate.getLastScanned()
|
||||
```
|
||||
|
||||
- 返回(成功)
|
||||
- `infoOrNil`:Lua table(票信息或 IC 信息)
|
||||
- 返回(失败)
|
||||
- `nil, "no ticket scanned"`
|
||||
|
||||
### 方法:destroyTicket
|
||||
|
||||
销毁最近一次扫描的物品(直接清空玩家手上那一张票/卡)。
|
||||
|
||||
```lua
|
||||
local ok, err = gate.destroyTicket()
|
||||
```
|
||||
|
||||
- 返回
|
||||
- `true`:已投递到服务器主线程执行
|
||||
- `false, "not server level"`:当前世界不是服务端世界
|
||||
|
||||
注意:该方法当前实现为“投递执行”,不会把实际销毁结果回传给 Lua;建议通过事件与 `getLastScanned()` 侧面确认状态。
|
||||
|
||||
- 方块状态
|
||||
- 成功执行:方块模型切换到 `ticket_inspection_machine_success`(绿灯通过状态)
|
||||
- 执行失败(如找不到扫描玩家):切换到 `ticket_inspection_machine_false`(红灯拒绝状态)
|
||||
- 两种状态均持续约 40 tick(2 秒)后自动恢复 `NONE` 基础模型。
|
||||
|
||||
### 方法:deductICCard
|
||||
|
||||
对最近一次扫描到的 IC 卡扣费。
|
||||
|
||||
```lua
|
||||
local ok, err = gate.deductICCard(amount)
|
||||
```
|
||||
|
||||
- 参数
|
||||
- `amount`:扣费金额(double)
|
||||
- 返回
|
||||
- `true`:已投递到服务器主线程执行
|
||||
- `false, "not server level"`:当前世界不是服务端世界
|
||||
|
||||
注意:当前实现同样不会把扣费后的余额回传给 Lua;如果需要余额,请调用 `getLastScanned()` 或使用充值机外设完成扣费/查询。
|
||||
|
||||
- 方块状态
|
||||
- 成功扣费:`_success`(绿灯)40 tick
|
||||
- 失败(非 IC 卡 / 找不到玩家 / 卡片不匹配 / 余额不足):`_false`(红灯)40 tick
|
||||
|
||||
### 方法:markEntered
|
||||
|
||||
将最近一次扫描的票/卡标记为已进站,并(可选)写入进站站点。
|
||||
|
||||
```lua
|
||||
local ok, err = gate.markEntered(entryStationId)
|
||||
```
|
||||
|
||||
- 参数(可选)
|
||||
- `entryStationId`:站点 ID/代码,默认 `""`(为空则不写入)
|
||||
- 返回
|
||||
- `true`:已投递到服务器主线程执行
|
||||
- `false, "not server level"`
|
||||
|
||||
执行后会推送 `ticket_state_updated` 或 `ic_card_state_updated` 事件(取决于最近扫描的是票还是卡)。
|
||||
|
||||
- 方块状态
|
||||
- 成功写入:`_success`(绿灯)40 tick
|
||||
- 失败(玩家/票卡无效):`_false`(红灯)40 tick
|
||||
|
||||
### 方法:markExited
|
||||
|
||||
将最近一次扫描的票/卡标记为已出站,并清理进站站点字段。
|
||||
|
||||
```lua
|
||||
local ok, err = gate.markExited()
|
||||
```
|
||||
|
||||
- 返回
|
||||
- `true`:已投递到服务器主线程执行
|
||||
- `false, "not server level"`
|
||||
- 方块状态
|
||||
- 成功写入:`_success`(绿灯)40 tick
|
||||
- 失败(玩家/票卡无效):`_false`(红灯)40 tick
|
||||
|
||||
### 方法:resetTicketState
|
||||
|
||||
将最近一次扫描的票/卡状态复位(entered/exited 均为 false,并清理进站站点字段)。
|
||||
|
||||
```lua
|
||||
local ok, err = gate.resetTicketState()
|
||||
```
|
||||
|
||||
- 返回
|
||||
- `true`:已投递到服务器主线程执行
|
||||
- `false, "not server level"`
|
||||
- 方块状态
|
||||
- 成功复位:`_success`(绿灯)40 tick
|
||||
- 失败(玩家/票卡无效):`_false`(红灯)40 tick
|
||||
|
||||
### 方法:markFault
|
||||
|
||||
CC 侧业务判断失败时,主动触发红灯拒绝状态(例如业务逻辑拒绝进站/出站,但 CC 侧又不想调用 resetTicketState 时)。
|
||||
|
||||
```lua
|
||||
local ok, err = gate.markFault()
|
||||
```
|
||||
|
||||
- 返回
|
||||
- `true`:已投递到服务器主线程执行(红灯亮起)
|
||||
- `false, "not server level"`:当前世界不是服务端世界
|
||||
- 方块状态
|
||||
- 调用后立即切换到 `ticket_inspection_machine_false`(红灯拒绝状态),持续 40 tick(2 秒)后自动恢复 NONE 基础款。
|
||||
- 与 `markEntered`/`markExited` 等方法内部业务错误触发的红灯效果完全相同。
|
||||
|
||||
### 事件
|
||||
|
||||
检票机对所有已连接电脑(`attach` 过的电脑)推送以下事件。事件参数为 1 个 table:
|
||||
|
||||
- `ticket_scanned(info)`
|
||||
- `ic_card_scanned(info)`
|
||||
- `ticket_state_updated(info)`
|
||||
- `ic_card_state_updated(info)`
|
||||
|
||||
`info` 结构见“数据字段”章节(包含 `passenger` 字段,值为扫描者玩家名)。
|
||||
|
||||
## 外设:ic\_refill\_machine(IC 充值机)
|
||||
|
||||
充值机要求玩家先把 IC 卡插入机器(右键把 IC 卡放进去)。只有在机器内有卡时,Lua 方法才可用。
|
||||
|
||||
- 方块状态 / 模型切换
|
||||
- 玩家右键把 IC 卡(手持 `limitcard` 物品)插入机器 → `HAS_CARD=true`,方块模型切换为 `ic_refill_machine_inserted`(卡片露出卡槽状态),同时 scheduleTick 100 tick(5 秒超时)。
|
||||
- 玩家右键空手拿走卡、或取出卡、或 5 秒超时(`tick()` 触发)→ `HAS_CARD=false`,模型切回基础款 `ic_refill_machine`。
|
||||
- 充值 / 扣费时(`refill`、`deduct`)只要卡还在,始终保持 `_inserted` 模型,不会额外切换。
|
||||
|
||||
### 方法:getCardInfo
|
||||
|
||||
```lua
|
||||
local infoOrNil, err = refill.getCardInfo()
|
||||
```
|
||||
|
||||
- 返回(成功)
|
||||
- `infoOrNil`:Lua table(IC 卡信息)
|
||||
- 返回(失败)
|
||||
- `nil, "no card"`:机器内没有插卡
|
||||
|
||||
### 方法:refill
|
||||
|
||||
```lua
|
||||
local okOrNil, r2 = refill.refill(amount)
|
||||
```
|
||||
|
||||
- 参数
|
||||
- `amount`:充值金额(double)
|
||||
- 返回(成功)
|
||||
- `true, newBalance`
|
||||
- 返回(失败)
|
||||
- `nil, "no card"`
|
||||
|
||||
### 方法:deduct
|
||||
|
||||
```lua
|
||||
local okOrNil, r2 = refill.deduct(amount)
|
||||
```
|
||||
|
||||
- 参数
|
||||
- `amount`:扣费金额(double)
|
||||
- 返回(成功)
|
||||
- `true, newBalance`
|
||||
- 返回(失败)
|
||||
- `nil, "no card"`
|
||||
- `false, "insufficient"`:余额不足(扣费后会变成负数时阻止)
|
||||
|
||||
## 数据字段(info table keys)
|
||||
|
||||
### 票信息(ticket\_\* / ticket\_state\_updated / getLastScanned 返回为票时)
|
||||
|
||||
- `start_name_en`:起点名(英文/标识)
|
||||
- `terminal_name_en`:终点名(英文/标识)
|
||||
- `start_station`:起点站 ID/代码
|
||||
- `terminal_station`:终点站 ID/代码
|
||||
- `fromNameCnU`:中文起点名(可能包含 `\u` 转义)
|
||||
- `toNameCnU`:中文终点名(可能包含 `\u` 转义)
|
||||
- `type`:票种(`local` / `limited_express` / `single`)
|
||||
- `rides`:剩余/可用次数
|
||||
- `entered`:是否已进站
|
||||
- `exited`:是否已出站
|
||||
- `ticketId`:票 ID
|
||||
- `timestamp`:签发时间戳(毫秒)
|
||||
- `cost`:金额
|
||||
- `order_datetime`:下单/签发时间字符串(`yyyy-MM-dd HH:mm:ss`)
|
||||
- `passenger`:扫描者玩家名(仅检票机事件与 getLastScanned 返回中提供)
|
||||
|
||||
注意:单程票的 `line_name` 当前不在 `info` table 中返回(仅保存在票的 NBT 内)。
|
||||
|
||||
### IC 信息(ic\_\* / ic\_card\_state\_updated / getLastScanned 返回为卡时)
|
||||
|
||||
- `cardId`:卡号
|
||||
- `ownerName`:持有人
|
||||
- `balance`:余额
|
||||
- `entered`:是否已进站
|
||||
- `entry_station`:进站站点 ID/代码(可能为空)
|
||||
- `passenger`:扫描者玩家名(仅检票机事件与 getLastScanned 返回中提供)
|
||||
@@ -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
|
||||
|
||||
|
||||
+2
-1
@@ -6,7 +6,8 @@ module.exports = {
|
||||
cors: {
|
||||
origin: "*",
|
||||
methods: ["GET", "POST"]
|
||||
}
|
||||
},
|
||||
transports: ['websocket']
|
||||
});
|
||||
|
||||
io.engine.on('connection_error', (err) => {
|
||||
|
||||
Reference in New Issue
Block a user