package server import ( "encoding/binary" "errors" "fmt" "github.com/gogf/gf/encoding/gbinary" "github.com/gogf/gf/net/gtcp" "github.com/gogf/gf/os/glog" "github.com/gogf/gf/util/gconv" "io" "net" "pt100-gateway/protocol" "strconv" "strings" "syscall" "time" ) type Client struct { Id string srv *Server conn *gtcp.Conn sendChan chan []byte closeChan chan struct{} closeHandler func(id string, c *Client) regHandler func(id string, c *Client) isReg bool } func NewClient(s *Server, conn *gtcp.Conn) *Client { return &Client{ srv: s, conn: conn, sendChan: make(chan []byte), closeChan: make(chan struct{}), } } func (c *Client) SetId(id string) { c.Id = id } func (c *Client) SendLoop() { for { select { case buf := <-c.sendChan: err := c.send(buf) if err != nil { glog.Errorf("指令发送失败:%s", err.Error()) continue } timer := time.NewTimer(5 * time.Second) for { select { case <-c.closeChan: return case <-timer.C: glog.Error("接收指令超时") break default: receiveBuf, err := c.conn.Recv(-1) if err != nil { c.readError(err) break } if !c.isReg { id := gbinary.DecodeToString(receiveBuf) glog.Debugf("收到注册包!id:%s", id) c.SetId(id) c.isReg = true if c.regHandler != nil { c.regHandler(c.Id, c) } continue } glog.Debugf("收到数据:%2X", receiveBuf) if err := c.decodeAndReport(receiveBuf); err != nil { glog.Debugf("处理数据失败:%s", err.Error()) break } } break } } } } // decodeAndReport 收到数据:01 03 10 01 05 01 09 FF FF 01 06 FF FF 01 03 FF FF FF FF E7 EB func (c *Client) decodeAndReport(buf []byte) error { length := len(buf) var crc crc crc.reset().pushBytes(buf[0 : length-2]) checksum := uint16(buf[length-1])<<8 | uint16(buf[length-2]) if checksum != crc.value() { return errors.New(fmt.Sprintf("modbus: response crc '%v' does not match expected '%v'", checksum, crc.value())) } if buf[1] == 0x03 { data := &protocol.Data{} data.Address = gconv.String(gbinary.BeDecodeToInt(buf[0:1])) data.Tem1 = decodeData(buf[3:5]) data.Tem2 = decodeData(buf[5:7]) data.Tem3 = decodeData(buf[7:9]) data.Tem4 = decodeData(buf[9:11]) data.Tem5 = decodeData(buf[11:13]) data.Tem6 = decodeData(buf[13:15]) data.Tem7 = decodeData(buf[15:17]) data.Tem8 = decodeData(buf[17:19]) if err := c.srv.ReportStatus(c.Id, data, "status"); err != nil { return err } } return nil } func decodeData(buf []byte) float32 { if gbinary.BeDecodeToUint16(buf) == 0xFFFF { return 0 } value := gconv.Float32(gbinary.BeDecodeToUint16(buf)&0x7FFF) / 10 if gbinary.BeDecodeToUint16(buf)&0x8000 == 0x8000 { value = -value } return value } func (c *Client) readError(err error) { defer c.closeConnection() if err == io.EOF || isErrConnReset(err) { return } glog.Errorf("读取数据发生错误:%s", err.Error()) } func (c *Client) closeConnection() { _ = c.conn.Close() c.conn = nil close(c.closeChan) c.isReg = false if c.closeHandler != nil { c.closeHandler(c.Id, c) } } // 计算温度值, 处理零下的情况 func caleTemperature(data []byte) int { var ym uint16 var isBlowZero bool var result int bm := binary.BigEndian.Uint16(data) var bitNum = len(data) * 8 f := "%." + strconv.Itoa(bitNum) + "b" bmStr := fmt.Sprintf(f, bm) if string(bmStr[0]) == "1" { // blow zero ym = ^bm + 1 isBlowZero = true } else { ym = bm } result = int(ym) if isBlowZero { result = int(ym) * -1 } return result } // isErrConnReset read: connection reset by peer func isErrConnReset(err error) bool { if ne, ok := err.(*net.OpError); ok { return strings.Contains(ne.Err.Error(), syscall.ECONNRESET.Error()) } return false } func (c *Client) GetSendByte() { for { c.sendChan <- []byte{0x01, 0x03, 0x00, 0x00, 0x00, 0x08, 0x44, 0x0C} time.Sleep(10 * time.Second) } } func (c *Client) send(buf []byte) error { if c.conn == nil { return nil } glog.Debugf("----->%2X", buf) err := c.conn.Send(buf) if err != nil { glog.Error(c.srv.ctx, err) c.closeConnection() return err } return nil }