package main import ( "encoding/binary" "errors" "io" "sync" "time" "github.com/goburrow/serial" "lc/edge/ipole/modbus" "lc/edge/ipole/ym485" "lc/edge/ipole/zigbee" ) const ( // SerialDefaultTimeout Serial Default timeout SerialDefaultTimeout = 2000 * time.Millisecond // SerialDefaultAutoReconnect Serial Default auto reconnect count SerialDefaultAutoReconnect = 6 SerialReadMaxLength = 40 FlagModbusRtu = 0 FlagChZigbee = 1 FlagYm485 = 2 ) var ErrClosedConnection = errors.New("use of closed connection") // serialPort has configuration and I/O controller. type serialPort struct { // Serial port configuration. serial.Config mu sync.Mutex port io.ReadWriteCloser // if > 0, when disconnected,it will try to reconnect the remote // but if we activate close self,it will not to reconnect // if == 0 auto reconnect not active autoReconnect byte } // Connect try to connect the remote server func (sf *serialPort) Connect() (err error) { sf.mu.Lock() err = sf.connect() sf.mu.Unlock() return } // Caller must hold the mutex before calling this method. func (sf *serialPort) connect() error { port, err := serial.Open(&sf.Config) if err != nil { return err } sf.port = port return nil } // IsConnected returns a bool signifying whether the client is connected or not. func (sf *serialPort) IsConnected() (b bool) { sf.mu.Lock() b = sf.isConnected() sf.mu.Unlock() return b } // Caller must hold the mutex before calling this method. func (sf *serialPort) isConnected() bool { return sf.port != nil } // SetAutoReconnect set auto reconnect count // if cnt == 0, disable auto reconnect // if cnt > 0 ,enable auto reconnect,but max 6 func (sf *serialPort) SetAutoReconnect(cnt byte) { sf.mu.Lock() sf.autoReconnect = cnt if sf.autoReconnect > 6 { sf.autoReconnect = 6 } sf.mu.Unlock() } // setSerialConfig set serial config func (sf *serialPort) setSerialConfig(config serial.Config) { sf.Config = config } // Close current connection. func (sf *serialPort) Close() (err error) { sf.mu.Lock() if sf.port != nil { err = sf.port.Close() sf.port = nil } sf.mu.Unlock() return } func (sf *serialPort) send(aduRequest []byte) error { var err error var tryCnt byte for { if _, err = sf.port.Write(aduRequest); err == nil { break } if sf.autoReconnect == 0 { break } for { if err = sf.connect(); err == nil { break } if tryCnt++; tryCnt <= sf.autoReconnect { return err //超过最大重试次数则退出 } time.Sleep(1 * time.Second) //1秒后重试 } } return err } func (sf *serialPort) recv(flag uint8) ([]byte, error) { var n int var aduResponse []byte var err error data := make([]byte, SerialReadMaxLength, SerialReadMaxLength) for { if n, err = sf.port.Read(data); err != nil { if VerifyCrc16(aduResponse, flag) { err = nil //读取完整 break } break } if n > 0 { aduResponse = append(aduResponse, data[:n]...) if n <= SerialReadMaxLength { //读取少于等于SerialReadMaxLength字节,可能已经读完,判断CRC16确认 if VerifyCrc16(aduResponse, flag) { break //读取完整 } } } else if n == 0 { break } } return aduResponse, err } func (sf *serialPort) waitSendRecv(aduRequest []byte, flag uint8, wait uint) { var bytesToRead = 0 if flag == 0 { //标准modbusRTU协议 bytesToRead = modbus.CalculateResponseLength(aduRequest) } else if flag == 1 { //长和单灯控制器,zigbee协议 bytesToRead = zigbee.CalculateChddjkZigbeeResponselength(aduRequest) } time.Sleep(sf.calculateDelay(len(aduRequest) + bytesToRead)) if wait > 0 { time.Sleep(time.Duration(wait) * time.Millisecond) } } // SendRecvData 发送和接收 func (sf *serialPort) SendRecvData(aduRequest []byte, flag uint8, wait uint) (aduResponse []byte, err error) { sf.mu.Lock() defer sf.mu.Unlock() //连接检查 if !sf.isConnected() { //重试一次 if err := sf.connect(); err != nil { return nil, ErrClosedConnection } } //发送请求 if err := sf.send(aduRequest); err != nil { return nil, err } //等待发完和从机处理返回 sf.waitSendRecv(aduRequest, flag, wait) //接收响应 return sf.recv(flag) } // SendData 仅发送,用于广播 func (sf *serialPort) SendData(aduRequest []byte, flag uint8, wait uint) error { sf.mu.Lock() defer sf.mu.Unlock() //连接检查 if !sf.isConnected() { //重试一次 if err := sf.connect(); err != nil { return ErrClosedConnection } } err := sf.send(aduRequest) if err == nil { sf.waitSendRecv(aduRequest, flag, wait) //等待发完 } return err } func (sf *serialPort) calculateDelay(chars int) time.Duration { var characterDelay, frameDelay int // us if sf.BaudRate <= 0 || sf.BaudRate > 19200 { characterDelay = 750 frameDelay = 1750 } else { characterDelay = 15000000 / sf.BaudRate frameDelay = 35000000 / sf.BaudRate } return time.Duration(characterDelay*chars+frameDelay) * time.Microsecond } // VerifyCrc16 标准Modbus-RTU,末尾2字节是crc16,大端 // 长和单灯控制器zigbee协议,末尾2字节是crc16 func VerifyCrc16(data []byte, flag uint8) bool { if flag == FlagYm485 { return ym485.VerifySum(data) } datalen := len(data) if datalen > 4 { var crc_ uint16 crc := modbus.CRC16(data[:datalen-2]) if flag == FlagModbusRtu { crc_ = binary.LittleEndian.Uint16(data[datalen-2 : datalen]) } else if flag == FlagChZigbee { crc_ = binary.BigEndian.Uint16(data[datalen-2 : datalen]) } else { crc_ = binary.LittleEndian.Uint16(data[datalen-2 : datalen]) } if crc == crc_ { return true } } return false }