2021-11-22 WPF上位机 96-Modbus通信代码的封装

Posted 微软MVP Eleven

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021-11-22 WPF上位机 96-Modbus通信代码的封装相关的知识,希望对你有一定的参考价值。

public class ModbusBase



    public List<byte> GetReadCommand(byte deviceAddr, byte funcCode, ushort startAddr, ushort length)
    
        List<byte> buffer = new List<byte>();
        buffer.Add(deviceAddr);
        buffer.Add(funcCode);
        buffer.Add(BitConverter.GetBytes(startAddr)[1]);
        buffer.Add(BitConverter.GetBytes(startAddr)[0]);
        buffer.Add(BitConverter.GetBytes(length)[1]);
        buffer.Add(BitConverter.GetBytes(length)[0]);
        return buffer;
    

public class SerialPortBase : ModbusBase


public class ModbusRTU : SerialPortBase

    SerialPort serialPort = null;
    public ModbusRTU(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
    
        serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
    

    public bool Open()
    
        try
        
            if (serialPort == null)
                throw new Exception();
            if (!serialPort.IsOpen)
                serialPort.Open();
            return true;
        
        catch
        
            return false;
        
    

    // Socket 掉线重连
    // 1、服务端(从站)踢掉一个连接:重新创建一个连接对象
    // 2、断网,网线中断(关键服务端的处理  监听)

    public bool Close()
    
        try
        
            if (serialPort != null && serialPort.IsOpen)
                serialPort.Close();
            serialPort = null;

            return true;
        
        catch
        
            return false;
        
    

    // 半双工 读时不能写 业务功能(任务队列(读、写))  List<Operation>

    // 1、重复代码非常多,泛型   转换类型怎么处理    最终方案  不是Switch
    // 2、字节序
    // 3、掉线重连


    // 发送报文 03
    public List<ushort> ReadKeepRegister(byte deviceAddr, byte funcCode, ushort startAddr, ushort length)
    
        // 获取基本报文 
        List<byte> bytes = this.GetReadCommand(deviceAddr, funcCode, startAddr, length);
        // CRC
        bytes.AddRange(CRC16(bytes));

        if (serialPort.IsOpen)
        
            serialPort.Write(bytes.ToArray(), 0, bytes.Count);

            // 进行响应报文的解析
            int time = 1;
            while (time < 2000 && serialPort.BytesToRead <= 0)
            
                time += 1;
                //Debug.WriteLine("1");
                Thread.Sleep(1);
            
            if (serialPort.BytesToRead <= 0)
                throw new Exception("响应超时");


            byte[] buffer = new byte[serialPort.BytesToRead];
            serialPort.Read(buffer, 0, buffer.Length);

            List<byte> byteList = new List<byte>(buffer);
            // CRC校验检查
            List<byte> checkCRC = byteList.GetRange(byteList.Count - 2, 2);
            byteList.RemoveRange(byteList.Count - 2, 2);
            List<byte> validCode = CRC16(byteList);
            if (validCode.SequenceEqual(checkCRC))
            

                // 进行协议异常码的检查
                // 1-检查功能码  最高位是否为1
                int fc = byteList[1];
                if ((fc & 0x80) == 0x80)
                
                    // 说明报文 异常
                    // 取异常码 
                    throw new Exception(byteList[2].ToString());
                

                // 数据解析
                // 01 03 14 00 00 ...... (CRC)
                byteList.RemoveRange(0, 3);

                List<ushort> values = new List<ushort>();
                List<byte> valueBytes = new List<byte>();
                for (int i = 0; i < length; i++)
                
                    valueBytes.Clear();
                    valueBytes.Add(byteList[i * 2 + 1]);
                    valueBytes.Add(byteList[i * 2]);

                    var value = BitConverter.ToUInt16(valueBytes.ToArray(), 0);

                    values.Add(value);
                

                return values;
            
        
        else
        
            Task.Run(async () =>
            
                // 尝试次数     会有升级
                while (true)
                
                    await Task.Delay(2000);

                    if (serialPort.IsOpen)
                    
                        Console.WriteLine("连接正常");
                        continue;
                    

                    try
                    
                        Console.WriteLine("连接断开,尝试重新连接");
                        serialPort.Open();
                    
                    catch (System.IO.IOException ex)
                    
                        Console.WriteLine("设备连接断开");
                        continue;
                    
                
            );
        

        return null;
    


    public List<float> ReadKeepRegisterFloat(byte deviceAddr, byte funcCode, ushort startAddr, ushort length)
    
        // 获取基本报文 
        List<byte> bytes = this.GetReadCommand(deviceAddr, funcCode, startAddr, length);
        // CRC
        bytes.AddRange(CRC16(bytes));

        if (serialPort.IsOpen)
        
            serialPort.Write(bytes.ToArray(), 0, bytes.Count);

            // 进行响应报文的解析
            int time = 1;
            while (time < 2000 && serialPort.BytesToRead <= 0)
            
                time += 1;
                //Debug.WriteLine("1");
                Thread.Sleep(1);
            
            if (serialPort.BytesToRead <= 0)
                throw new Exception("响应超时");


            byte[] buffer = new byte[serialPort.BytesToRead];
            serialPort.Read(buffer, 0, buffer.Length);

            List<byte> byteList = new List<byte>(buffer);
            // CRC校验检查
            List<byte> checkCRC = byteList.GetRange(byteList.Count - 2, 2);
            byteList.RemoveRange(byteList.Count - 2, 2);
            List<byte> validCode = CRC16(byteList);
            if (validCode.SequenceEqual(checkCRC))
            

                // 进行协议异常码的检查
                // 1-检查功能码  最高位是否为1
                int fc = byteList[1];
                if ((fc & 0x80) == 0x80)
                
                    // 说明报文 异常
                    // 取异常码 
                    throw new Exception(byteList[2].ToString());
                

                // 数据解析
                // 01 03 14 00 00 ...... (CRC)
                byteList.RemoveRange(0, 3);

                List<float> values = new List<float>();
                List<byte> valueBytes = new List<byte>();
                for (int i = 0; i < length / 2; i++)
                
                    valueBytes.Clear();
                    valueBytes.Add(byteList[i * 4 + 3]);
                    valueBytes.Add(byteList[i * 4 + 2]);
                    valueBytes.Add(byteList[i * 4 + 1]);
                    valueBytes.Add(byteList[i * 4]);

                    var value = BitConverter.ToSingle(valueBytes.ToArray(), 0);

                    values.Add(value);
                

                return values;
            
        
        else
        
            Task.Run(async () =>
            
                // 尝试次数     会有升级
                while (true)
                
                    await Task.Delay(2000);

                    if (serialPort.IsOpen)
                    
                        Console.WriteLine("连接正常");
                        continue;
                    

                    try
                    
                        Console.WriteLine("连接断开,尝试重新连接");
                        serialPort.Open();
                    
                    catch (System.IO.IOException ex)
                    
                        Console.WriteLine("设备连接断开");
                        continue;
                    
                
            );
        

        return null;
    

    /// <summary>
    /// CRC校验
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    private List<byte> CRC16(List<byte> value)
    
        ushort poly = 0xA001;
        ushort crcInit = 0xFFFF;

        if (value == null || !value.Any())
            throw new ArgumentException("");

        //运算
        ushort crc = crcInit;
        for (int i = 0; i < value.Count; i++)
        
            crc = (ushort)(crc ^ (value[i]));
            for (int j = 0; j < 8; j++)
            
                crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ poly) : (ushort)(crc >> 1);
            
        
        byte hi = (byte)((crc & 0xFF00) >> 8);  //高位置
        byte lo = (byte)(crc & 0x00FF);         //低位置

        List<byte> buffer = new List<byte>();
        buffer.Add(lo);
        buffer.Add(hi);
        return buffer;
    

以上是关于2021-11-22 WPF上位机 96-Modbus通信代码的封装的主要内容,如果未能解决你的问题,请参考以下文章

2021-11-22 WPF上位机 96-Modbus通信代码的封装

2021-11-22 WPF上位机 93-ModbusTCP/IP消息帧

2021-11-22 WPF上位机 89-Modbus协议

2021-11-22 WPF上位机 90-Modbus协议的分类

2021-11-22 WPF上位机 88-了解Modbus

2021-11-22 WPF上位机 94-Modbus通信数据交换问题