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 }