C#之MODBUS RTU通信

C#之MODBUS RTU通信_第1张图片

C#之MODBUS RTU通信_第2张图片
C#之MODBUS RTU通信_第3张图片
C#之MODBUS RTU通信_第4张图片

C#之MODBUS RTU通信_第5张图片
C#之MODBUS RTU通信_第6张图片
C#之MODBUS RTU通信_第7张图片
C#之MODBUS RTU通信_第8张图片
C#之MODBUS RTU通信_第9张图片
C#之MODBUS RTU通信_第10张图片
C#之MODBUS RTU通信_第11张图片
C#之MODBUS RTU通信_第12张图片

from程序段



using ModbusRTULib;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;



namespace Modbus通信
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        ModbusRTU objModbus = new ModbusRTU();//创建ModBusd对象

        ushort dataType = 0;//数据类型:0 Signed;1 Usigned

        /// 
        /// 连接串口
        /// 
        /// 
        /// 
        private void btn_Connect_Click(object sender, EventArgs e)
        {
            try
            {
                objModbus.Connect(this.cbx_PortName.Text, Convert.ToInt32(this.cbx_BaudRate.Text), (Parity)(Convert.ToInt32(this.cbx_Parity.Text)), Convert.ToInt32(this.cbx_DataBits.Text), (StopBits)Convert.ToInt32(this.cbx_StopBits.Text));
                if (objModbus.ConnectState == true)
                {
                    MessageBox.Show("连接成功!");
                }
            }
            catch(Exception ex)
            {
                MessageBox.Show("连接失败:"+ex.ToString());
            }
           
        }

        /// 
        /// 断开串口
        /// 
        /// 
        /// 
        private void btn_DisConnect_Click(object sender, EventArgs e)
        {
            try
            {
                objModbus.DisConnect();
            }
            catch(Exception ex)
            {
                MessageBox.Show("串口关闭失败:" + ex.ToString());
            }
        }

        /// 
        /// 读写操作
        /// 
        /// 
        /// 
        private void btn_WR_Click(object sender, EventArgs e)
        {
            if(objModbus.ConnectState==false)
            {
                MessageBox.Show("串口没有连接!");
                return;
            }
            switch(this.cbx_RWSelect.Text)
            {
                case "01 读取输出线圈":
                    ReadOutputStatus();
                    break;
                case "02 读取输入线圈":
                    ReadInputStatus();
                    break;
                case "03 读取保持寄存器":
                    ReadHoldRegister();
                    break;

                case "04 读取输入寄存器":
                    ReadInputRegister();
                    break;

                case "05 写入单个输出线圈":
                    WriteSingleOutputStatus();
                    break;
                case "06 写入单个保持寄存器":
                    WriteSingleHoldRegister();
                    break;
                case "15 写入多个输出线圈":
                    WriteMultiOutputStatus();
                    break;
                case "16 写入多个保持寄存器":
                    WriteMultiHoldRegister();
                    break;
                default:
                    break;
            }            
        }

        /// 
        /// 读取输出线圈01H
        /// 
        private void ReadOutputStatus()
        {
            try
            {
                byte[] result = objModbus.ReadOutputStatus(Convert.ToByte(this.txt_SlaveAddr.Text), Convert.ToUInt16(this.txt_StartAddr.Text), Convert.ToUInt16(this.txt_RWCount.Text));
                //数据解析
                if (result != null)
                {
                    bool[] boolResult = GetBitArrayFromByteArray(result);
                    string WRResult = string.Empty;
                    for (int i = 0; i < Convert.ToUInt16(this.txt_RWCount.Text); i++)
                    {
                        WRResult += boolResult[i].ToString() + " ";
                    }
                    this.rtb_ReadData.AppendText(WRResult.Trim() + Environment.NewLine);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("读写失败:"+ex.ToString());
                return;
            }
        }

        /// 
        /// 读取输入线圈02H
        /// 
        private void ReadInputStatus()
        {
            try
            {
                byte[] result = objModbus.ReadInputStatus(Convert.ToByte(this.txt_SlaveAddr.Text), Convert.ToUInt16(this.txt_StartAddr.Text), Convert.ToUInt16(this.txt_RWCount.Text));
                //数据解析
                if (result != null)
                {
                    bool[] boolResult = GetBitArrayFromByteArray(result);
                    string WRResult = string.Empty;
                    for (int i = 0; i < Convert.ToUInt16(this.txt_RWCount.Text); i++)
                    {
                        WRResult += boolResult[i].ToString() + " ";
                    }
                    this.rtb_ReadData.AppendText(WRResult.Trim() + Environment.NewLine);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("读写失败:" + ex.ToString());
                return;
            }
        }

        /// 
        /// 读取保持寄存器03H
        /// 
        private void ReadHoldRegister()
        {
            try
            {
                byte[] result = objModbus.ReadHoldRegister(Convert.ToByte(this.txt_SlaveAddr.Text), Convert.ToUInt16(this.txt_StartAddr.Text), Convert.ToUInt16(this.txt_RWCount.Text));
                //解析数据
                if (result != null)
                {  //将字节数组转化为16位的short类型数据
                    switch(dataType)
                    {
                        case 0://Signed
                            for (int i = 0; i < Convert.ToUInt16(this.txt_RWCount.Text); i++)
                            {
                                int calResult = result[2 * i] * 256 + result[2 * i + 1];//16位word转换为int
                                if (calResult>32767)//如果是负数:1、减1;2、按位取反;3、转换为整型;4、加负号;
                                {
                                    calResult = calResult - 1;
                                    byte hi = (byte)(calResult / 256);
                                    byte lo = (byte)(calResult % 256);
                                    hi = (byte)~hi ;
                                    lo = (byte) ~lo;
                                    calResult = hi * 256 + lo;
                                    this.rtb_ReadData.AppendText("-" + calResult + " ");
                                }
                                else//如果是正数
                                {
                                    this.rtb_ReadData.AppendText(result[2 * i] * 256 + result[2 * i + 1] + " ");
                                }                               
                            }
                            this.rtb_ReadData.AppendText(Environment.NewLine);
                            break;
                        case 1://Unsigned
                            for (int i = 0; i < Convert.ToUInt16(this.txt_RWCount.Text); i++)
                            {
                                this.rtb_ReadData.AppendText(result[2 * i] * 256 + result[2 * i + 1] + " ");
                            }
                            this.rtb_ReadData.AppendText(Environment.NewLine);
                            break;
                    }                   
                }
            }
            catch(Exception ex)
            {
                MessageBox.Show("读写失败:" + ex.ToString());
                return;
            }
        }

        /// 
        /// 读取输入寄存器04H
        /// 
        private void ReadInputRegister()
        {
            try
            {
                byte[] result = objModbus.ReadInputRegister(Convert.ToByte(this.txt_SlaveAddr.Text), Convert.ToUInt16(this.txt_StartAddr.Text), Convert.ToUInt16(this.txt_RWCount.Text));
                if (result != null)
                {  //将字节数组转化为16位的short类型数据
                    switch (dataType)
                    {
                        case 0://Signed
                            for (int i = 0; i < Convert.ToUInt16(this.txt_RWCount.Text); i++)
                            {
                                int calResult = result[2 * i] * 256 + result[2 * i + 1];//16位word转换为int
                                if (calResult > 32767)//如果是负数:1、减1;2、按位取反;3、转换为整型;4、加负号;
                                {
                                    calResult = calResult - 1;
                                    byte hi = (byte)(calResult / 256);
                                    byte lo = (byte)(calResult % 256);
                                    hi = (byte)~hi;
                                    lo = (byte)~lo;
                                    calResult = hi * 256 + lo;
                                    this.rtb_ReadData.AppendText("-" + calResult + " ");
                                }
                                else//如果是正数
                                {
                                    this.rtb_ReadData.AppendText(result[2 * i] * 256 + result[2 * i + 1] + " ");
                                }
                            }
                            this.rtb_ReadData.AppendText(Environment.NewLine);
                            break;
                        case 1://Unsigned
                            for (int i = 0; i < Convert.ToUInt16(this.txt_RWCount.Text); i++)
                            {
                                this.rtb_ReadData.AppendText(result[2 * i] * 256 + result[2 * i + 1] + " ");
                            }
                            this.rtb_ReadData.AppendText(Environment.NewLine);
                            break;
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("读写失败:" + ex.ToString());
                return;
            }
        }

        /// 
        /// 写入单个输出线圈05H
        /// 
        private void WriteSingleOutputStatus()
        {
            try
            {
                if (this.rbx_WriteData.Text == "")
                {
                    MessageBox.Show("写入数据为空!");
                    return;
                }//拆分数据并移除空格字符
                string[] writeData = this.rbx_WriteData.Text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                //写入单个输出线圈必须只能有一个数据
                if (writeData.Length != 1)
                {
                    MessageBox.Show("写入方式选择错误!");
                }//每一个布尔量写入长度必须为1
                for (int i = 0; i < writeData.Length; i++)
                {
                    if (writeData[i].Length != 1 || (!(writeData[i] == "0" || writeData[i] == "1")))
                    {
                        MessageBox.Show("写入布尔数据格式错误!");
                    }
                }
                bool boolValue = writeData[0] == "1" ? true : false;
                bool result = objModbus.WriteSingleOutputStatus(Convert.ToByte(this.txt_SlaveAddr.Text), Convert.ToUInt16(this.txt_StartAddr.Text), 1, boolValue);
                if (result != true)
                {
                    MessageBox.Show("单个线圈写入失败!");
                    return;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("读写失败:" + ex.ToString());
                return;
            }
        }

        /// 
        /// 写入单个保持寄存器06H
        /// 
        private void WriteSingleHoldRegister()
        {
            try
            {
                if(this.rbx_WriteData.Text=="")
                {
                    MessageBox.Show("写入数据为空!");
                    return;
                }//拆分数据并移除空格字符
                string[] writeData = this.rbx_WriteData.Text.Split(new char[] { ' ' },StringSplitOptions.RemoveEmptyEntries);
                if(writeData.Length != 1)
                {
                    MessageBox.Show("写入方式选择错误!");
                }
                int intValue = Convert.ToInt32(writeData[0]);
                //判断输入的是Signed还是Unsigned
                if ((dataType==1&&intValue<0)||(dataType == 0 && intValue > 32767))
                {
                    MessageBox.Show("选择的数据类型和输入数据类型不匹配!");
                    return;
                }
                //负数转换
                byte[] writeBytes = new byte[2];//存储单个寄存器的值
                if (intValue < 0)
                {
                    intValue = -intValue;
                    writeBytes[0] = (byte)(intValue / 256);//高位
                    writeBytes[1] = (byte)(intValue % 256);//低位
                    writeBytes[0] = (byte)~writeBytes[0];
                    writeBytes[1] = (byte)~writeBytes[1];
                    intValue = writeBytes[0] * 256 + writeBytes[1] + 1;                   
                }
                writeBytes[0] = (byte)(intValue / 256);//高位
                writeBytes[1] = (byte)(intValue % 256);//低位

                bool result = objModbus.WriteSingleHoldRegister(Convert.ToByte(this.txt_SlaveAddr.Text), Convert.ToUInt16(this.txt_StartAddr.Text),writeBytes);
                if (result == false)
                {
                    MessageBox.Show("单个寄存器写入失败!");
                    return;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("读写失败:" + ex.ToString());
                return;
            }
        }

        /// 
        /// 写入多个输出线圈0FH
        /// 
        private void WriteMultiOutputStatus()
        {
            try
            {
                if (this.rbx_WriteData.Text == "")
                {
                    MessageBox.Show("写入数据为空!");
                    return;
                }//拆分数据并移除空格字符
                string[] writeData = this.rbx_WriteData.Text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                //写入单个输出线圈必须只能有一个数据
                if ((writeData.Length % 8) != 0)
                {
                    MessageBox.Show("写入的数据不是8的整数倍!");
                    return;
                }//每一个布尔量写入长度必须为1
                for (int i = 0; i < writeData.Length; i++)
                {
                    if (writeData[i].Length != 1 || (!(writeData[i] == "0" || writeData[i] == "1")))
                    {
                        MessageBox.Show("写入布尔数据格式错误!");
                        return;
                    }
                }
                List<byte> boolValue = new List<byte>();
                //解析数据
                for(int i=0;i<writeData.Length/8;i++)
                {
                    int calResult=0;
                    for(int j=0;j<8;j++)
                    {
                        calResult += Convert.ToInt32(Math.Pow(2,j)*Convert.ToInt16(writeData[8 * i + j]));
                    }
                    boolValue.Add((byte)calResult);
                }
                bool result = objModbus.WriteMultiOutputStatus(Convert.ToByte(this.txt_SlaveAddr.Text), Convert.ToUInt16(this.txt_StartAddr.Text), 1, boolValue);
                if (result != true)
                {
                    MessageBox.Show("多个线圈写入失败!");
                    return;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("读写失败:" + ex.ToString());
                return;
            }
        }

        /// 
        /// 写入多个保持寄存器10H
        /// 
        private void WriteMultiHoldRegister()
        {
            try
            {
                if (this.rbx_WriteData.Text == "")
                {
                    MessageBox.Show("写入数据为空!");
                    return;
                }//拆分数据并移除空格字符
                string[] writeData = this.rbx_WriteData.Text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                List<byte> writeList = new List<byte>() ;//存储多个寄存器的值
                for (int i=0;i<writeData.Length;i++)
                {
                    int intValue = Convert.ToInt32(writeData[i]);
                    //判断输入的是Signed还是Unsigned
                    if ((dataType == 1 && intValue < 0) || (dataType == 0 && intValue > 32767))
                    {
                        MessageBox.Show("选择的数据类型和输入数据类型不匹配!");
                        return;
                    }
                    //负数转换
                    byte[] writeBytes = new byte[2];
                    if (intValue < 0)
                    {
                        intValue = -intValue;
                        writeBytes[0] = (byte)(intValue / 256);//高位
                        writeBytes[1] = (byte)(intValue % 256);//低位
                        writeBytes[0] = (byte)~writeBytes[0];
                        writeBytes[1] = (byte)~writeBytes[1];
                        intValue = writeBytes[0] * 256 + writeBytes[1] + 1;
                    }
                    writeList.Add((byte)(intValue / 256));//高位
                    writeList.Add((byte)(intValue % 256));//低位
                }
                bool result = objModbus.WriteMultiHoldRegister(Convert.ToByte(this.txt_SlaveAddr.Text), Convert.ToUInt16(this.txt_StartAddr.Text), writeList);
                if (result == false)
                {
                    MessageBox.Show("单个寄存器写入失败!");
                    return;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("读写失败:" + ex.ToString());
                return;
            }
        }



        /// 
        /// 将一个字节转换成布尔数组
        /// 
        /// 字节
        /// 位顺序
        /// 布尔数组
        private bool[] GetBitArrayFromByte(byte b,bool reverse=false)
        {
            bool[] array = new bool[8];

            if(reverse)
            {
                for(int i=7;i>=0;i--)
                {  //对于byte的每一位进行判定
                    array[i] = (b & 1) == 1;  //判定byte的最后一位是否为1,若位1,则是true,否则是false
                    b = (byte)(b >> 1);  //将byte右移1位
                }               
            }
            else
            {
                for (int i = 0; i <= 7; i++)
                {  //对于byte的每一位进行判定
                    array[i] = (b & 1) == 1;//判定byte的最后一位是否为1,若位1,则是true,否则是false
                    b = (byte)(b >> 1);  //将byte右移1位
                }
            }
            return array;
        }

        /// 
        /// 将一个字节数组转换成布尔数组
        /// 
        /// 字节数组
        /// 位顺序
        /// 布尔数组
        private bool[] GetBitArrayFromByteArray(byte[] b, bool reverse = false)
        {
            List<bool> result = new List<bool>();
            foreach(var item in b)
            {
                result.AddRange(GetBitArrayFromByte(item, reverse));
            }
            return result.ToArray();
        }

        /// 
        /// 将一个字节数组转换成整型数组
        /// 
        /// 字节数组
        /// 整型数组
        private int[] GetIntArrayFromByteArray(byte[] b)
        {
            List<int> result = new List<int>();
            for (int i=0;i<b.Length/2; i++)
            {
                result.Add(BitConverter.ToInt16(b, 2*i));
            }
            return result.ToArray();
        }


        /// 
        /// 窗体加载
        /// 
        /// 
        /// 
        private void Form1_Load(object sender, EventArgs e)
        {
            #region 获取设备当前所有的串口
            string[] iAllPortName = null;
            try
            {
                iAllPortName = SerialPort.GetPortNames();
            }
            catch(Exception ex)
            {
                MessageBox.Show("获取设备串口失败:"+ex.ToString());
            }
            this.cbx_PortName.Items.AddRange(iAllPortName);
            if(this.cbx_PortName.Items.Count>0)
            {
                this.cbx_PortName.SelectedIndex = 0;
            }
            #endregion

            #region 波特率
            this.cbx_BaudRate.Items.Add("9600");
            this.cbx_BaudRate.Items.Add("19200");
            if (this.cbx_BaudRate.Items.Count > 0)
            {
                this.cbx_BaudRate.SelectedIndex = 0;
            }
            #endregion

            #region 校验
            this.cbx_Parity.Items.Add("0");
            this.cbx_Parity.Items.Add("1");
            this.cbx_Parity.Items.Add("2");
            if (this.cbx_Parity.Items.Count > 0)
            {
                this.cbx_Parity.SelectedIndex = 0;
            }
            #endregion

            #region 数据位
            this.cbx_DataBits.Items.Add("8");
            this.cbx_DataBits.Items.Add("7");
            if (this.cbx_DataBits.Items.Count > 0)
            {
                this.cbx_DataBits.SelectedIndex = 0;
            }
            #endregion

            #region 停止位
            this.cbx_StopBits.Items.Add("0");
            this.cbx_StopBits.Items.Add("1");
            this.cbx_StopBits.Items.Add("2");
            if (this.cbx_StopBits.Items.Count > 0)
            {
                this.cbx_StopBits.SelectedIndex = 1;
            }
            #endregion

            #region 读写选择
            this.cbx_RWSelect.Items.Add("01 读取输出线圈");
            this.cbx_RWSelect.Items.Add("02 读取输入线圈");
            this.cbx_RWSelect.Items.Add("03 读取保持寄存器");
            this.cbx_RWSelect.Items.Add("04 读取输入寄存器");
            this.cbx_RWSelect.Items.Add("05 写入单个输出线圈");
            this.cbx_RWSelect.Items.Add("06 写入单个保持寄存器");
            this.cbx_RWSelect.Items.Add("15 写入多个输出线圈");
            this.cbx_RWSelect.Items.Add("16 写入多个保持寄存器");
            if (this.cbx_RWSelect.Items.Count > 0)
            {
                this.cbx_RWSelect.SelectedIndex = 0;
            }
            #endregion

            #region 数据类型
            this.cbx_DataType.Items.Add("Signed");
            this.cbx_DataType.Items.Add("Unsigned");
            if (this.cbx_DataType.Items.Count > 0)
            {
                this.cbx_DataType.SelectedIndex = 0;
            }
            #endregion

        }

        /// 
        /// 数据类型选择切换
        /// 
        /// 
        /// 
        private void cbx_DataType_SelectedIndexChanged(object sender, EventArgs e)
        {
            switch (this.cbx_DataType.SelectedItem.ToString())
            {
                case "Signed":
                    dataType = 0;
                    break;
                case "Unsigned":
                    dataType = 1;
                    break;
                default:

                    break;
            }
        }

        /// 
        /// 功能码选择
        /// 
        /// 
        /// 
        private void cbx_RWSelect_SelectedIndexChanged(object sender, EventArgs e)
        {

        }
    }
}

ModbusRTULib的DLL文件代码

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ModbusRTULib
{
    public class ModbusRTU
    {
        #region Field

        private SerialPort MyCom;//创建串口对象
        #endregion

        #region Property
        //超时时间
        public int RcvTimeOut { get; set; } = 2000;
        //连接状态
        public bool ConnectState { get; set; } = false;
        #endregion

        #region Methods

        /// 
        /// 串口连接
        /// 
        /// 串口号
        /// 波特率
        /// 奇偶校验
        /// 数据位
        /// 停止位
        public void Connect(string iPortName, int iBaudRate, Parity iParity, int iDataBits, StopBits iStopBits)
        {
            //实例化串口对象
            MyCom = new SerialPort();
            MyCom.PortName = iPortName;
            MyCom.BaudRate = iBaudRate;
            MyCom.Parity = iParity;
            MyCom.DataBits = iDataBits;
            MyCom.StopBits = iStopBits;
            if (MyCom.IsOpen)
            {
                MyCom.Close();
            }
            MyCom.Open();
            if(MyCom.IsOpen)
            {
                ConnectState = true;
            }           
        }

        /// 
        /// 串口断开
        /// 
        public void DisConnect()
        {
            if(MyCom.IsOpen)
            {
                MyCom.Close();
                ConnectState = false;
            }
        }

        /// 
        /// 读取输出线圈01H
        /// 
        /// 从站号
        /// 起始地址
        /// 读取线圈数量
        /// 
        public byte[] ReadOutputStatus(byte iDevAddr, ushort iStartAddr, ushort iLength)
        {
            //报文拼接
            //站号
            List<byte> sendCommand = new List<byte>();
            sendCommand.Add(iDevAddr);
            //功能码
            sendCommand.Add(0x01);
            //起始地址
            sendCommand.Add((byte)(iStartAddr / 256));
            sendCommand.Add((byte)(iStartAddr % 256));
            //线圈数量
            sendCommand.Add((byte)(iLength / 256));
            sendCommand.Add((byte)(iLength % 256));

            //CRC校验:
            //1、查表的方式;
            sendCommand.AddRange(Crc16(sendCommand.ToArray(), sendCommand.Count));

            //2、根据CRC算法进行编写
            /*byte hi, lo;//CRC校验的高位和低位
            CalculateCRC(sendCommand.ToArray(), sendCommand.Count, out hi, out lo);
            //低位在前,高位在后
            sendCommand.Add(lo);
            sendCommand.Add(hi);*/

            byte[] response = null;

            int byteLength = iLength % 8 == 0 ? iLength / 8 : iLength / 8 + 1;
            if(SendAndReceive(sendCommand.ToArray(),ref response))
            {
                //验证报文
                if(response.Length==5+byteLength)
                {
                    if(response[0]==iDevAddr&&response[1]==0x01&&response[2]==byteLength&&CheckCRC(response))
                    {
                        return GetByteArray(response, 3, response.Length-5);
                    }
                }
            }
            else
            {
                return null;
            }          
            return null;
        }

        /// 
        /// 读取输入线圈02H
        /// 
        /// 从站号
        /// 起始地址
        /// 读取线圈数量
        /// 
        public byte[] ReadInputStatus(byte iDevAddr, ushort iStartAddr, ushort iLength)
        {
            //报文拼接
            //站号
            List<byte> sendCommand = new List<byte>();
            sendCommand.Add(iDevAddr);
            //功能码
            sendCommand.Add(0x02);
            //起始地址
            sendCommand.Add((byte)(iStartAddr / 256));
            sendCommand.Add((byte)(iStartAddr % 256));
            //线圈数量
            sendCommand.Add((byte)(iLength / 256));
            sendCommand.Add((byte)(iLength % 256));

            //CRC校验:
            //1、查表的方式;
            sendCommand.AddRange(Crc16(sendCommand.ToArray(), sendCommand.Count));

            //2、根据CRC算法进行编写
            /*byte hi, lo;//CRC校验的高位和低位
            CalculateCRC(sendCommand.ToArray(), sendCommand.Count, out hi, out lo);
            //低位在前,高位在后
            sendCommand.Add(lo);
            sendCommand.Add(hi);*/

            byte[] response = null;

            int byteLength = iLength % 8 == 0 ? iLength / 8 : iLength / 8 + 1;
            if (SendAndReceive(sendCommand.ToArray(), ref response))
            {
                //验证报文
                if (response.Length == 5 + byteLength)
                {
                    if (response[0] == iDevAddr && response[1] == 0x02 && response[2] == byteLength && CheckCRC(response))
                    {
                        return GetByteArray(response, 3, response.Length - 5);
                    }
                }
            }
            else
            {
                return null;
            }
            return null;
        }

        /// 
        /// 读取保持性寄存器03H
        /// 
        /// 从站号
        /// 起始地址
        /// 读取寄存器数量
        /// 
        public byte[] ReadHoldRegister(byte iDevAddr, ushort iStartAddr, ushort iLength)
        {
            //报文拼接
            //站号
            List<byte> sendCommand = new List<byte>();
            sendCommand.Add(iDevAddr);
            //功能码
            sendCommand.Add(0x03);
            //起始地址
            sendCommand.Add((byte)(iStartAddr / 256));
            sendCommand.Add((byte)(iStartAddr % 256));
            //寄存器数量
            sendCommand.Add((byte)(iLength / 256));
            sendCommand.Add((byte)(iLength % 256));

            //CRC校验
            //1、查表法CRC计算
            //sendCommand.AddRange(Crc16(sendCommand.ToArray(), sendCommand.Count));

            //2、根据CRC算法进行编写
            byte hi, lo;//CRC校验的高位和低位
            CalculateCRC(sendCommand.ToArray(), sendCommand.Count, out hi, out lo);
            //低位在前,高位在后
            sendCommand.Add(lo);
            sendCommand.Add(hi);

            byte[] response = null;

            int byteLength = iLength * 2;
            if (SendAndReceive(sendCommand.ToArray(), ref response))
            {
                //验证报文
                if (response.Length == 5 + byteLength)
                {
                    if (response[0] == iDevAddr && response[1] == 0x03 && response[2] == byteLength && CheckCRC(response))
                    {
                        return GetByteArray(response, 3, response.Length - 5);
                    }
                }
            }
            else
            {
                return null;
            }
            return null;
        }

        /// 
        /// 读取输入寄存器04H
        /// 
        /// 从站号
        /// 起始地址
        /// 读取寄存器数量
        /// 
        public byte[] ReadInputRegister(byte iDevAddr, ushort iStartAddr, ushort iLength)
        {
            //报文拼接
            //站号
            List<byte> sendCommand = new List<byte>();
            sendCommand.Add(iDevAddr);
            //功能码
            sendCommand.Add(0x04);
            //起始地址
            sendCommand.Add((byte)(iStartAddr / 256));
            sendCommand.Add((byte)(iStartAddr % 256));
            //寄存器数量
            sendCommand.Add((byte)(iLength / 256));
            sendCommand.Add((byte)(iLength % 256));

            //CRC校验
            //1、查表法CRC计算
            //sendCommand.AddRange(Crc16(sendCommand.ToArray(), sendCommand.Count));

            //2、根据CRC算法进行编写
            byte hi, lo;//CRC校验的高位和低位
            CalculateCRC(sendCommand.ToArray(), sendCommand.Count, out hi, out lo);
            //低位在前,高位在后
            sendCommand.Add(lo);
            sendCommand.Add(hi);

            byte[] response = null;

            int byteLength = iLength * 2;
            if (SendAndReceive(sendCommand.ToArray(), ref response))
            {
                //验证报文
                if (response.Length == 5 + byteLength)
                {
                    if (response[0] == iDevAddr && response[1] == 0x04 && response[2] == byteLength && CheckCRC(response))
                    {
                        return GetByteArray(response, 3, response.Length - 5);
                    }
                }
            }
            else
            {
                return null;
            }
            return null;
        }

        /// 
        /// 写入单个输出线圈05H
        /// 
        /// 从站号
        /// 起始地址
        /// 写入线圈数量
        /// 写入线圈值
        /// 
        public bool WriteSingleOutputStatus(byte iDevAddr, ushort iStartAddr, ushort iLength, bool iValue)
        {
            //报文拼接
            //站号
            List<byte> sendCommand = new List<byte>();
            sendCommand.Add(iDevAddr);
            //功能码
            sendCommand.Add(0x05);
            //起始地址
            sendCommand.Add((byte)(iStartAddr / 256));
            sendCommand.Add((byte)(iStartAddr % 256));
            //线圈值
            byte[] outputStatus = new byte[2];
            if(iValue==true)
            {
                sendCommand.Add(0xFF);
                sendCommand.Add(0x00);
                outputStatus[0] = 0xFF;
                outputStatus[1] = 0x00;
            }
            else
            {
                sendCommand.Add(0x00);
                sendCommand.Add(0x00);
                outputStatus[0] = 0x00;
                outputStatus[1] = 0x00;
            }
            //CRC校验:
            //1、查表的方式;
            sendCommand.AddRange(Crc16(sendCommand.ToArray(), sendCommand.Count));

            //2、根据CRC算法进行编写
            /*byte hi, lo;//CRC校验的高位和低位
            CalculateCRC(sendCommand.ToArray(), sendCommand.Count, out hi, out lo);
            //低位在前,高位在后
            sendCommand.Add(lo);
            sendCommand.Add(hi);*/

            byte[] response = null;

            if (SendAndReceive(sendCommand.ToArray(), ref response))
            {
                //验证报文
                if (response.Length == 8)
                {
                    if (response[0] == iDevAddr && response[1] == 0x05 && response[2] == ((byte)(iStartAddr / 256)) && response[3] == ((byte)(iStartAddr % 256))&& response[4]== outputStatus[0] && response[5] == outputStatus[1] && CheckCRC(response))
                    {
                        return true;
                    }
                }
            }
            else
            {
                return false;
            }
            return false;
        }

        /// 
        /// 写入单个字保持性寄存器06H
        /// 
        /// 从站号
        /// 起始地址
        /// 写入单个字寄存器数量
        /// 
        public bool WriteSingleHoldRegister(byte iDevAddr, ushort iStartAddr, byte[] iWriteByte)
        {
            //报文拼接
            //站号
            List<byte> sendCommand = new List<byte>();
            sendCommand.Add(iDevAddr);
            //功能码
            sendCommand.Add(0x06);
            //起始地址
            sendCommand.Add((byte)(iStartAddr / 256));
            sendCommand.Add((byte)(iStartAddr % 256));

            //需要写入的数据
            sendCommand.Add(iWriteByte[0]);
            sendCommand.Add(iWriteByte[1]);

            //CRC校验
            //1、查表法CRC计算
            //sendCommand.AddRange(Crc16(sendCommand.ToArray(), sendCommand.Count));

            //2、根据CRC算法进行编写
            byte hi, lo;//CRC校验的高位和低位
            CalculateCRC(sendCommand.ToArray(), sendCommand.Count, out hi, out lo);
            //低位在前,高位在后
            sendCommand.Add(lo);
            sendCommand.Add(hi);

            byte[] response = null;

            int byteLength = 2;
            if (SendAndReceive(sendCommand.ToArray(), ref response))
            {
                //验证报文
                if (response.Length == 6 + byteLength)
                {
                    if (response[0] == iDevAddr && response[1] == 0x06 && response[2] == (iStartAddr / 256) && response[3] == (iStartAddr % 256) && CheckCRC(response))
                    {
                        return true;
                    }
                }
            }
            else
            {
                return false;
            }
            return false;
        }

        /// 
        /// 写入多个输出线圈0FH
        /// 
        /// 从站号
        /// 起始地址
        /// 写入线圈数量
        /// 写入线圈值
        /// 
        public bool WriteMultiOutputStatus(byte iDevAddr, ushort iStartAddr, ushort iLength, List<byte> iValue)
        {
            //报文拼接
            //站号
            List<byte> sendCommand = new List<byte>();
            sendCommand.Add(iDevAddr);
            //功能码
            sendCommand.Add(0x0F);
            //起始地址
            sendCommand.Add((byte)(iStartAddr / 256));
            sendCommand.Add((byte)(iStartAddr % 256));
            //线圈数量
            sendCommand.Add((byte)((iValue.Count * 8) / 256));
            sendCommand.Add((byte)((iValue.Count * 8) % 256));
            //字节数
            sendCommand.Add((byte)(iValue.Count));
            //线圈值
            for (int i=0;i<iValue.Count;i++)
            {
                sendCommand.Add(iValue[i]);
            }
            //CRC校验:
            //1、查表的方式;
            sendCommand.AddRange(Crc16(sendCommand.ToArray(), sendCommand.Count));

            //2、根据CRC算法进行编写
            /*byte hi, lo;//CRC校验的高位和低位
            CalculateCRC(sendCommand.ToArray(), sendCommand.Count, out hi, out lo);
            //低位在前,高位在后
            sendCommand.Add(lo);
            sendCommand.Add(hi);*/

            byte[] response = null;

            if (SendAndReceive(sendCommand.ToArray(), ref response))
            {
                //验证报文
                if (response.Length == 8)
                {
                    if (response[0] == iDevAddr && response[1] == 0x0F && response[2] == ((byte)(iStartAddr / 256)) && response[3] == ((byte)(iStartAddr % 256)) && response[4] == ((byte)iValue.Count*8/256) && response[5] == ((byte)iValue.Count*8%256) && CheckCRC(response))
                    {
                        return true;
                    }
                }
            }
            else
            {
                return false;
            }
            return false;
        }

        /// 
        /// 写入多个字保持性寄存器10H
        /// 
        /// 从站号
        /// 起始地址
        /// 写入多个字寄存器数量
        /// 
        public bool WriteMultiHoldRegister(byte iDevAddr, ushort iStartAddr, List<byte> iWriteByte)
        {
            //报文拼接
            //站号
            List<byte> sendCommand = new List<byte>();
            sendCommand.Add(iDevAddr);
            //功能码
            sendCommand.Add(0x10);
            //起始地址
            sendCommand.Add((byte)(iStartAddr / 256));
            sendCommand.Add((byte)(iStartAddr % 256));

            //写入数据的数量
            int writeCount = iWriteByte.Count / 2;
            sendCommand.Add((byte)(writeCount / 256));
            sendCommand.Add((byte)(writeCount % 256));

            //写入字节计数
            sendCommand.Add((byte)(iWriteByte.Count));

            //需要写入的数据
            for(int i=0;i< writeCount;i++)
            {
                sendCommand.Add(iWriteByte[2*i]);
                sendCommand.Add(iWriteByte[2*i+1]);
            }
            //CRC校验
            //1、查表法CRC计算
            //sendCommand.AddRange(Crc16(sendCommand.ToArray(), sendCommand.Count));

            //2、根据CRC算法进行编写
            byte hi, lo;//CRC校验的高位和低位
            CalculateCRC(sendCommand.ToArray(), sendCommand.Count, out hi, out lo);
            //低位在前,高位在后
            sendCommand.Add(lo);
            sendCommand.Add(hi);

            byte[] response = null;

            int byteLength = 2;
            if (SendAndReceive(sendCommand.ToArray(), ref response))
            {
                //验证报文
                if (response.Length == 6 + byteLength)
                {
                    if (response[0] == iDevAddr && response[1] == 0x10 && response[2] == (iStartAddr / 256) && response[3] == (iStartAddr % 256) && response[4] == (writeCount / 256) && response[5] == (writeCount % 256) && CheckCRC(response))
                    {
                        return true;
                    }
                }
            }
            else
            {
                return false;
            }
            return false;
        }

        /// 
        /// 截取数组
        /// 
        /// 
        /// 
        /// 
        /// 
        private byte[] GetByteArray(byte[] source, int start, int count)
        {
            if (source == null || source?.Length <= 0) return null;
            if (start < 0 || count < 0) return null;
            if (source.Length < start + count) return null;
            byte[] result = new byte[count];
            Array.Copy(source, start, result, 0, count);
            return result;
        }


        /// 
        /// CRC校验
        /// 
        /// 
        /// 
        private bool CheckCRC(byte[] response)
        {
            byte[] crc = Crc16(response, response.Length - 2);
            if(crc[0]==response[response.Length-2]&&crc[1]==response[response.Length-1])
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// 
        /// 发送并接收报文
        /// 
        /// 
        /// 
        /// 
        private bool SendAndReceive(byte[] send, ref byte[] response)
        {
            try
            {
                MyCom.Write(send, 0, send.Length);
                byte[] buffer = new byte[1024];

                MemoryStream ms = new MemoryStream();

                DateTime start = DateTime.Now;

                //串口的接收事件:
                while (true)
                {
                    Thread.Sleep(20);
                    if (MyCom.BytesToRead > 0)
                    {
                        int count = MyCom.Read(buffer, 0, buffer.Length);
                        ms.Write(buffer, 0, count);
                    }
                    else
                    {
                        if ((DateTime.Now - start).TotalMilliseconds > this.RcvTimeOut)
                        {
                            ms.Dispose();
                            return false;
                        }
                        else if (ms.Length > 0)
                        {
                            break;
                        }
                    }
                }
                response = ms.ToArray();
                return true;
            }
            catch
            {
                return false;
            }
        }

        #region  CRC校验
        #region 查表法
        /// 
        /// 高位表
        /// 
        private static readonly byte[] aucCRCCHi =
        {
            0x00,0xc1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,
            0x00,0xc1,0x81,0x40,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
            0x00,0xc1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,
            0x00,0xc1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
            0x00,0xc1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,
            0x00,0xc1,0x81,0x40,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
            0x00,0xc1,0x81,0x40,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
            0x01,0xc0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
            0x00,0xc1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,
            0x00,0xc1,0x81,0x40,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
            0x00,0xc1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,
            0x00,0xc1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
            0x00,0xc1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,
            0x00,0xc1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
            0x01,0xc0,0x80,0x41,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
            0x00,0xc1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
            0x00,0xc1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,
            0x00,0xc1,0x81,0x40,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
            0x00,0xc1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,
            0x00,0xc1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
            0x00,0xc1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,
            0x00,0xc1,0x81,0x40
        };
        /// 
        /// 低位表
        /// 
        private static readonly byte[] aucCRCCLo =
        {
            0x00,0xc0,0xc1,0x01,0xc3,0x03,0x02,0xc2,0xc6,0x06,0x07,0xc7,
            0x05,0xc5,0xc4,0x04,0xcc,0x0c,0x0d,0xcd,0x0f,0xcf,0xce,0x0e,
            0x0a,0xca,0xcb,0x0b,0xc9,0x09,0x08,0xc8,0xd8,0x18,0x19,0xd9,
            0x1b,0xdb,0xda,0x1a,0x1e,0xde,0xdf,0x1f,0xdd,0x1d,0x1c,0xdc,
            0x14,0xd4,0xd5,0x15,0xd7,0x17,0x16,0xd6,0xd2,0x12,0x13,0xd3,
            0x11,0xd1,0xd0,0x10,0xf0,0x30,0x31,0xf1,0x33,0xf3,0xf2,0x32,
            0x36,0xf6,0xf7,0x37,0xf5,0x35,0x34,0xf4,0x3c,0xfc,0xfd,0x3d,
            0xff,0x3f,0x3e,0xfe,0xfa,0x3a,0x3b,0xfb,0x39,0xf9,0xf8,0x38,
            0x28,0xe8,0xe9,0x29,0xeb,0x2b,0x2a,0xea,0xee,0x2e,0x2f,0xef,
            0x2d,0xed,0xec,0x2c,0xe4,0x24,0x25,0xe5,0x27,0xe7,0xe6,0x26,
            0x22,0xe2,0xe3,0x23,0xe1,0x21,0x20,0xe0,0xa0,0x60,0x61,0xa1,
            0x63,0xa3,0xa2,0x62,0x66,0xa6,0xa7,0x67,0xa5,0x65,0x64,0xa4,
            0x6c,0xac,0xad,0x6d,0xaf,0x6f,0x6e,0xae,0xaa,0x6a,0x6b,0xab,
            0x69,0xa9,0xa8,0x68,0x78,0xb8,0xb9,0x79,0xbb,0x7b,0x7a,0xba,
            0xbe,0x7e,0x7f,0xbf,0x7d,0xbd,0xbc,0x7c,0xb4,0x74,0x75,0xb5,
            0x77,0xb7,0xb6,0x76,0x72,0xb2,0xb3,0x73,0xb1,0x71,0x70,0xb0,
            0x50,0x90,0x91,0x51,0x93,0x53,0x52,0x92,0x96,0x56,0x57,0x97,
            0x55,0x95,0x94,0x54,0x9c,0x5c,0x5d,0x9d,0x5f,0x9f,0x9e,0x5e,
            0x5a,0x9a,0x9b,0x5b,0x99,0x59,0x58,0x98,0x88,0x48,0x49,0x89,
            0x4b,0x8b,0x8a,0x4a,0x4e,0x8e,0x8f,0x4f,0x8d,0x4d,0x4c,0x8c,
            0x44,0x84,0x85,0x45,0x87,0x47,0x46,0x86,0x82,0x42,0x43,0x83,
            0x41,0x81,0x80,0x40
        };

        /// 
        /// 查表的方式CRC校验
        /// 
        /// 
        /// 
        /// 
        private byte[] Crc16(byte[] pucFrame, int usLen)
        {
            int i = 0;
            byte[] res = new byte[2] { 0xff, 0xff };
            UInt16 iIndex = 0x0000;
            while(usLen-->0)
            {
                iIndex = (UInt16)(res[0] ^ pucFrame[i++]);
                res[0] = (byte)(res[1] ^ aucCRCCHi[iIndex]);
                res[1] = aucCRCCLo[iIndex];
            }
            return res;
        }
        #endregion

        #region 计算法
        /// 
        /// 计算CheckSum
        /// 
        /// 
        /// 
        /// 
        private void CalculatepCheckSum(byte[] pByte, int nNumberOfBytes, out ushort pCheckSum)
        {
            int nBit;
            ushort nShiftedBit;
            pCheckSum = 0xFFFF;
            for(int nByte=0; nByte<nNumberOfBytes; nByte++)
            {
                pCheckSum ^= pByte[nByte];
                for(nBit=0; nBit<8; nBit++)
                {
                    if((pCheckSum & 0x1)==1)
                    {
                        nShiftedBit = 1;
                    }
                    else
                    {
                        nShiftedBit = 0;
                    }
                    pCheckSum >>= 1;
                    if(nShiftedBit!=0)
                    {
                        pCheckSum ^= 0xA001;
                    }
                }
            }
        }

        /// 
        /// 算法的方式CRC校验
        /// 
        /// 
        /// 
        /// 
        /// 
        private void CalculateCRC(byte[] pByte,int nNumberOfBytes, out byte hi, out byte lo)
        {
            ushort sum;
            CalculatepCheckSum(pByte, nNumberOfBytes, out sum);
            lo = (byte)(sum & 0xff);
            hi = (byte)((sum & 0xFF00) >> 8);

        }
        #endregion

        #endregion

        #endregion

    }
}


你可能感兴趣的:(c#,modbus)