发卡关卡的读卡流程完成

This commit is contained in:
2024-12-10 17:40:59 +08:00
parent 193fa806c2
commit 25cb34f6f5
6 changed files with 157 additions and 60 deletions

View File

@@ -1,7 +1,7 @@
location: wushan location: wushan
point: 5 point: 5
relay: COM7 relay: COM7
maxTimeout: 60 # 单位 s maxTimeout: 60 # 单位 s,必须大于 0
log: log:
level: debug level: debug
file: file:
@@ -20,12 +20,13 @@ aliyun:
voice: zhifeng_emo voice: zhifeng_emo
game: game:
url: rtu:///dev/ttyUSB0 # 点位 5 的串口地址 url: rtu:///dev/ttyUSB0 # 点位 5 的串口地址
# cardGroups: # 点位 4 的发卡器配置 # pushGroups: # 点位 4 的发卡器配置
# - name: gpiochip0 # - name: gpiochip0
# outOK: 6 # outOK: 6
# lower: 13 # lower: 13
# error: 19 # error: 19
# empty: 26 # empty: 26
# push: 11 # push: 11
# reset: 22 # reset: 22
# pull: 27 # pull: 27
# readUrl: rtu:///dev/ttyUSB0 # 点位 4 的读卡器配置

View File

@@ -3,5 +3,6 @@ package game
import "game-driver/internal/routes/play/card_pusher" import "game-driver/internal/routes/play/card_pusher"
type ConfigPush struct { type ConfigPush struct {
CardGroups []*card_pusher.LineGroup PushGroups []*card_pusher.LineGroup
ReadURL string
} }

41
demo/modbus2/main.go Normal file
View 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("rtu:///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("关闭完成")
}

View File

