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) // WriteUnitId 写入读卡器的 UnitID WriteUnitId(uint16) error // Init 初始化读卡器配置 Init() error // OnCardInfo 会在卡号信息发生变化时调用回调函数,直到上下文被取消,或者 Reader 被关闭。需要在 goroutine 中调用。 OnCardInfo(context.Context, func(*CardInfo)) GetCardInfo() *CardInfo } type CardInfo struct { Type uint16 ID string } type reader struct { 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 { // 配置读卡间隔时间为 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 } // GetCardInfo 获取卡号信息 func (r *reader) GetCardInfo() *CardInfo { // 读取状态寄存器确认是否信息已经准备好 status, err := r.c.ReadRegister(29, modbus.HoldingRegister) if err != nil { zap.S().Errorln("ReadRegister 40030 error:", err) return nil } else if status == 0x0000 { return nil } // 读取卡号长度 cardLength, err := r.c.ReadRegister(30, modbus.HoldingRegister) if err != nil { zap.S().Errorln("ReadRegister 40031 error:", err) return nil } dataLength := cardLength >> 8 // 读取卡类型 cardType, err := r.c.ReadRegister(31, modbus.HoldingRegister) if err != nil { zap.S().Errorln("ReadRegister 40032 error:", err) return nil } // 读取卡号数据 cardData, err := r.c.ReadRegisters(32, uint16(math.Round(float64(dataLength)/2)), modbus.HoldingRegister) if err != nil { zap.S().Errorln("ReadRegister 40033~ error:", err) return nil } 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) } } 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) 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 }