Merge branch 'refs/heads/main' into clean_beep
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,6 @@
|
||||
/logs
|
||||
/.idea
|
||||
/.vscode
|
||||
|
||||
*.mp3
|
||||
game-driver*
|
||||
24
cmd/root.go
24
cmd/root.go
@@ -4,8 +4,11 @@ Copyright © 2024 慕枫Go <mapleafgo@163.com>
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"game-driver/config"
|
||||
"game-driver/config/game"
|
||||
"game-driver/internal"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
@@ -45,22 +48,35 @@ func Execute() {
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "config.yml", "默认当前目录下的config.yml")
|
||||
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "config.yml", "默认当前目录下的 config.yml")
|
||||
}
|
||||
|
||||
// initConfig reads in config file and ENV variables if set.
|
||||
func initConfig() {
|
||||
viper.SetConfigFile(cfgFile)
|
||||
|
||||
viper.AutomaticEnv() // read in environment variables that match
|
||||
|
||||
// If a config file is found, read it in.
|
||||
if err := viper.ReadInConfig(); err == nil {
|
||||
log.Printf("Using config file: %s", viper.ConfigFileUsed())
|
||||
} else if errors.Is(err, fs.ErrNotExist) {
|
||||
log.Printf("无法找到主配置文件: %s", viper.ConfigFileUsed())
|
||||
os.Exit(1)
|
||||
} else {
|
||||
log.Panicln("read config file error: ", err)
|
||||
}
|
||||
|
||||
err := viper.Unmarshal(&config.C)
|
||||
if err != nil {
|
||||
log.Panicln("unmarshal config failed: ", err)
|
||||
}
|
||||
|
||||
// 初始化游戏节点配置
|
||||
game.G = game.NewConfig(config.C.Point)
|
||||
if game.G != nil { // 如果需要游戏配置
|
||||
err = viper.UnmarshalKey("game", &game.G)
|
||||
if err != nil {
|
||||
log.Panicln("unmarshal game config failed: ", err)
|
||||
}
|
||||
} else {
|
||||
log.Panicln("game config not found")
|
||||
}
|
||||
}
|
||||
|
||||
25
config.yml
25
config.yml
@@ -1,6 +1,7 @@
|
||||
location: wushan
|
||||
point: 0
|
||||
relay: COM7
|
||||
point: 4
|
||||
relay: /dev/ttyUSB1
|
||||
maxTimeout: 60 # 单位 s,必须大于 0
|
||||
log:
|
||||
level: debug
|
||||
file:
|
||||
@@ -16,14 +17,16 @@ aliyun:
|
||||
accessKeySecret:
|
||||
appKey:
|
||||
timeout: 10 # 单位 s
|
||||
voice: zhifeng_emo
|
||||
game:
|
||||
maxTimeout: 60 # 单位 s
|
||||
cardGroups:
|
||||
# addr: /dev/ttyUSB0 # 点位 5 的串口地址
|
||||
pushGroups: # 点位 4 的发卡器配置
|
||||
- name: gpiochip0
|
||||
outOK: 6
|
||||
lower: 13
|
||||
error: 19
|
||||
empty: 26
|
||||
push: 11
|
||||
reset: 22
|
||||
pull: 27
|
||||
outOK: 12
|
||||
lower: 1
|
||||
error: 7
|
||||
empty: 8
|
||||
push: 0
|
||||
reset: 5
|
||||
pull: 6
|
||||
readAddr: /dev/ttyUSB0 # 点位 4 的读卡器配置
|
||||
@@ -1,7 +1,6 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"game-driver/internal/routes/play/card_device"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
@@ -14,11 +13,7 @@ type AliyunConfig struct {
|
||||
AccessKeySecret string
|
||||
AppKey string
|
||||
Timeout int
|
||||
}
|
||||
|
||||
type GameConfig struct {
|
||||
MaxTimeout int
|
||||
CardGroups []*card_device.LineGroup
|
||||
Voice string
|
||||
}
|
||||
|
||||
type Logger struct {
|
||||
@@ -27,13 +22,13 @@ type Logger struct {
|
||||
}
|
||||
|
||||
type config struct {
|
||||
Location string
|
||||
Point int
|
||||
Relay string
|
||||
Log Logger
|
||||
Mqtt MqttConfig
|
||||
Aliyun AliyunConfig
|
||||
Game GameConfig
|
||||
Location string
|
||||
Point int
|
||||
Relay string
|
||||
Log Logger
|
||||
Mqtt MqttConfig
|
||||
Aliyun AliyunConfig
|
||||
MaxTimeout int
|
||||
}
|
||||
|
||||
var C config
|
||||
|
||||
16
config/game/game.go
Normal file
16
config/game/game.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package game
|
||||
|
||||
type Config any
|
||||
|
||||
func NewConfig(point int) Config {
|
||||
switch point {
|
||||
case 4:
|
||||
return ConfigPush{}
|
||||
case 5:
|
||||
return ConfigWait{}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var G Config
|
||||
8
config/game/push_card.go
Normal file
8
config/game/push_card.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package game
|
||||
|
||||
import "game-driver/internal/routes/play/card_pusher"
|
||||
|
||||
type ConfigPush struct {
|
||||
PushGroups []*card_pusher.LineGroup
|
||||
ReadAddr string
|
||||
}
|
||||
5
config/game/wait_card.go
Normal file
5
config/game/wait_card.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package game
|
||||
|
||||
type ConfigWait struct {
|
||||
Addr string
|
||||
}
|
||||
48
demo/gpio2/main.go
Normal file
48
demo/gpio2/main.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"game-driver/internal/routes/play/card_pusher"
|
||||
"game-driver/logger"
|
||||
"github.com/warthog618/go-gpiocdev/device/rpi"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
logger.DefaultLogger()
|
||||
defer logger.Sync()
|
||||
|
||||
device, err := card_pusher.New(&card_pusher.LineGroup{
|
||||
Name: "gpiochip0",
|
||||
OutOK: rpi.GPIO6,
|
||||
Lower: rpi.GPIO13,
|
||||
Error: rpi.GPIO19,
|
||||
Empty: rpi.GPIO26,
|
||||
Push: rpi.GPIO11,
|
||||
Reset: rpi.GPIO22,
|
||||
Pull: rpi.GPIO27,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println("打开 GPIO 设备失败:", err)
|
||||
return
|
||||
}
|
||||
defer device.Close()
|
||||
|
||||
for {
|
||||
var userInput string
|
||||
fmt.Println("按 o/p/r 发送信号")
|
||||
_, _ = fmt.Scanln(&userInput)
|
||||
|
||||
if userInput == "o" {
|
||||
device.PushCard()
|
||||
}
|
||||
if userInput == "p" {
|
||||
device.PullCard()
|
||||
}
|
||||
if userInput == "r" {
|
||||
device.Reset()
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
41
demo/modbus2/main.go
Normal file
41
demo/modbus2/main.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"game-driver/logger"
|
||||
"game-driver/pkg/card_reader"
|
||||
"go.uber.org/zap"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func main() {
|
||||
logger.DefaultLogger()
|
||||
defer logger.Sync()
|
||||
|
||||
reader, err := card_reader.NewReader("/dev/ttyUSB0")
|
||||
if err != nil {
|
||||
zap.S().Panicln(err)
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
err = reader.Init()
|
||||
if err != nil {
|
||||
zap.S().Panicln(err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go reader.OnCardInfo(ctx, func(info *card_reader.CardInfo) {
|
||||
zap.S().Infow("Card info", "Type", fmt.Sprintf("%#x", info.Type), "ID", info.ID)
|
||||
})
|
||||
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
<-sig
|
||||
zap.S().Infoln("接收到关闭命令 - 正在关闭程序")
|
||||
cancel()
|
||||
zap.S().Infoln("关闭完成")
|
||||
}
|
||||
41
demo/relay/main.go
Normal file
41
demo/relay/main.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"game-driver/logger"
|
||||
"game-driver/pkg/relay"
|
||||
"go.uber.org/zap"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
logger.DefaultLogger()
|
||||
defer logger.Sync()
|
||||
|
||||
r, err := relay.New("/dev/ttyUSB0")
|
||||
if err != nil {
|
||||
zap.S().Panicln(err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
func(num int) {
|
||||
r.On(num)
|
||||
defer r.Off(num)
|
||||
time.Sleep(1 * time.Second)
|
||||
}(i)
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
r.OnAll()
|
||||
defer r.OffAll()
|
||||
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
<-sig
|
||||
zap.S().Infoln("接收到关闭命令 - 正在关闭程序")
|
||||
zap.S().Infoln("关闭完成")
|
||||
}
|
||||
BIN
docs/继电器LH-04.zip
Executable file
BIN
docs/继电器LH-04.zip
Executable file
Binary file not shown.
BIN
docs/读卡器YMC1701&1702资料V1.rar
Normal file
BIN
docs/读卡器YMC1701&1702资料V1.rar
Normal file
Binary file not shown.
27
game.md
27
game.md
@@ -22,13 +22,24 @@
|
||||
```json lines
|
||||
{
|
||||
// 播放文件地址,支持 file:// 本地文件地址、 http(s):// 远程文件地址
|
||||
"video": "",
|
||||
"video": ""
|
||||
}
|
||||
```
|
||||
|
||||
## 神女除妖(3)
|
||||
|
||||
除妖处不需要任何额外的处理,待游戏自然完成即可
|
||||
参照 [请求响应文档](https://www.emqx.com/zh/blog/mqtt5-request-response),
|
||||
发送附带`ResponseTopic`,并订阅`ResponseTopic`,才能接收到响应结果
|
||||
|
||||
### RequestPayload
|
||||
|
||||
```json lines
|
||||
{}
|
||||
```
|
||||
|
||||
### ResponsePayload
|
||||
|
||||
暂时还未对接除妖设备,不知道返回的数据结构是什么
|
||||
|
||||
```json lines
|
||||
{}
|
||||
@@ -36,8 +47,8 @@
|
||||
|
||||
## 神女授书(4)
|
||||
|
||||
参照 [请求响应文档](https://www.emqx.com/zh/blog/mqtt5-request-response),发送附带`ResponseTopic`,并订阅`ResponseTopic`
|
||||
,才能接收到响应结果
|
||||
参照 [请求响应文档](https://www.emqx.com/zh/blog/mqtt5-request-response),
|
||||
发送附带`ResponseTopic`,并订阅`ResponseTopic`,才能接收到响应结果
|
||||
|
||||
### RequestPayload
|
||||
|
||||
@@ -54,6 +65,8 @@
|
||||
|
||||
```json lines
|
||||
{
|
||||
// 发出的卡片ID
|
||||
"card_id": "",
|
||||
// 空卡设备数量
|
||||
"empty": 0,
|
||||
// 错误设备数量
|
||||
@@ -71,6 +84,12 @@
|
||||
|
||||
```json lines
|
||||
{
|
||||
// 需要的卡片ID
|
||||
"card_id": "",
|
||||
// 卡片错误时的播报内容
|
||||
"card_error": "",
|
||||
// 卡片正确时播报的恭喜通关内容
|
||||
"card_ok": "",
|
||||
// 等待插卡时间,单位秒
|
||||
"wait_card": 0,
|
||||
// 插卡后持续时间,单位秒
|
||||
|
||||
4
go.mod
4
go.mod
@@ -6,21 +6,21 @@ require (
|
||||
github.com/aliyun/alibabacloud-nls-go-sdk v1.1.1
|
||||
github.com/eclipse/paho.golang v0.22.0
|
||||
github.com/gopxl/beep/v2 v2.1.0
|
||||
github.com/grid-x/modbus v0.0.0-20241004123532-f6c6fb5201b3
|
||||
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.53 // 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
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/grid-x/serial v0.0.0-20211107191517-583c7356b3aa // indirect
|
||||
github.com/hajimehoshi/go-mp3 v0.3.4 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
|
||||
12
go.sum
12
go.sum
@@ -3,15 +3,11 @@ 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.52 h1:2qZQ6tiGuBqtaXd0rgsct29WxFzYyUKywg113mMP7QE=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.52/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.53 h1:I93ILTm5ytF4e5+lEQXSXcydS26D9eVyJ4H6z3rJqMA=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.53/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=
|
||||
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
|
||||
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -41,6 +37,10 @@ github.com/gopxl/beep/v2 v2.1.0/go.mod h1:sQvj2oSsu8fmmDWH3t0DzIe0OZzTW6/TJEHW4K
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grid-x/modbus v0.0.0-20241004123532-f6c6fb5201b3 h1:TfBJ561lUg0i0GLsxKeRaWoBGN8nyCLNt0OMGRx7R2M=
|
||||
github.com/grid-x/modbus v0.0.0-20241004123532-f6c6fb5201b3/go.mod h1:WpbUAyptAAi0VAriSRopZa6uhiJOJCTz7KFvgGtNRXc=
|
||||
github.com/grid-x/serial v0.0.0-20211107191517-583c7356b3aa h1:Rsn6ARgNkXrsXJIzhkE4vQr5Gbx2LvtEMv4BJOK4LyU=
|
||||
github.com/grid-x/serial v0.0.0-20211107191517-583c7356b3aa/go.mod h1:kdOd86/VGFWRrtkNwf1MPk0u1gIjc4Y7R2j7nhwc7Rk=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo=
|
||||
github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo=
|
||||
@@ -126,8 +126,6 @@ github.com/warthog618/go-gpiocdev v0.9.1 h1:pwHPaqjJfhCipIQl78V+O3l9OKHivdRDdmgX
|
||||
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=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
@@ -193,4 +191,6 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw=
|
||||
pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
||||
@@ -53,7 +53,7 @@ func PlayBgm() leaf.HandlerFunc {
|
||||
// }
|
||||
//}()
|
||||
} else {
|
||||
zap.S().Errorln("背景音乐解析为空")
|
||||
zap.S().Infoln("未解析到背景音乐")
|
||||
}
|
||||
|
||||
c.Next()
|
||||
|
||||
@@ -8,12 +8,12 @@ import (
|
||||
)
|
||||
|
||||
// RelayMaster 继电器中间件
|
||||
func RelayMaster(r *relay.Device) leaf.HandlerFunc {
|
||||
func RelayMaster(r relay.Relay) leaf.HandlerFunc {
|
||||
return func(c *leaf.Context) {
|
||||
pm := leaf.Value[*schema.PlayModal](c, PayloadJSONKey)
|
||||
if r != nil && pm.Power {
|
||||
r.On(1)
|
||||
defer r.Off(1)
|
||||
r.On(0)
|
||||
defer r.Off(0)
|
||||
|
||||
zap.S().Infoln("开启电源")
|
||||
defer zap.S().Infoln("关闭电源")
|
||||
|
||||
@@ -24,7 +24,7 @@ func switchPoint(ctx context.Context, point int) leaf.HandlerFunc {
|
||||
return play.PushCard(ctx)
|
||||
case 5:
|
||||
// 5号点位(等待插卡)
|
||||
return play.WaitCard
|
||||
return play.WaitCard(ctx)
|
||||
default:
|
||||
return play.Default
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package card_device
|
||||
package card_pusher
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,4 +1,4 @@
|
||||
package card_device
|
||||
package card_pusher
|
||||
|
||||
type LineGroup struct {
|
||||
Name string
|
||||
@@ -1,4 +1,4 @@
|
||||
package card_device
|
||||
package card_pusher
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
@@ -3,32 +3,63 @@ package play
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"game-driver/config"
|
||||
"errors"
|
||||
"game-driver/config/game"
|
||||
"game-driver/internal/middleware"
|
||||
"game-driver/internal/routes/play/card_device"
|
||||
"game-driver/internal/routes/play/card_pusher"
|
||||
"game-driver/internal/schema"
|
||||
"game-driver/leaf"
|
||||
"game-driver/pkg/card_reader"
|
||||
"game-driver/pkg/utils"
|
||||
"github.com/eclipse/paho.golang/paho"
|
||||
"go.uber.org/zap"
|
||||
"io/fs"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ResponseBody struct {
|
||||
Empty int `json:"empty"`
|
||||
Error int `json:"error"`
|
||||
OutOk int `json:"out_ok"`
|
||||
num int
|
||||
CardId string `json:"card_id"`
|
||||
Empty int `json:"empty"`
|
||||
Error int `json:"error"`
|
||||
OutOk int `json:"out_ok"`
|
||||
num int
|
||||
}
|
||||
|
||||
func PushCard(ctx context.Context) leaf.HandlerFunc {
|
||||
devices := make([]*card_device.Device, 0)
|
||||
for _, group := range config.C.Game.CardGroups {
|
||||
g := (game.G).(game.ConfigPush)
|
||||
|
||||
// 开始初始化读卡器
|
||||
reader, err := card_reader.NewReader(g.ReadAddr)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
zap.S().Errorf("读卡器串口文件不存在: %s", g.ReadAddr)
|
||||
os.Exit(1)
|
||||
}
|
||||
zap.S().Panicf("读卡器串口连接失败 [%T]: %q", err, err)
|
||||
}
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
_ = reader.Close()
|
||||
}()
|
||||
|
||||
// 开始初始化发卡器
|
||||
devices := make([]*card_pusher.Device, 0)
|
||||
for i, group := range g.PushGroups {
|
||||
// 对读卡器初始化配置
|
||||
reader.SetSlave(byte(i + 1))
|
||||
err = reader.Init()
|
||||
if err != nil {
|
||||
zap.S().Panicln("读卡器初始配置失败", err)
|
||||
}
|
||||
|
||||
// 解析发卡器配置
|
||||
gv, _ := json.Marshal(group)
|
||||
zap.S().Info("发卡器指针:", string(gv))
|
||||
|
||||
device, err := card_device.New(group)
|
||||
// 初始化发卡器
|
||||
device, err := card_pusher.New(group)
|
||||
if err != nil {
|
||||
zap.S().Panicln("初始化发卡器失败: ", err)
|
||||
}
|
||||
@@ -81,6 +112,13 @@ func PushCard(ctx context.Context) leaf.HandlerFunc {
|
||||
// 延迟1秒获取结果并发送消息
|
||||
time.AfterFunc(time.Second, func() {
|
||||
if body.num != 0 {
|
||||
// 若卡片就位,读取卡片信息
|
||||
if devices[body.num-1].GetOutOk() == 1 {
|
||||
reader.SetSlave(byte(body.num))
|
||||
if info := reader.GetCardInfo(); info != nil {
|
||||
body.CardId = info.ID
|
||||
}
|
||||
}
|
||||
body.OutOk += devices[body.num-1].GetOutOk()
|
||||
}
|
||||
publishBody(ctx, c.Properties.ResponseTopic, body)
|
||||
|
||||
@@ -1,43 +1,119 @@
|
||||
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"
|
||||
"go.uber.org/zap"
|
||||
"io/fs"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func WaitCard(c *leaf.Context) {
|
||||
payload := leaf.Value[*schema.PlayModal](c, middleware.PayloadJSONKey)
|
||||
func WaitCard(ctx context.Context) leaf.HandlerFunc {
|
||||
g := (game.G).(game.ConfigWait)
|
||||
|
||||
var waitCard time.Duration
|
||||
if a, ok := payload.Game["wait_card"]; ok {
|
||||
if v, ok := a.(float64); ok {
|
||||
waitCard = time.Duration(v)
|
||||
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)
|
||||
}
|
||||
|
||||
// 等待组
|
||||
var wait sync.WaitGroup
|
||||
defer wait.Wait()
|
||||
|
||||
a := make(chan string)
|
||||
defer close(a)
|
||||
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
defer wait.Done()
|
||||
time.Sleep(3 * time.Second)
|
||||
a <- "卡片数据"
|
||||
<-ctx.Done()
|
||||
_ = reader.Close()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-c.Done():
|
||||
case <-time.After(waitCard * time.Second):
|
||||
case _, ok := <-a: // 等待卡片插入
|
||||
if ok { // 非关闭信号
|
||||
Default(c)
|
||||
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()
|
||||
|
||||
// 结束信号通道
|
||||
cc, cancel := context.WithCancel(context.TODO())
|
||||
defer cancel()
|
||||
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
defer wait.Done()
|
||||
reader.OnCardInfo(cc, 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(cardError)
|
||||
isNeed = true
|
||||
break
|
||||
}
|
||||
// 播报恭喜语音
|
||||
tts.DefaultTTS.Sound(cardOk)
|
||||
//TODO: 打开炫酷光效,屏幕跳转恭喜页面
|
||||
zap.S().Infof("读取到卡片数据%q,开始打开炫酷光效", id)
|
||||
Default(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,12 +175,12 @@ func relayAction(c *leaf.Context, item schema.WaitItemModel, root schema.TimeMod
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
device, err := relay.New(item.Data, nil)
|
||||
r, err := relay.New(item.Data)
|
||||
if err != nil {
|
||||
zap.S().Errorln("继电器初始化异常: ", err)
|
||||
return
|
||||
}
|
||||
defer device.Close()
|
||||
defer r.Close()
|
||||
|
||||
select {
|
||||
case <-c.Done():
|
||||
@@ -189,9 +189,9 @@ func relayAction(c *leaf.Context, item schema.WaitItemModel, root schema.TimeMod
|
||||
zap.S().Infoln("待机继电器供电")
|
||||
defer zap.S().Infoln("待机继电器断电")
|
||||
|
||||
device.On(1)
|
||||
r.On(0)
|
||||
<-c.Done()
|
||||
device.Off(1)
|
||||
r.Off(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"game-driver/internal/schema"
|
||||
"game-driver/leaf"
|
||||
"game-driver/logger"
|
||||
"game-driver/pkg/relay"
|
||||
"game-driver/pkg/tts"
|
||||
"game-driver/pkg/utils"
|
||||
"github.com/eclipse/paho.golang/autopaho"
|
||||
@@ -108,13 +109,11 @@ func Run() {
|
||||
tts.DefaultTTS = tts.New(ctx, config.C.Aliyun)
|
||||
|
||||
// 构建继电器对象
|
||||
//r, err := relay.New(config.C.Relay, func(msg string) {
|
||||
// zap.S().Infoln("串口返回: ", msg)
|
||||
//})
|
||||
//if err != nil {
|
||||
// zap.S().Panicln("串口连接异常: ", err)
|
||||
//}
|
||||
//defer r.Close()
|
||||
r, err := relay.New(config.C.Relay)
|
||||
if err != nil {
|
||||
zap.S().Panicln("串口连接异常: ", err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
// 构建全局设备变量
|
||||
device := common.DefaultDevice(ctx, publishTopic)
|
||||
@@ -129,7 +128,7 @@ func Run() {
|
||||
middleware.EmergencyStop(common.GlobalStopper),
|
||||
middleware.SoundStart(),
|
||||
middleware.RelayMaster(nil),
|
||||
middleware.TimeoutOver(config.C.Game.MaxTimeout),
|
||||
middleware.TimeoutOver(config.C.MaxTimeout),
|
||||
middleware.TickerAction(),
|
||||
middleware.PlayBgm(),
|
||||
routes.PlayRouter(ctx, config.C.Location, config.C.Point),
|
||||
|
||||
155
pkg/card_reader/reader.go
Normal file
155
pkg/card_reader/reader.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package card_reader
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/grid-x/modbus"
|
||||
"go.uber.org/zap"
|
||||
"io"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Reader interface {
|
||||
io.Closer
|
||||
// SetSlave 设置读卡器的 UnitID
|
||||
SetSlave(byte)
|
||||
// WriteUnitId 写入读卡器的 UnitID
|
||||
WriteUnitId(uint16) error
|
||||
// Init 初始化读卡器配置
|
||||
Init() error
|
||||
// OnCardInfo 会在卡号信息发生变化时调用回调函数,直到上下文被取消,或者 Reader 被关闭。需要在 goroutine 中调用。
|
||||
OnCardInfo(context.Context, func(*CardInfo))
|
||||
GetCardInfo() *CardInfo
|
||||
}
|
||||
|
||||
type CardInfo struct {
|
||||
Type []byte
|
||||
ID string
|
||||
}
|
||||
|
||||
type reader struct {
|
||||
h modbus.ClientHandler
|
||||
c modbus.Client
|
||||
}
|
||||
|
||||
// WriteUnitId 写入读卡器的 UnitID
|
||||
func (r *reader) WriteUnitId(id uint16) error {
|
||||
v := id<<8 + 1
|
||||
zap.S().Infof("WriteRegister 40007 %#x", v)
|
||||
_, err := r.c.WriteSingleRegister(6, v)
|
||||
return err
|
||||
}
|
||||
|
||||
// Init 初始化读卡器配置
|
||||
func (r *reader) Init() error {
|
||||
// 配置读卡间隔时间为 50 毫秒
|
||||
_, err := r.c.WriteSingleRegister(8, 0x0511)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 由低到高输出 500 毫秒
|
||||
_, err = r.c.WriteSingleRegister(9, 0xF20A)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 读取后自动清空,高字节:00 不清空 01 清空
|
||||
_, err = r.c.WriteSingleRegister(10, 0x0103)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 自动读卡号,高字节高4位:0 读一次 1 连续读
|
||||
_, err = r.c.WriteSingleRegister(11, 0x0301)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCardInfo 获取卡号信息
|
||||
func (r *reader) GetCardInfo() *CardInfo {
|
||||
// 读取状态寄存器确认是否信息已经准备好
|
||||
status, err := r.c.ReadHoldingRegisters(29, 1)
|
||||
if err != nil {
|
||||
zap.S().Errorln("ReadRegister 40030 error:", err)
|
||||
return nil
|
||||
} else if len(status) != 2 || status[1] == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 读取卡号长度
|
||||
cardLength, err := r.c.ReadHoldingRegisters(30, 1)
|
||||
if err != nil {
|
||||
zap.S().Errorln("ReadRegister 40031 error:", err)
|
||||
return nil
|
||||
}
|
||||
dataLength := cardLength[0]
|
||||
|
||||
// 读取卡类型
|
||||
cardType, err := r.c.ReadHoldingRegisters(31, 1)
|
||||
if err != nil {
|
||||
zap.S().Errorln("ReadRegister 40032 error:", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 读取卡号数据
|
||||
cardData, err := r.c.ReadHoldingRegisters(32, uint16(math.Round(float64(dataLength)/2)))
|
||||
if err != nil {
|
||||
zap.S().Errorln("ReadRegister 40033~ error:", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
s := make([]string, dataLength)
|
||||
for i := 0; i < int(dataLength); i++ {
|
||||
s[i] = fmt.Sprintf("%02X", cardData[i])
|
||||
}
|
||||
|
||||
return &CardInfo{
|
||||
Type: cardType,
|
||||
ID: strings.Join(s, ""),
|
||||
}
|
||||
}
|
||||
|
||||
// OnCardInfo 会在卡号信息发生变化时调用回调函数,直到上下文被取消,或者 Reader 被关闭。需要在 goroutine 中调用。
|
||||
func (r *reader) OnCardInfo(ctx context.Context, f func(info *CardInfo)) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
info := r.GetCardInfo()
|
||||
if info != nil {
|
||||
f(info)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reader) SetSlave(unitID byte) {
|
||||
r.h.SetSlave(unitID)
|
||||
}
|
||||
|
||||
func (r *reader) Close() error {
|
||||
return r.h.Close()
|
||||
}
|
||||
|
||||
// NewReader 创建一个新的读卡器
|
||||
func NewReader(address string) (Reader, error) {
|
||||
// 配置串口客户端
|
||||
h := modbus.NewRTUClientHandler(address)
|
||||
h.SlaveID = 1
|
||||
h.BaudRate = 9600
|
||||
h.DataBits = 8
|
||||
h.Parity = "N"
|
||||
h.StopBits = 1
|
||||
|
||||
// 连接串口
|
||||
if err := h.Connect(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 创建 modbus 客户端
|
||||
c := modbus.NewClient(h)
|
||||
|
||||
return &reader{h, c}, nil
|
||||
}
|
||||
54
pkg/channel/channel.go
Normal file
54
pkg/channel/channel.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package channel
|
||||
|
||||
import "sync"
|
||||
|
||||
// Closed 可包含关闭状态的通道
|
||||
type Closed[T any] struct {
|
||||
ch chan T
|
||||
closed bool
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *Closed[T]) Close() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if s.closed {
|
||||
return
|
||||
}
|
||||
|
||||
close(s.ch)
|
||||
s.closed = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Closed[T]) Data() <-chan T {
|
||||
return s.ch
|
||||
}
|
||||
|
||||
func (s *Closed[T]) Send(data T) bool {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
if s.closed {
|
||||
return false
|
||||
}
|
||||
|
||||
s.ch <- data
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Closed[T]) isClosed() bool {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.closed
|
||||
}
|
||||
|
||||
func NewClosed[T any]() *Closed[T] {
|
||||
return &Closed[T]{
|
||||
ch: make(chan T),
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"go.bug.st/serial/enumerator"
|
||||
)
|
||||
|
||||
func PrintPorts() {
|
||||
ports, err := enumerator.GetDetailedPortsList()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if len(ports) == 0 {
|
||||
return
|
||||
}
|
||||
for _, port := range ports {
|
||||
fmt.Printf("Port: %s\n", port.Name)
|
||||
if port.Product != "" {
|
||||
fmt.Printf(" Product Name: %s\n", port.Product)
|
||||
}
|
||||
if port.IsUSB {
|
||||
fmt.Printf(" USB ID : %s:%s\n", port.VID, port.PID)
|
||||
fmt.Printf(" USB serial : %s\n", port.SerialNumber)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,69 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"go.bug.st/serial"
|
||||
"github.com/grid-x/modbus"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Device struct {
|
||||
port serial.Port
|
||||
type Relay interface {
|
||||
io.Closer
|
||||
// SetSlave 设置继电器的 UnitID
|
||||
SetSlave(byte)
|
||||
// On 打开继电器
|
||||
On(int) error
|
||||
// Off 关闭继电器
|
||||
Off(int) error
|
||||
// OnAll 打开所有继电器
|
||||
OnAll() error
|
||||
// OffAll 关闭所有继电器
|
||||
OffAll() error
|
||||
}
|
||||
|
||||
func (r *Device) Close() error {
|
||||
return r.port.Close()
|
||||
type device struct {
|
||||
h modbus.ClientHandler
|
||||
c modbus.Client
|
||||
}
|
||||
|
||||
func (r *Device) On(num int) error {
|
||||
_, err := io.WriteString(r.port, fmt.Sprintf("AT+OUT%v+1=ON\r\n", num))
|
||||
func (r *device) SetSlave(slaveID byte) {
|
||||
r.h.SetSlave(slaveID)
|
||||
}
|
||||
|
||||
func (r *device) Close() error {
|
||||
return r.h.Close()
|
||||
}
|
||||
|
||||
func (r *device) OnAll() error {
|
||||
_, err := r.c.WriteMultipleCoils(uint16(0), 16, []byte{0xFF, 0xFF})
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Device) Off(num int) error {
|
||||
_, err := io.WriteString(r.port, fmt.Sprintf("AT+OUT%v+1=OFF\r\n", num))
|
||||
func (r *device) OffAll() error {
|
||||
_, err := r.c.WriteMultipleCoils(uint16(0), 16, []byte{0x00, 0x00})
|
||||
return err
|
||||
}
|
||||
|
||||
func New(portName string, reader func(msg string)) (*Device, error) {
|
||||
port, err := serial.Open(portName, &serial.Mode{
|
||||
BaudRate: 9600,
|
||||
DataBits: 8,
|
||||
})
|
||||
if err != nil {
|
||||
func (r *device) On(num int) error {
|
||||
_, err := r.c.WriteSingleCoil(uint16(num), 0xFF00)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *device) Off(num int) error {
|
||||
_, err := r.c.WriteSingleCoil(uint16(num), 0x0000)
|
||||
return err
|
||||
}
|
||||
|
||||
func New(address string) (Relay, error) {
|
||||
h := modbus.NewRTUClientHandler(address)
|
||||
h.SlaveID = 1
|
||||
h.BaudRate = 9600
|
||||
h.DataBits = 8
|
||||
h.Parity = "N"
|
||||
h.StopBits = 1
|
||||
|
||||
if err := h.Connect(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
r := bufio.NewReader(port)
|
||||
line, _, _ := r.ReadLine()
|
||||
if reader != nil {
|
||||
reader(string(line))
|
||||
}
|
||||
}
|
||||
}()
|
||||
return &Device{port: port}, nil
|
||||
c := modbus.NewClient(h)
|
||||
|
||||
return &device{h, c}, nil
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ func (tts *AliTTS) getToken() error {
|
||||
func (tts *AliTTS) Get(text string) (io.Reader, error) {
|
||||
param := nls.DefaultSpeechSynthesisParam()
|
||||
param.Volume = 100
|
||||
param.Voice = tts.Voice
|
||||
|
||||
err := tts.getToken()
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user