@@ -3,14 +3,18 @@ package play
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"game-driver/config/game" "game-driver/config/game"
"game-driver/internal/middleware" "game-driver/internal/middleware"
"game-driver/internal/routes/play/card_pusher" "game-driver/internal/routes/play/card_pusher"
"game-driver/internal/schema" "game-driver/internal/schema"
"game-driver/leaf" "game-driver/leaf"
"game-driver/pkg/card_reader"
"game-driver/pkg/utils" "game-driver/pkg/utils"
"github.com/eclipse/paho.golang/paho" "github.com/eclipse/paho.golang/paho"
"go.uber.org/zap" "go.uber.org/zap"
"io/fs"
"os"
"sync" "sync"
"time" "time"
) )
@@ -26,11 +30,35 @@ type ResponseBody struct {
func PushCard(ctx context.Context) leaf.HandlerFunc { func PushCard(ctx context.Context) leaf.HandlerFunc {
g := (game.G).(game.ConfigPush) g := (game.G).(game.ConfigPush)
// 开始初始化读卡器
reader, err := card_reader.NewReader(g.ReadURL)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
zap.S().Errorf("读卡器串口文件不存在: %s", g.ReadURL)
os.Exit(1)
}
zap.S().Panicf("读卡器串口连接失败 [%T]: %q", err, err)
}
go func() {
<-ctx.Done()
_ = reader.Close()
}()
// 开始初始化发卡器
devices := make([]*card_pusher.Device, 0) devices := make([]*card_pusher.Device, 0)
for _, group := range g.CardGroups { for i, group := range g.PushGroups {
// 对读卡器初始化配置
reader.SetUnitID(uint8(i + 1))
err = reader.Init()
if err != nil {
zap.S().Panicln("读卡器初始配置失败", err)
}
// 解析发卡器配置
gv, _ := json.Marshal(group) gv, _ := json.Marshal(group)
zap.S().Info("发卡器指针:", string(gv)) zap.S().Info("发卡器指针:", string(gv))
// 初始化发卡器
device, err := card_pusher.New(group) device, err := card_pusher.New(group)
if err != nil { if err != nil {
zap.S().Panicln("初始化发卡器失败: ", err) zap.S().Panicln("初始化发卡器失败: ", err)
@@ -84,6 +112,13 @@ func PushCard(ctx context.Context) leaf.HandlerFunc {
// 延迟1秒获取结果并发送消息 // 延迟1秒获取结果并发送消息
time.AfterFunc(time.Second, func() { time.AfterFunc(time.Second, func() {
if body.num != 0 { if body.num != 0 {
// 若卡片就位,读取卡片信息
if devices[body.num-1].GetOutOk() == 1 {
reader.SetUnitID(uint8(body.num))
if info := reader.GetCardInfo(); info != nil {
body.CardId = info.ID
}
}
body.OutOk += devices[body.num-1].GetOutOk() body.OutOk += devices[body.num-1].GetOutOk()
} }
publishBody(ctx, c.Properties.ResponseTopic, body) publishBody(ctx, c.Properties.ResponseTopic, body)

View File

@@ -85,7 +85,7 @@ func WaitCard(ctx context.Context) leaf.HandlerFunc {
wait.Add(1) wait.Add(1)
go func() { go func() {
defer wait.Done() defer wait.Done()
reader.OnCardInfo(cc, func(info card_reader.CardInfo) { reader.OnCardInfo(cc, func(info *card_reader.CardInfo) {
cardInfo.Send(info.ID) cardInfo.Send(info.ID)
}) })
}() }()

View File

@@ -14,9 +14,13 @@ import (
type Reader interface { type Reader interface {
io.Closer io.Closer
SetUnitID(uint8) SetUnitID(uint8)
// WriteUnitId 写入读卡器的 UnitID
WriteUnitId(uint16) error
// Init 初始化读卡器配置
Init() error Init() error
// OnCardInfo 会在卡号信息发生变化时调用回调函数,直到上下文被取消,或者 Reader 被关闭。需要在 goroutine 中调用。 // OnCardInfo 会在卡号信息发生变化时调用回调函数,直到上下文被取消,或者 Reader 被关闭。需要在 goroutine 中调用。
OnCardInfo(context.Context, func(CardInfo)) OnCardInfo(context.Context, func(*CardInfo))
GetCardInfo() *CardInfo
} }
type CardInfo struct { type CardInfo struct {
@@ -28,6 +32,14 @@ type reader struct {
c *modbus.Client c *modbus.Client
} }
// WriteUnitId 写入读卡器的 UnitID
func (r *reader) WriteUnitId(id uint16) error {
v := id<<8 + 1
zap.S().Infof("WriteRegister 40007 %#x", v)
return r.c.WriteRegister(6, v)
}
// Init 初始化读卡器配置
func (r *reader) Init() error { func (r *reader) Init() error {
// 配置读卡间隔时间为 50 毫秒 // 配置读卡间隔时间为 50 毫秒
err := r.c.WriteRegister(8, 0x0511) err := r.c.WriteRegister(8, 0x0511)
@@ -52,27 +64,22 @@ func (r *reader) Init() error {
return nil return nil
} }
func (r *reader) OnCardInfo(ctx context.Context, f func(info CardInfo)) { // GetCardInfo 获取卡号信息
for { func (r *reader) GetCardInfo() *CardInfo {
select {
case <-ctx.Done():
return
case <-time.After(100 * time.Millisecond):
{
// 读取状态寄存器确认是否信息已经准备好 // 读取状态寄存器确认是否信息已经准备好
status, err := r.c.ReadRegister(29, modbus.HoldingRegister) status, err := r.c.ReadRegister(29, modbus.HoldingRegister)
if err != nil { if err != nil {
zap.S().Errorln("ReadRegister 40030 error:", err) zap.S().Errorln("ReadRegister 40030 error:", err)
break return nil
} else if status == 0x0000 { } else if status == 0x0000 {
break return nil
} }
// 读取卡号长度 // 读取卡号长度
cardLength, err := r.c.ReadRegister(30, modbus.HoldingRegister) cardLength, err := r.c.ReadRegister(30, modbus.HoldingRegister)
if err != nil { if err != nil {
zap.S().Errorln("ReadRegister 40031 error:", err) zap.S().Errorln("ReadRegister 40031 error:", err)
break return nil
} }
dataLength := cardLength >> 8 dataLength := cardLength >> 8
@@ -80,14 +87,14 @@ func (r *reader) OnCardInfo(ctx context.Context, f func(info CardInfo)) {
cardType, err := r.c.ReadRegister(31, modbus.HoldingRegister) cardType, err := r.c.ReadRegister(31, modbus.HoldingRegister)
if err != nil { if err != nil {
zap.S().Errorln("ReadRegister 40032 error:", err) zap.S().Errorln("ReadRegister 40032 error:", err)
break return nil
} }
// 读取卡号数据 // 读取卡号数据
cardData, err := r.c.ReadRegisters(32, uint16(math.Round(float64(dataLength)/2)), modbus.HoldingRegister) cardData, err := r.c.ReadRegisters(32, uint16(math.Round(float64(dataLength)/2)), modbus.HoldingRegister)
if err != nil { if err != nil {
zap.S().Errorln("ReadRegister 40033~ error:", err) zap.S().Errorln("ReadRegister 40033~ error:", err)
break return nil
} }
s := make([]string, dataLength) s := make([]string, dataLength)
@@ -99,10 +106,22 @@ func (r *reader) OnCardInfo(ctx context.Context, f func(info CardInfo)) {
} }
} }
f(CardInfo{ return &CardInfo{
Type: cardType, Type: cardType,
ID: strings.Join(s, ""), 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)
} }
} }
} }