package plc import ( "encoding/hex" "errors" "fmt" "io" "strings" "time" "github.com/jacobsa/go-serial/serial" ) /* 定义串口客户端 */ type SerialClient struct { readWriter io.ReadWriteCloser OutBuffer chan string PortName string baudRate uint } func NewSerialClient(portName string, baudRate uint) (*SerialClient, error) { c := &SerialClient{ readWriter: nil, OutBuffer: make(chan string, 1024), PortName: portName, baudRate: baudRate, } r, err := c.open(portName, baudRate) if err != nil { return nil, err } c.readWriter = r return c, nil } func (c *SerialClient) Start() { for { buf := make([]byte, 1024) n, err := c.readWriter.Read(buf) if err != nil { if err != io.EOF { //尝试重新打开 r, err := c.open(c.PortName, c.baudRate) if err != nil { time.Sleep(2 * time.Second) continue } c.readWriter = r } continue } buf = buf[:n] res := hex.EncodeToString(buf) if strings.TrimSpace(res) == "" { continue } fmt.Printf("Rx: %s\n", res) c.OutBuffer <- res } } func (c *SerialClient) open(portName string, baudRate uint) (io.ReadWriteCloser, error) { options := serial.OpenOptions{ PortName: portName, BaudRate: baudRate, DataBits: 7, StopBits: 1, MinimumReadSize: 4, ParityMode: serial.PARITY_EVEN, } r, err := serial.Open(options) if err != nil { return nil, err } c.readWriter = r return r, err } func (c *SerialClient) Close() { if c.readWriter != nil { c.readWriter.Close() } } func (c *SerialClient) SendMsg(data string) (int, error) { if c.readWriter == nil { return 0, errors.New("serial port is closed") } bytes, err := hex.DecodeString(data) if err != nil { return 0, err } return c.readWriter.Write(bytes) } func (c *SerialClient) ReadHoldRegister(addr string) (int, error) { if c.readWriter == nil { return 0, errors.New("serial port is closed") } // 三菱fn系列串口协议 // 02 表示开始 + 30 表示读数据 + 4位地址位 + 02 数据长度 + 03 停止 + 偶校验 // 先组装数据 // 读取的地址 dataBytes := append([]byte{0x30}, []byte(addr)...) // 读取的长度 dataBytes = append(dataBytes, []byte("02")...) // 停止 dataBytes = append(dataBytes, []byte{0x03}...) // 计算校验码, 30 + 地址编码 + 数据长度 + 03 , 十六进制结果取后两位. var sum uint8 for _, d := range dataBytes { sum += d } sumStr := fmt.Sprintf("%x", sum) fmt.Printf("sumStr: %s\n", sumStr) var checkCode string if len(sumStr) < 2 { checkCode = "0" + sumStr } else { checkCode = sumStr[len(sumStr)-2:] } var bytes = []byte{0x02} // 添加数据 bytes = append(bytes, dataBytes...) // 添加校验 bytes = append(bytes, []byte(checkCode)...) fmt.Printf("Tx: %X \n", bytes) // 写 if n, err := c.readWriter.Write(bytes); err != nil { return n, err } else { fmt.Printf("Tx:len:%d\n", n) } // 读 buf := make([]byte, 1024) n, err := c.readWriter.Read(buf) if err != nil { return n, err } buf = buf[:n] fmt.Printf("Rx: %X\nRx len:%d", buf, n) fmt.Printf("value: %d\n", buf[2:6]) return n, nil } func (c *SerialClient) WriteHoldRegister(addr string) (int, error) { if c.readWriter == nil { return 0, errors.New("serial port is closed") } // 三菱fn系列串口协议 // 02 表示开始 + 31 表示写数据 + 4位地址位 + 02 数据长度 + 03 停止 + 偶校验 // 先组装数据 // 读取的地址 dataBytes := append([]byte{0x31}, []byte(addr)...) // 读取的长度 dataBytes = append(dataBytes, []byte("02")...) // 停止 dataBytes = append(dataBytes, []byte{0x03}...) // 计算校验码, 30 + 地址编码 + 数据长度 + 03 , 十六进制结果取后两位. var sum uint8 for _, d := range dataBytes { sum += d } sumStr := fmt.Sprintf("%x", sum) fmt.Printf("sumStr: %s\n", sumStr) var checkCode string if len(sumStr) < 2 { checkCode = "0" + sumStr } else { checkCode = sumStr[len(sumStr)-2:] } var bytes = []byte{0x02} // 添加数据 bytes = append(bytes, dataBytes...) // 添加校验 bytes = append(bytes, []byte(checkCode)...) fmt.Printf("Tx: %X \n", bytes) // 写 if n, err := c.readWriter.Write(bytes); err != nil { return n, err } else { fmt.Printf("Tx:len:%d\n", n) } // 读 buf := make([]byte, 1024) n, err := c.readWriter.Read(buf) if err != nil { return n, err } buf = buf[:n] fmt.Printf("Rx: %X\nRx len:%d", buf, n) fmt.Printf("value: %d\n", buf[2:6]) return n, nil }