- 使用 替代 - 简化 为 - 统一代码风格,移除冗余的容量参数 Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
156 lines
3.4 KiB
Go
156 lines
3.4 KiB
Go
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 := range s {
|
||
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
|
||
}
|