- interval: 添加 Sleep 避免默认分支的忙循环(CPU 100%) - cron: 使用 context.Background() 确保定时任务完整执行,不受外部取消影响 - wait_card: 使用 context.Background() 确保读卡器监听完整执行 这些修复确保了关键操作能够完整运行,同时避免 CPU 资源浪费。
122 lines
2.7 KiB
Go
122 lines
2.7 KiB
Go
package play
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"game-driver/config/game"
|
||
"game-driver/internal/middleware"
|
||
"game-driver/internal/schema"
|
||
"game-driver/leaf"
|
||
"game-driver/pkg/card_reader"
|
||
"game-driver/pkg/channel"
|
||
"game-driver/pkg/tts"
|
||
"io/fs"
|
||
"os"
|
||
"sync"
|
||
"time"
|
||
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
func WaitCard(ctx context.Context) leaf.HandlerFunc {
|
||
g := (game.C).(game.ConfigWait)
|
||
|
||
reader, err := card_reader.NewReader(g.Addr)
|
||
if err != nil {
|
||
if errors.Is(err, fs.ErrNotExist) {
|
||
zap.S().Errorf("读卡器串口文件不存在: %s", g.Addr)
|
||
os.Exit(1)
|
||
}
|
||
zap.S().Panicf("读卡器串口连接失败 [%T]: %q", err, err)
|
||
}
|
||
go func() {
|
||
<-ctx.Done()
|
||
_ = reader.Close()
|
||
}()
|
||
|
||
err = reader.Init()
|
||
if err != nil {
|
||
zap.S().Panicln("读卡器初始化失败", err)
|
||
}
|
||
|
||
return func(c *leaf.Context) {
|
||
payload := leaf.Value[*schema.PlayModal](c, middleware.PayloadJSONKey)
|
||
|
||
// 读取卡片等待时间
|
||
var waitCard time.Duration
|
||
if a, ok := payload.Game["wait_card"]; ok {
|
||
if v, ok := a.(float64); ok {
|
||
waitCard = time.Duration(v)
|
||
}
|
||
}
|
||
// 卡片ID预期值
|
||
var cardId string
|
||
if a, ok := payload.Game["card_id"]; ok {
|
||
if v, ok := a.(string); ok {
|
||
cardId = v
|
||
}
|
||
}
|
||
// 卡片比对成功语音内容
|
||
var cardOk string
|
||
if a, ok := payload.Game["card_ok"]; ok {
|
||
if v, ok := a.(string); ok {
|
||
cardOk = v
|
||
}
|
||
}
|
||
// 卡片比对失败语音内容
|
||
var cardError string
|
||
if a, ok := payload.Game["card_error"]; ok {
|
||
if v, ok := a.(string); ok {
|
||
cardError = v
|
||
}
|
||
}
|
||
|
||
// 等待组
|
||
var wait sync.WaitGroup
|
||
defer wait.Wait()
|
||
|
||
// 卡片信息通道
|
||
cardInfo := channel.NewClosed[string]()
|
||
defer cardInfo.Close()
|
||
|
||
// 结束信号通道
|
||
// 使用独立 context 确保读卡器监听完整执行,不受外部取消影响
|
||
c2, cancel := context.WithCancel(context.Background())
|
||
defer cancel()
|
||
|
||
wait.Add(1)
|
||
go func() {
|
||
defer wait.Done()
|
||
reader.OnCardInfo(c2, func(info *card_reader.CardInfo) {
|
||
cardInfo.Send(info.ID)
|
||
})
|
||
}()
|
||
|
||
// 多次读取,直到读取到正确的卡片
|
||
for isNeed := true; isNeed; {
|
||
isNeed = false
|
||
|
||
select {
|
||
case <-c.Done():
|
||
case <-time.After(waitCard * time.Second):
|
||
case id, ok := <-cardInfo.Data(): // 等待卡片插入
|
||
if ok { // 非关闭信号
|
||
// 比对卡号是否正确,不正确则重新读取
|
||
if cardId != id {
|
||
zap.S().Infof("读取到卡片数据%q,与预期卡片数据%q不一致", id, cardId)
|
||
// 播报错误提示
|
||
tts.DefaultTTS.Sound(c, cardError)
|
||
isNeed = true
|
||
break
|
||
}
|
||
// 播报恭喜语音
|
||
tts.DefaultTTS.Sound(c, cardOk)
|
||
//TODO: 打开炫酷光效,屏幕跳转恭喜页面
|
||
zap.S().Infof("读取到卡片数据%q,开始打开炫酷光效", id)
|
||
Default(c)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|