diff --git a/.idea/game-driver.iml b/.idea/game-driver.iml index 5e764c4..b279d48 100644 --- a/.idea/game-driver.iml +++ b/.idea/game-driver.iml @@ -1,6 +1,10 @@ - + + + + diff --git a/go.mod b/go.mod index b9cb8f0..8786da3 100644 --- a/go.mod +++ b/go.mod @@ -4,17 +4,18 @@ go 1.23.2 require ( github.com/aliyun/alibabacloud-nls-go-sdk v1.1.1 - github.com/eclipse/paho.golang v0.21.0 + github.com/eclipse/paho.golang v0.22.0 github.com/gopxl/beep/v2 v2.1.0 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 + github.com/warthog618/go-gpiocdev v0.9.1 go.bug.st/serial v1.6.2 go.uber.org/zap v1.27.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) require ( - github.com/aliyun/alibaba-cloud-sdk-go v1.63.51 // indirect + github.com/aliyun/alibaba-cloud-sdk-go v1.63.52 // indirect github.com/creack/goselect v0.1.2 // indirect github.com/ebitengine/oto/v3 v3.3.1 // indirect github.com/ebitengine/purego v0.8.1 // indirect diff --git a/go.sum b/go.sum index 7f61b5a..d2128b9 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/aliyun/alibaba-cloud-sdk-go v1.61.1376/go.mod h1:9CMdKNL3ynIGPpfTcdwTvIm8SGuAZYYC4jFVSSvE1YQ= -github.com/aliyun/alibaba-cloud-sdk-go v1.63.51 h1:ezfQdoOrqK7xa0+t8uw7ih2TFA/dm/bTH+U0/0Y++7g= -github.com/aliyun/alibaba-cloud-sdk-go v1.63.51/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= +github.com/aliyun/alibaba-cloud-sdk-go v1.63.52 h1:2qZQ6tiGuBqtaXd0rgsct29WxFzYyUKywg113mMP7QE= +github.com/aliyun/alibaba-cloud-sdk-go v1.63.52/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= github.com/aliyun/alibabacloud-nls-go-sdk v1.1.1 h1:LjItoNZuu5xHlsByFo+kr3nGa4LRIESCGWhfurayxBg= github.com/aliyun/alibabacloud-nls-go-sdk v1.1.1/go.mod h1:4BDMUKpEaP/Ct79w0ozR0nbnEj49g1k3mrgX/IKG5I4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -19,8 +19,8 @@ github.com/ebitengine/oto/v3 v3.3.1 h1:d4McwGQuXOT0GL7bA5g9ZnaUEIEjQvG3hafzMy+T3 github.com/ebitengine/oto/v3 v3.3.1/go.mod h1:MZeb/lwoC4DCOdiTIxYezrURTw7EvK/yF863+tmBI+U= github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE= github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= -github.com/eclipse/paho.golang v0.21.0 h1:cxxEReu+iFbA5RrHfRGxJOh8tXZKDywuehneoeBeyn8= -github.com/eclipse/paho.golang v0.21.0/go.mod h1:GHF6vy7SvDbDHBguaUpfuBkEB5G6j0zKxMG4gbh6QRQ= +github.com/eclipse/paho.golang v0.22.0 h1:JhhUngr8TBlyUZDZw/L6WVayPi9qmSmdWeki48i5AVE= +github.com/eclipse/paho.golang v0.22.0/go.mod h1:9ZiYJ93iEfGRJri8tErNeStPKLXIGBHiqbHV74t5pqI= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= @@ -120,6 +120,10 @@ github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaO github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/warthog618/go-gpiocdev v0.9.1 h1:pwHPaqjJfhCipIQl78V+O3l9OKHivdRDdmgXYbmhuCI= +github.com/warthog618/go-gpiocdev v0.9.1/go.mod h1:dN3e3t/S2aSNC+hgigGE/dBW8jE1ONk9bDSEYfoPyl8= +github.com/warthog618/go-gpiosim v0.1.1 h1:MRAEv+T+itmw+3GeIGpQJBfanUVyg0l3JCTwHtwdre4= +github.com/warthog618/go-gpiosim v0.1.1/go.mod h1:YXsnB+I9jdCMY4YAlMSRrlts25ltjmuIsrnoUrBLdqU= go.bug.st/serial v1.6.2 h1:kn9LRX3sdm+WxWKufMlIRndwGfPWsH1/9lCWXQCasq8= go.bug.st/serial v1.6.2/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= diff --git a/internal/common/device.go b/internal/common/device.go index d6d606b..df83884 100644 --- a/internal/common/device.go +++ b/internal/common/device.go @@ -3,7 +3,7 @@ package common import ( "context" "fmt" - "github.com/eclipse/paho.golang/autopaho" + "game-driver/pkg/utils" "github.com/eclipse/paho.golang/paho" "go.uber.org/zap" "sync" @@ -19,7 +19,6 @@ type DeviceMan interface { type Device struct { mu sync.Mutex C context.Context - cm *autopaho.ConnectionManager topic string status atomic.Int32 @@ -46,29 +45,27 @@ func (d *Device) Status() int { // PublishStatus 推送设备状态 func (d *Device) PublishStatus() { - err := d.cm.AwaitConnection(d.C) + err := utils.GlobalMqttClient.AwaitConnection(d.C) if err != nil { return } - _, _ = d.cm.Publish(d.C, &paho.Publish{ + _, _ = utils.GlobalMqttClient.Publish(d.C, &paho.Publish{ Topic: d.topic, Payload: []byte(fmt.Sprint(d.Status())), QoS: 1, }) } -func DefaultDevice(ctx context.Context, cm *autopaho.ConnectionManager, topic string) *Device { +func DefaultDevice(ctx context.Context, topic string) *Device { return &Device{ C: ctx, - cm: cm, topic: topic, } } -func NewDevice(ctx context.Context, cm *autopaho.ConnectionManager, topic string, onChange func()) *Device { +func NewDevice(ctx context.Context, topic string, onChange func()) *Device { return &Device{ C: ctx, - cm: cm, topic: topic, OnChange: onChange, } diff --git a/internal/routes/play.go b/internal/routes/play.go index 1d1da8a..120b3a0 100644 --- a/internal/routes/play.go +++ b/internal/routes/play.go @@ -18,6 +18,9 @@ func switchPoint(point int) leaf.HandlerFunc { switch point { case 2: // 镇水塔点位 return play.OnlyVideo + case 4: + // 4号点位(发卡机) + return play.NewPushCard() default: return play.Default } diff --git a/internal/routes/play/default.go b/internal/routes/play/default.go index 523015f..7e92ca1 100644 --- a/internal/routes/play/default.go +++ b/internal/routes/play/default.go @@ -13,7 +13,7 @@ func Default(c *leaf.Context) { if w, ok := payload.Game["wait"]; ok { select { case <-c.Done(): - case <-time.After(time.Duration(w.(int)) * time.Second): + case <-time.After(time.Duration(w.(float64)) * time.Second): } } } diff --git a/internal/routes/play/push_card.go b/internal/routes/play/push_card.go new file mode 100644 index 0000000..b726d23 --- /dev/null +++ b/internal/routes/play/push_card.go @@ -0,0 +1,151 @@ +package play + +import ( + "fmt" + "game-driver/internal/middleware" + "game-driver/internal/schema" + "game-driver/leaf" + "game-driver/pkg/utils" + "github.com/warthog618/go-gpiocdev" + "github.com/warthog618/go-gpiocdev/device/rpi" + "go.uber.org/zap" + "sync" + "time" +) + +var gpios = []int{rpi.GPIO6, rpi.GPIO13, rpi.GPIO19, rpi.GPIO26} + +type label int + +func (t label) String() string { + switch t { + case rpi.GPIO6: + return "OutOK" + case rpi.GPIO13: + return "Lower" + case rpi.GPIO19: + return "Error" + case rpi.GPIO26: + return "Empty" + default: + return fmt.Sprint(int(t)) + } +} + +type statusData map[int]int + +func (data statusData) Init(offsets []int, values []int) { + for i := 0; i < len(offsets); i++ { + data[offsets[i]] = values[i] + } + zap.S().Infof( + "初始状态: %s-%d %s-%d %s-%d %s-%d", + label(rpi.GPIO6), data[rpi.GPIO6], + label(rpi.GPIO13), data[rpi.GPIO13], + label(rpi.GPIO19), data[rpi.GPIO19], + label(rpi.GPIO26), data[rpi.GPIO26], + ) +} + +func (data statusData) Change(offset int, value gpiocdev.LineEventType) { + if v, ok := data[offset]; ok { + if value == gpiocdev.LineEventFallingEdge { + if v == 0 { + return + } else { + zap.S().Infof("%s: 0", label(offset)) + data[offset] = 0 + } + } else if value == gpiocdev.LineEventRisingEdge { + if v == 1 { + return + } else { + zap.S().Infof("%s: 1", label(offset)) + data[offset] = 1 + } + } + } +} + +func NewPushCard() leaf.HandlerFunc { + chip, err := gpiocdev.NewChip("gpiochip0", gpiocdev.AsActiveLow) + if err != nil { + zap.S().Panicln("打开 GPIO 设备失败:", err) + } + go func() { + <-utils.GlobalMqttClient.Done() + chip.Close() + }() + + status := make(statusData) + + lines, err := chip.RequestLines( + gpios, + gpiocdev.AsInput, // 请求引脚作为输入 + gpiocdev.WithPullUp, // 使用上拉电阻 + gpiocdev.WithRealtimeEventClock, // 使用实时时钟 + gpiocdev.WithBothEdges, // 监听上升沿和下降沿 + gpiocdev.WithEventHandler(func(evt gpiocdev.LineEvent) { + status.Change(evt.Offset, evt.Type) + }), + ) + if err != nil { + zap.S().Panicln("请求引脚作为输入信号失败:", err) + } + go func() { + <-utils.GlobalMqttClient.Done() + lines.Close() + }() + + // 读取引脚初始状态 + rr := make([]int, len(gpios), len(gpios)) + lines.Values(rr) + status.Init(gpios, rr) + + // 初始化发卡引脚 + payout, err := chip.RequestLine(rpi.GPIO11, gpiocdev.AsOutput(0)) + if err != nil { + zap.S().Panicf("请求引脚 %d 作为 PayOut 失败: %s\n", rpi.GPIO11, err) + } + go func() { + <-utils.GlobalMqttClient.Done() + payout.Close() + }() + + return func(c *leaf.Context) { + payload := leaf.Value[*schema.PlayModal](c, middleware.PayloadJSONKey) + + var action time.Duration + if a, ok := payload.Game["action"]; ok { + action = time.Duration(a.(float64)) + } + + // 等待组 + var wait sync.WaitGroup + defer wait.Wait() + + // 结束信号通道 + a := make(chan struct{}) + // 发送结束信号 + defer close(a) + + wait.Add(1) + go func() { + defer wait.Done() + select { + case <-a: + case <-time.After(action * time.Second): + // 将输出引脚设置为活动,发卡信号 + payout.SetValue(1) + // 恢复为未活动,停止发卡信号 + defer payout.SetValue(0) + + // 信号持续 500ms + time.Sleep(500 * time.Millisecond) + } + }() + + // 执行等待信号 + Default(c) + } +} diff --git a/internal/server.go b/internal/server.go index 0443ee6..21beb92 100644 --- a/internal/server.go +++ b/internal/server.go @@ -102,6 +102,7 @@ func Run() { if err != nil { zap.S().Panicln("连接 MQTT 异常: ", err) } + utils.GlobalMqttClient = cm // 构建语音合成对象 tts.DefaultTTS = tts.New(ctx, config.C.Aliyun) @@ -116,7 +117,7 @@ func Run() { //defer r.Close() // 构建全局设备变量 - device := common.DefaultDevice(ctx, cm, publishTopic) + device := common.DefaultDevice(ctx, publishTopic) // 设备状态变化 device.OnChange = func() { device.PublishStatus() } diff --git a/pkg/utils/mqtt.go b/pkg/utils/mqtt.go new file mode 100644 index 0000000..1c495b1 --- /dev/null +++ b/pkg/utils/mqtt.go @@ -0,0 +1,5 @@ +package utils + +import "github.com/eclipse/paho.golang/autopaho" + +var GlobalMqttClient *autopaho.ConnectionManager