初步完成龙台的读卡逻辑
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,6 @@
|
|||||||
/logs
|
/logs
|
||||||
|
/.idea
|
||||||
|
/.vscode
|
||||||
|
|
||||||
*.mp3
|
*.mp3
|
||||||
game-driver*
|
game-driver*
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"game-driver/internal/routes/play/card_device"
|
"game-driver/internal/routes/play/card_pusher"
|
||||||
"gopkg.in/natefinch/lumberjack.v2"
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ type AliyunConfig struct {
|
|||||||
|
|
||||||
type GameConfig struct {
|
type GameConfig struct {
|
||||||
MaxTimeout int
|
MaxTimeout int
|
||||||
CardGroups []*card_device.LineGroup
|
CardGroups []*card_pusher.LineGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
type Logger struct {
|
type Logger struct {
|
||||||
|
|||||||
BIN
docs/读卡器YMC1701&1702资料V1.rar
Normal file
BIN
docs/读卡器YMC1701&1702资料V1.rar
Normal file
Binary file not shown.
8
game.md
8
game.md
@@ -65,6 +65,8 @@
|
|||||||
|
|
||||||
```json lines
|
```json lines
|
||||||
{
|
{
|
||||||
|
// 发出的卡片ID
|
||||||
|
"card_id": "",
|
||||||
// 空卡设备数量
|
// 空卡设备数量
|
||||||
"empty": 0,
|
"empty": 0,
|
||||||
// 错误设备数量
|
// 错误设备数量
|
||||||
@@ -82,6 +84,12 @@
|
|||||||
|
|
||||||
```json lines
|
```json lines
|
||||||
{
|
{
|
||||||
|
// 需要的卡片ID
|
||||||
|
"card_id": "",
|
||||||
|
// 卡片错误时的播报内容
|
||||||
|
"card_error": "",
|
||||||
|
// 卡片正确时播报的恭喜通关内容
|
||||||
|
"card_ok": "",
|
||||||
// 等待插卡时间,单位秒
|
// 等待插卡时间,单位秒
|
||||||
"wait_card": 0,
|
"wait_card": 0,
|
||||||
// 插卡后持续时间,单位秒
|
// 插卡后持续时间,单位秒
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -6,6 +6,7 @@ require (
|
|||||||
github.com/aliyun/alibabacloud-nls-go-sdk v1.1.1
|
github.com/aliyun/alibabacloud-nls-go-sdk v1.1.1
|
||||||
github.com/eclipse/paho.golang v0.22.0
|
github.com/eclipse/paho.golang v0.22.0
|
||||||
github.com/gopxl/beep/v2 v2.1.0
|
github.com/gopxl/beep/v2 v2.1.0
|
||||||
|
github.com/munnik/modbus v1.6.6
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/spf13/viper v1.19.0
|
github.com/spf13/viper v1.19.0
|
||||||
github.com/warthog618/go-gpiocdev v0.9.1
|
github.com/warthog618/go-gpiocdev v0.9.1
|
||||||
|
|||||||
6
go.sum
6
go.sum
@@ -3,8 +3,6 @@ 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/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/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.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 h1:I93ILTm5ytF4e5+lEQXSXcydS26D9eVyJ4H6z3rJqMA=
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.53/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
|
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 h1:LjItoNZuu5xHlsByFo+kr3nGa4LRIESCGWhfurayxBg=
|
||||||
@@ -75,6 +73,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
|||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/munnik/modbus v1.6.6 h1:QRAR+04bivKSzPN5qi5g9Wyzh+e3oQVFMUOhv35ya5Q=
|
||||||
|
github.com/munnik/modbus v1.6.6/go.mod h1:p8PIBjiZgsY82MiZPrkAGkrDomL1tBNGGQIwyAm4Vp0=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
|
||||||
@@ -160,6 +160,8 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ func PlayBgm() leaf.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
} else {
|
} else {
|
||||||
zap.S().Errorln("背景音乐解析为空")
|
zap.S().Infoln("未解析到背景音乐")
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Next()
|
c.Next()
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func switchPoint(ctx context.Context, point int) leaf.HandlerFunc {
|
|||||||
return play.PushCard(ctx)
|
return play.PushCard(ctx)
|
||||||
case 5:
|
case 5:
|
||||||
// 5号点位(等待插卡)
|
// 5号点位(等待插卡)
|
||||||
return play.WaitCard
|
return play.WaitCard(ctx)
|
||||||
default:
|
default:
|
||||||
return play.Default
|
return play.Default
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package card_device
|
package card_pusher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package card_device
|
package card_pusher
|
||||||
|
|
||||||
type LineGroup struct {
|
type LineGroup struct {
|
||||||
Name string
|
Name string
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package card_device
|
package card_pusher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"game-driver/config"
|
"game-driver/config"
|
||||||
"game-driver/internal/middleware"
|
"game-driver/internal/middleware"
|
||||||
"game-driver/internal/routes/play/card_device"
|
"game-driver/internal/routes/play/card_pusher"
|
||||||
"game-driver/internal/schema"
|
"game-driver/internal/schema"
|
||||||
"game-driver/leaf"
|
"game-driver/leaf"
|
||||||
"game-driver/pkg/utils"
|
"game-driver/pkg/utils"
|
||||||
@@ -16,19 +16,20 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ResponseBody struct {
|
type ResponseBody struct {
|
||||||
Empty int `json:"empty"`
|
CardId string `json:"card_id"`
|
||||||
Error int `json:"error"`
|
Empty int `json:"empty"`
|
||||||
OutOk int `json:"out_ok"`
|
Error int `json:"error"`
|
||||||
num int
|
OutOk int `json:"out_ok"`
|
||||||
|
num int
|
||||||
}
|
}
|
||||||
|
|
||||||
func PushCard(ctx context.Context) leaf.HandlerFunc {
|
func PushCard(ctx context.Context) leaf.HandlerFunc {
|
||||||
devices := make([]*card_device.Device, 0)
|
devices := make([]*card_pusher.Device, 0)
|
||||||
for _, group := range config.C.Game.CardGroups {
|
for _, group := range config.C.Game.CardGroups {
|
||||||
gv, _ := json.Marshal(group)
|
gv, _ := json.Marshal(group)
|
||||||
zap.S().Info("发卡器指针:", string(gv))
|
zap.S().Info("发卡器指针:", string(gv))
|
||||||
|
|
||||||
device, err := card_device.New(group)
|
device, err := card_pusher.New(group)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
zap.S().Panicln("初始化发卡器失败: ", err)
|
zap.S().Panicln("初始化发卡器失败: ", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,109 @@
|
|||||||
package play
|
package play
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"game-driver/internal/middleware"
|
"game-driver/internal/middleware"
|
||||||
"game-driver/internal/schema"
|
"game-driver/internal/schema"
|
||||||
"game-driver/leaf"
|
"game-driver/leaf"
|
||||||
|
"game-driver/pkg/card_reader"
|
||||||
|
"game-driver/pkg/channel"
|
||||||
|
"game-driver/pkg/tts"
|
||||||
|
"go.uber.org/zap"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func WaitCard(c *leaf.Context) {
|
func WaitCard(ctx context.Context) leaf.HandlerFunc {
|
||||||
payload := leaf.Value[*schema.PlayModal](c, middleware.PayloadJSONKey)
|
reader, err := card_reader.NewReader("rtu:///dev/ttyUSB0")
|
||||||
|
if err != nil {
|
||||||
var waitCard time.Duration
|
zap.S().Panicln("读卡器串口连接失败", err)
|
||||||
if a, ok := payload.Game["wait_card"]; ok {
|
|
||||||
if v, ok := a.(float64); ok {
|
|
||||||
waitCard = time.Duration(v)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 等待组
|
|
||||||
var wait sync.WaitGroup
|
|
||||||
defer wait.Wait()
|
|
||||||
|
|
||||||
a := make(chan string)
|
|
||||||
defer close(a)
|
|
||||||
|
|
||||||
wait.Add(1)
|
|
||||||
go func() {
|
go func() {
|
||||||
defer wait.Done()
|
<-ctx.Done()
|
||||||
//TODO: 模拟卡片 3s 插入
|
_ = reader.Close()
|
||||||
time.Sleep(3 * time.Second)
|
|
||||||
a <- "卡片数据"
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
select {
|
err = reader.Init()
|
||||||
case <-c.Done():
|
if err != nil {
|
||||||
case <-time.After(waitCard * time.Second):
|
zap.S().Panicln("读卡器初始化失败", err)
|
||||||
case _, ok := <-a: // 等待卡片插入
|
}
|
||||||
if ok { // 非关闭信号
|
|
||||||
Default(c)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
137
pkg/card_reader/reader.go
Normal file
137
pkg/card_reader/reader.go
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
package card_reader
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/munnik/modbus"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Reader interface {
|
||||||
|
io.Closer
|
||||||
|
SetUnitID(uint8)
|
||||||
|
Init() error
|
||||||
|
// OnCardInfo 会在卡号信息发生变化时调用回调函数,直到上下文被取消,或者 Reader 被关闭。需要在 goroutine 中调用。
|
||||||
|
OnCardInfo(context.Context, func(CardInfo))
|
||||||
|
}
|
||||||
|
|
||||||
|
type CardInfo struct {
|
||||||
|
Type uint16
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type reader struct {
|
||||||
|
c *modbus.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reader) Init() error {
|
||||||
|
// 配置读卡间隔时间为 50 毫秒
|
||||||
|
err := r.c.WriteRegister(8, 0x0511)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 由低到高输出 500 毫秒
|
||||||
|
err = r.c.WriteRegister(9, 0xF20A)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 读取后自动清空,高字节:00 不清空 01 清空
|
||||||
|
err = r.c.WriteRegister(10, 0x0103)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 自动读卡号,高字节高4位:0 读一次 1 连续读
|
||||||
|
err = r.c.WriteRegister(11, 0x0301)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reader) OnCardInfo(ctx context.Context, f func(info CardInfo)) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-time.After(100 * time.Millisecond):
|
||||||
|
{
|
||||||
|
// 读取状态寄存器确认是否信息已经准备好
|
||||||
|
status, err := r.c.ReadRegister(29, modbus.HoldingRegister)
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Errorln("ReadRegister 40030 error:", err)
|
||||||
|
break
|
||||||
|
} else if status == 0x0000 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取卡号长度
|
||||||
|
cardLength, err := r.c.ReadRegister(30, modbus.HoldingRegister)
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Errorln("ReadRegister 40031 error:", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
dataLength := cardLength >> 8
|
||||||
|
|
||||||
|
// 读取卡类型
|
||||||
|
cardType, err := r.c.ReadRegister(31, modbus.HoldingRegister)
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Errorln("ReadRegister 40032 error:", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取卡号数据
|
||||||
|
cardData, err := r.c.ReadRegisters(32, uint16(math.Round(float64(dataLength)/2)), modbus.HoldingRegister)
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Errorln("ReadRegister 40033~ error:", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
s := make([]string, dataLength)
|
||||||
|
for i := 0; i < int(dataLength); i++ {
|
||||||
|
if i%2 == 0 {
|
||||||
|
s[i] = fmt.Sprintf("%02X", cardData[i/2]>>8)
|
||||||
|
} else {
|
||||||
|
s[i] = fmt.Sprintf("%02X", cardData[i/2]&0xFF)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f(CardInfo{
|
||||||
|
Type: cardType,
|
||||||
|
ID: strings.Join(s, ""),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reader) SetUnitID(unitID uint8) {
|
||||||
|
_ = r.c.SetUnitID(unitID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reader) Close() error {
|
||||||
|
return r.c.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewReader 创建一个新的读卡器
|
||||||
|
func NewReader(URL string) (Reader, error) {
|
||||||
|
// 配置串口客户端
|
||||||
|
c, _ := modbus.NewClient(&modbus.Configuration{
|
||||||
|
URL: URL,
|
||||||
|
Speed: 9600,
|
||||||
|
DataBits: 8,
|
||||||
|
Timeout: 2 * time.Second,
|
||||||
|
Logger: zap.NewStdLog(zap.L()),
|
||||||
|
})
|
||||||
|
|
||||||
|
// 打开串口连接
|
||||||
|
err := c.Open()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &reader{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),
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user