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 }