• Modbus Com SerialPort


    项目中用到的工具,串口modbus协议读写数据。

        public class ModbusHelper
        {
            private readonly SerialPort _serialPort;
            private readonly ILog _logger;
            private bool _isReceivedData = false;
            private byte _modbusAdd = 0x02;
            private int _readTimes = 0;
    
            public ModbusHelper(string port, int baudRate, byte slave)
            {
                try
                {
                    _modbusAdd = slave;
                    _logger = LogManager.GetLogger(this.GetType());
                    _serialPort = new SerialPort(port, baudRate, Parity.Even, 8, StopBits.One);
                    _serialPort.DataReceived += _serialPort_DataReceived;
                    _logger = LogManager.GetLogger(this.GetType());
                }
                catch (Exception e)
                {
                    _logger.Error("Modbus串口初始化失败", e);
                }
            }
    
            public bool Open()
            {
                try
                {
                    if (!_serialPort.IsOpen)
                    {
                        _serialPort.Open();
                    }
                    _logger.Debug("串口" + _serialPort.PortName + "正常打开");
                    return true;
                }
                catch (Exception e)
                {
                    _logger.Error("打开串口打开失败", e);
                    return false;
                }
            }
    
            public bool Close()
            {
                try
                {
                    if (_serialPort.IsOpen)
                    {
                        _serialPort.Close();
                        _serialPort.Dispose();
                    }
                    _logger.Debug("串口" + _serialPort.PortName + "正常关闭");
                    return true;
                }
                catch (Exception err)
                {
                    _logger.Error("关闭串口失败", err);
                    return false;
                }
            }
    
            private void ResetBuffer()
            {
                _readTimes = 0;
                _serialPort.DiscardInBuffer();
            }
    
            /// <summary>
            /// 串口寄存器写值
            /// </summary>
            /// <param name="beginAddress">开始写入相对地址</param>
            /// <param name="data">写入数据</param>
            public void WriteSingleWord(short beginAddress, short data)
            {
                try
                {
                    var writeData = new List<byte>(0);
                    writeData.Add(_modbusAdd);
                    writeData.Add(0x06);
                    writeData.AddRange(BitConverter.GetBytes(beginAddress).Reverse());
                    writeData.AddRange(BitConverter.GetBytes(data).Reverse());
                    byte[] crc = CrcCheck(writeData.ToArray());
                    writeData.AddRange(crc.Reverse());
    
                    if (_serialPort.IsOpen)
                    {
                        ResetBuffer();
                        _serialPort.Write(writeData.ToArray(), 0, writeData.Count);
                        _history.Add(writeData);
                        _serialPort.ReceivedBytesThreshold = 8;
                        while (_readTimes < 20 && !_isReceivedData)
                        {
                            _readTimes++;
                            Thread.Sleep(100);
                        }
                        _logger.Debug("写单字数据成功,数据:" + string.Join(",", writeData));
                    }
                }
                catch (Exception err)
                {
                    _logger.Error("写入单字数据时失败", err);
                }
            }
    
            /// <summary>
            /// 向寄存器写入多个字
            /// </summary>
            /// <param name="beginAddress">起始地址(相对地址)</param>
            /// <param name="writeLen">写入的字数</param>
            /// <param name="data">按字写入数据(字节高地址,字节低地址;字节高地址,字节低地址;)</param>
            public void WriteMutiWord(short beginAddress, short writeLen, byte[] data)
            {
                try
                {
                    var writeData = new List<byte>(0);
                    writeData.Add(_modbusAdd);
                    writeData.Add(0x10);
                    writeData.AddRange(BitConverter.GetBytes(beginAddress).Reverse());
                    writeData.AddRange(BitConverter.GetBytes(writeLen).Reverse());
                    writeData.Add((byte)(writeLen * 2));//写入的字节长度
                    writeData.AddRange(data);
                    byte[] crc = CrcCheck(writeData.ToArray());
                    writeData.AddRange(crc.Reverse());
    
                    if (_serialPort.IsOpen)
                    {
                        ResetBuffer();
                        _serialPort.Write(writeData.ToArray(), 0, writeData.Count);
                        _history.Add(writeData);
                        _serialPort.ReceivedBytesThreshold = 8;
                        while (_readTimes < 20 && !_isReceivedData)
                        {
                            _readTimes++;
                            Thread.Sleep(100);
                        }
                        _logger.Debug("多字写数据成功,数据:" + string.Join(",", writeData));
                    }
                }
                catch (Exception err)
                {
                    _logger.Error("往串口写入数据时失败", err);
                }
            }
    
            /// <summary>
            /// 向寄存器写入双字
            /// </summary>
            /// <param name="beginAddress">起始地址(相对地址)</param>
            /// <param name="data">写入数据</param>
            public void WriteDoubleWord(short beginAddress, int data)
            {
                try
                {
                    var writeData = new List<byte>(0);
                    writeData.Add(_modbusAdd);
                    writeData.Add(0x10);
                    writeData.AddRange(BitConverter.GetBytes(beginAddress).Reverse());
                    writeData.Add(0);//寄存器数量高字节
                    writeData.Add(2);//寄存器数量低字节
                    writeData.Add(4);//写入的字节长度
                    writeData.AddRange(BitConverter.GetBytes(data).Reverse());
    
                    byte[] crc = CrcCheck(writeData.ToArray());
                    writeData.AddRange(crc.Reverse());
    
                    if (_serialPort.IsOpen)
                    {
                        ResetBuffer();
                        _serialPort.Write(writeData.ToArray(), 0, writeData.Count);
                        _history.Add(writeData);
                        _serialPort.ReceivedBytesThreshold = 8;
                        while (_readTimes < 20 && !_isReceivedData)
                        {
                            _readTimes++;
                            Thread.Sleep(100);
                        }
                        _logger.Debug("写双字数据成功,数据:" + string.Join(",", writeData));
                    }
                }
                catch (Exception err)
                {
                    _logger.Error("写入双字数据时失败", err);
                }
            }
            
            /// <summary>
            /// 串口读取寄存器值
            /// </summary>
            /// <param name="beginAddress">开始读取相对地址</param>
            /// <param name="readLen">读取的字数</param>
            /// <returns></returns>
            public byte[] Read(short beginAddress, short readLen)
            {
                try
                {
                    var readData = new List<byte>(0);
                    readData.Add(_modbusAdd);
                    readData.Add(0x03);
                    readData.AddRange(BitConverter.GetBytes(beginAddress).Reverse());
                    readData.AddRange(BitConverter.GetBytes(readLen).Reverse());
                    byte[] crc = CrcCheck(readData.ToArray());
                    readData.AddRange(crc.Reverse());
    
                    if (_serialPort.IsOpen)
                    {
                        ResetBuffer();
                        _serialPort.Write(readData.ToArray(), 0, readData.Count);
                        List<string> logdata = new List<string>();
                        readData.ForEach(x => logdata.Add(Convert.ToString(x, 16).ToUpper().PadLeft(2,'0')));
                        _logger.Debug("串口写入数据:" + string.Join(" ", logdata));
                        _serialPort.ReceivedBytesThreshold = 5 + readLen * 2;
                        Thread.Sleep(50);
                        while (_serialPort.BytesToRead == 0 && _readTimes < 60)
                        {
                            _readTimes++;
                            Thread.Sleep(50);
                        }
                        if (_serialPort.BytesToRead > 0)
                        {
                            var bytesToRead = _serialPort.BytesToRead;
                            byte[] buffer = new byte[bytesToRead];
                            _serialPort.Read(buffer, 0, bytesToRead);
                            _serialPort.DiscardInBuffer();
    
                            _logger.Debug("串口返回的数据:" + string.Join(",", buffer));
                            int readDataLength = bytesToRead - 5;//减去开头的modbus地址、功能码、字节数,和结尾的2字节校验位
                            if (readDataLength > 0)
                            {
                                return buffer.Skip(3).Take(readDataLength).ToArray();
                            }
                        }
                        return null;
                    }
                    return null;
                }
                catch (Exception err)
                {
                    _logger.Error("写入<读取>指令时失败", err);
                    return null;
                }
            }
    
            private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
            {
                try
                {
    
                }
                catch (Exception err)
                {
                    _logger.Error("读取串口数据失败", err);
                }
            }
    
            /// <summary>
            /// 获取Modbus校验值
            /// </summary>
            /// <param name="data">数据字节流</param>
            /// <returns>返回2byte,byte0高位,byte1低位</returns>
            private byte[] CrcCheck(byte[] data)
            {
                try
                {
                    byte CRC16Lo;
                    byte CRC16Hi;
                    byte CL; byte CH;
                    byte SaveHi; byte SaveLo;
                    byte[] tmpData;
                    int Flag;
                    CRC16Lo = 0xFF;
                    CRC16Hi = 0xFF;
                    CL = 0x01;
                    CH = 0xA0;
                    tmpData = data;
                    for (int i = 0; i < tmpData.Length; i++)
                    {
                        CRC16Lo = (byte)(CRC16Lo ^ tmpData[i]);
                        for (Flag = 0; Flag <= 7; Flag++)
                        {
                            SaveHi = CRC16Hi;
                            SaveLo = CRC16Lo;
                            CRC16Hi = (byte)(CRC16Hi >> 1);
                            CRC16Lo = (byte)(CRC16Lo >> 1);
                            if ((SaveHi & 0x01) == 0x01)
                            {
                                CRC16Lo = (byte)(CRC16Lo | 0x80);
                            }
    
                            if ((SaveLo & 0x01) == 0x01)
                            {
                                CRC16Hi = (byte)(CRC16Hi ^ CH);
                                CRC16Lo = (byte)(CRC16Lo ^ CL);
                            }
                        }
                    }
                    byte[] ReturnData = new byte[2];
                    ReturnData[0] = CRC16Hi;
                    ReturnData[1] = CRC16Lo;
                    return ReturnData;
                }
                catch (Exception err)
                {
                    _logger.Error("计算crc校验值出错", err);
                    return null;
                }
            }
    
            private readonly List<List<byte>> _history = new List<List<byte>>();
    
            public void ClearHistory()
            {
                _history.Clear();
            }
    
            public void WriteHistory()
            {
                foreach (List<byte> item in _history)
                {
                    _serialPort.Write(item.ToArray(), 0, item.Count);
                    _serialPort.ReceivedBytesThreshold = 8;
                    while (_readTimes < 10 && !_isReceivedData)
                    {
                        _readTimes++;
                        Thread.Sleep(100);
                    }
                }
            }
    
        }
  • 相关阅读:
    HTTP协议
    C# 调用接口实例httpclient(postman)
    什么是Web Service?什么是soap?
    windows服务启动失败解决流程(1053错误举例)
    C# 创建window服务 -- 定时任务
    自定义Log日志
    C# 调用web service soap接口(wsdl文件)(一) --- 接口请求
    C# List转Json,Json转List
    MVC+EF+SQL Server项目创建数据库连接流程
    查询库中所有的表及表中记录总条数
  • 原文地址:https://www.cnblogs.com/jonney-wang/p/9882521.html
Copyright © 2020-2023  润新知