03_仿Modbus工具案例

花了6个小时写的Modbus通信案例,通信方式包括RTU,ASCII,TCP,UTP。
案例图:

 

 

 

using Modbus.Data;
using Modbus.Device;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.AxHost;

namespace MyModbusPS
{
    public partial class Form1 : Form
    {
        private ModbusMaster master;//主站
        private ModbusSlave slave;//从站

        public delegate void Logdelegate(string log);
        private Logdelegate Log;


        public Form1()
        {
            InitializeComponent();

            InotMasterUI();
            InotSlaveUI();

            Log = new Logdelegate((s) =>
            {
                string time = DateTime.Now.ToString("HH:mm:ss");
                Slave_RZ.AppendText($"{time}:{s} \r\n");
                Slave_RZ.SelectionStart = Slave_RZ.Text.Length;
                Slave_RZ.ScrollToCaret();
            });
        }

        #region 初始化主站UI数据
        /// <summary>
        /// 初始化主站UI数据
        /// </summary>
        private void InotMasterUI()
        {
            Master_TXFS.Items.Add("SerialIPort");
            Master_TXFS.Items.Add("TCP");
            Master_TXFS.Items.Add("UDP");
            Master_TXFS.SelectedIndex = 0;
            Master_IP.Text = "COM1";
            Master_DK.Text = "";
            Master_RTU.Checked = true;

            Master_dataGridView.AllowUserToAddRows = false;
            Master_dataGridView.RowHeadersWidth = 60;
            Master_dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;

            Master_dataGridView.AllowUserToOrderColumns = false;
            //Master_dataGridView.Columns[1].SortMode=DataGridViewColumnSortMode.NotSortable;

            Master_dataGridView.Columns.Add("Alias", "Alias");
            Master_dataGridView.Columns.Add("00000", "00000");

            for (int i = 0; i < 10; i++)
            {
                Master_dataGridView.Rows.Add();
                Master_dataGridView.Rows[i].HeaderCell.Value = i.ToString();
            }

            //从站地址
            Master_CZDZ.Text = "1";
        }
        #endregion

        #region 主站通信方式选择事件
        /// <summary>
        /// 主站通信方式选择事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Master_TXFS_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (Master_TXFS.SelectedIndex == 0)
            {
                Master_IP.Text = "COM1";
                Master_DK.Text = "";
                Master_RTU.Visible = true;
                Master_ASCII.Visible = true;
                Master_RTU.Checked = true;
            }
            else
            {
                Master_IP.Text = "127.0.0.1";
                Master_DK.Text = "8080";
                Master_RTU.Visible = false;
                Master_ASCII.Visible = false;
                Master_RTU.Checked = false;
                Master_ASCII.Checked = false;
            }
        }
        #endregion

        #region 连接
        /// <summary>
        /// 连接
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Master_LJ_Click(object sender, EventArgs e)
        {
            if (Master_LJ.Text == "连接")
            {
                StartMaster();

            }
            else
            {
                StopMaster();
            }
        }

        /// <summary>
        /// 连接
        /// </summary>
        private void StartMaster()
        {
            try
            {
                if (Master_TXFS.SelectedIndex == 0)//开启RTU或Ascii
                {
                    SerialPort port = new SerialPort();
                    port.PortName = Master_IP.Text;//通信方式
                    port.BaudRate = 9600;//波特率
                    port.DataBits = 8;//数据位
                    port.Parity = Parity.Even;//奇偶校验
                    port.StopBits = StopBits.One;//停止位
                    port.Open();
                    if (Master_RTU.Checked)
                    {
                        master = ModbusSerialMaster.CreateRtu(port);
                    }
                    else if (Master_ASCII.Checked)
                    {
                        master = ModbusSerialMaster.CreateAscii(port);
                    }
                }
                else if (Master_TXFS.SelectedIndex == 1)//开启TCP
                {
                    string ip = Master_IP.Text;
                    ushort port = ushort.Parse(Master_DK.Text);
                    TcpClient tcpClient = new TcpClient(ip, port);

                    master = ModbusIpMaster.CreateIp(tcpClient);
                }
                else if (Master_TXFS.SelectedIndex == 2)//开启UTP
                {
                    string ip = Master_IP.Text;
                    ushort port = ushort.Parse(Master_DK.Text);
                    UdpClient udpClient = new UdpClient(ip, port);
                    master = ModbusIpMaster.CreateIp(udpClient);
                }
                master.Transport.ReadTimeout = 100;//读取数据超时100ms
                master.Transport.WriteTimeout = 100;//写入数据超时100ms
                master.Transport.Retries = 3;//重试次数
                master.Transport.WaitToRetryMilliseconds = 10;//重试间隔
                Master_LJ.Text = "停止";
            }
            catch (Exception e)
            {
                MessageBox.Show($"连接失败:{e.Message}");
            }

        }

        private void StopMaster()
        {

            master.Dispose();
            master = null;
            Master_LJ.Text = "连接";
        }

        #endregion

        #region 03读一个或多个报持寄存器
        /// <summary>
        /// 03读一个或多个报持寄存器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Master_03_Click(object sender, EventArgs e)
        {
            if (master == null) return;
            try
            {
                byte slaveld = byte.Parse(Master_CZDZ.Text);//从站位置 
                ushort StartAddress = 0; //从0开始读
                ushort EndAddress = 10; //读10个
                ushort[] data = master.ReadHoldingRegisters(slaveld, StartAddress, EndAddress);
                for (int i = 0; i < 10; i++)
                {
                    Master_dataGridView.Rows[i].Cells[1].Value = data[i];
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        #endregion

        #region 06写单个保持寄存器
        /// <summary>
        /// 06写单个保持寄存器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Master_06_Click(object sender, EventArgs e)
        {
            if (master == null) return;
            try
            {
                byte slaveld = byte.Parse(Master_CZDZ.Text);//从站位置 
                ushort Address = 9;//写入值的地址
                ushort value = 99;//写入的值(写死了)
                master.WriteSingleRegister(slaveld, Address, value);//写单个保持寄存器
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        #endregion

        #region 16写多个保持寄存器
        /// <summary>
        /// 16写多个保持寄存器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Master_16_Click(object sender, EventArgs e)
        {
            if (master == null) return;
            try
            {
                byte slaveld = byte.Parse(Master_CZDZ.Text);//从站位置 
                ushort Address = 1;//起始位置
                ushort[] value = { 11, 22, 33 };//写入的值
                master.WriteMultipleRegisters(slaveld, Address, value);//写多个保持寄存器
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        #endregion

        #region 01读多个线圈状态
        /// <summary>
        /// 01读多个线圈状态
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Master_01_Click(object sender, EventArgs e)
        {
            if (master == null) return;
            try
            {
                byte slaveld = byte.Parse(Master_CZDZ.Text);//从站位置 
                ushort Address = 0;//起始位置
                ushort value = 10;//读取的值
                bool[] data = master.ReadCoils(slaveld, Address, value);//读多个线圈状态
                for (int i = 0; i < data.Length; i++)
                {
                    Master_dataGridView.Rows[i].Cells[1].Value = data[i] == true ? 1 : 0;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        #endregion

        #region 05写单个线圈状态
        /// <summary>
        /// 05写单个线圈状态
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            if (master == null) return;
            try
            {
                byte slaveld = byte.Parse(Master_CZDZ.Text);//从站位置 
                ushort Address = 1;//写入值的地址
                bool value = true;//写入的值
                master.WriteSingleCoil(slaveld, Address, value);//写单个线圈状态
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        #endregion

        #region 15写多个线圈状态
        /// <summary>
        /// 15写多个线圈状态
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Master_15_Click(object sender, EventArgs e)
        {
            if (master == null) return;
            try
            {
                byte slaveld = byte.Parse(Master_CZDZ.Text);//从站位置 
                ushort Address = 1;//起始位置
                bool[] value = { true, true, true };//写入的值
                master.WriteMultipleCoils(slaveld, Address, value);//写多个线圈状态
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        #endregion

        #region 初始化从站UI数据
        /// <summary>
        /// 初始化主站UI数据
        /// </summary>
        private void InotSlaveUI()
        {
            Slave_TXFS.Items.Add("SerialIPort");
            Slave_TXFS.Items.Add("TCP");
            Slave_TXFS.Items.Add("UDP");
            Slave_TXFS.SelectedIndex = 0;
            Slave_IP.Text = "COM2";
            Slave_DK.Text = "";

            Slave_dataGridView.AllowUserToAddRows = false;
            Slave_dataGridView.RowHeadersWidth = 60;
            Slave_dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;

            Slave_dataGridView.AllowUserToOrderColumns = false;
            //Master_dataGridView.Columns[1].SortMode=DataGridViewColumnSortMode.NotSortable;

            Slave_dataGridView.Columns.Add("Alias", "Alias");
            Slave_dataGridView.Columns.Add("00000", "00000");

            for (int i = 0; i < 10; i++)
            {
                Slave_dataGridView.Rows.Add();
                Slave_dataGridView.Rows[i].HeaderCell.Value = i.ToString();
            }

            //从站地址
            Slave_CZDZ.Text = "1";

            //表类型
            Slave_BLX.Items.Add(ModbusDataType.HoldingRegister);//保持寄存器,读写
            Slave_BLX.Items.Add(ModbusDataType.InputRegister);//输入寄存器,只读
            Slave_BLX.Items.Add(ModbusDataType.Coil);//线圈,读写
            Slave_BLX.Items.Add(ModbusDataType.Input);//离撒输入,只读
            Slave_BLX.SelectedIndex = 0;
        }
        #endregion

        #region 从站通信方式选择事件
        /// <summary>
        /// 从站通信方式选择事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Slave_TXFS_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (Slave_TXFS.SelectedIndex == 0)
            {
                Slave_IP.Text = "COM2";
                Slave_DK.Text = "";
                Slave_RTU.Visible = true;
                Slave_ASCII.Visible = true;
                Slave_RTU.Checked = true;
            }
            else
            {
                Slave_IP.Text = "127.0.0.1";
                Slave_DK.Text = "8080";
                Slave_RTU.Visible = false;
                Slave_ASCII.Visible = false;
                Slave_RTU.Checked = false;
                Slave_ASCII.Checked = false;
            }
        }







        #endregion

        #region 开启
        /// <summary>
        /// 开启
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Slave_KQ_Click(object sender, EventArgs e)
        {
            if (Slave_KQ.Text == "开启")
            {
                StartSlave();
            }
            else
            {
                StopSlave();
            }
        }

        private void StartSlave()
        {
            try
            {
                byte slaveld = byte.Parse(Slave_CZDZ.Text);//从站位置 
                if (Slave_TXFS.SelectedIndex == 0)//开启RTU或Ascii
                {
                    SerialPort port = new SerialPort();
                    port.PortName = Slave_IP.Text;//通信方式
                    port.BaudRate = 9600;//波特率
                    port.DataBits = 8;//数据位
                    port.Parity = Parity.Even;//奇偶校验
                    port.StopBits = StopBits.One;//停止位
                    port.Open();
                    if (Slave_RTU.Checked)
                    {
                        slave = ModbusSerialSlave.CreateRtu(slaveld, port);
                    }
                    else if (Slave_ASCII.Checked)
                    {
                        slave = ModbusSerialSlave.CreateAscii(slaveld, port);
                    }
                }
                else if (Slave_TXFS.SelectedIndex == 1)//开启TCP
                {
                    string ip = Slave_IP.Text;
                    ushort port = ushort.Parse(Slave_DK.Text);
                    TcpListener tcpListener = new TcpListener(IPAddress.Parse(ip),port);
                    //tcpListener.Start();
                    slave = ModbusTcpSlave.CreateTcp(slaveld, tcpListener);
                }
                else if (Slave_TXFS.SelectedIndex == 2)//开启UTP
                {
                    string ip = Slave_IP.Text;
                    ushort port = ushort.Parse(Slave_DK.Text);
                    IPEndPoint iPEnd=new IPEndPoint(IPAddress.Parse(ip), port);
                    UdpClient udpClient = new UdpClient(iPEnd);
                    slave = ModbusUdpSlave.CreateUdp(slaveld,udpClient);
                }
               // slave.Transport.ReadTimeout = 100;//读取数据超时100ms
                //slave.Transport.WriteTimeout = 100;//写入数据超时100ms
                slave.Transport.Retries = 3;//重试次数
                slave.Transport.WaitToRetryMilliseconds = 10;//重试间隔

                Task.Factory.StartNew(() =>
                {
                    slave.Listen();//开启监听
                });

                Slave_KQ.Text = "停止";

                //订阅一些事件
                //从站收到主站请求时
                slave.ModbusSlaveRequestReceived += new EventHandler<ModbusSlaveRequestEventArgs>((s, e) =>
                {
                    this.BeginInvoke(Log, "ModbusSlaveRequestReceived");

                });
                //从站写完数据之后的事件
                slave.WriteComplete += new EventHandler<ModbusSlaveRequestEventArgs>((s, e) =>
                {
                    this.BeginInvoke(Log, "WriteComplete");
                });

                //数据写入事件
                slave.DataStore.DataStoreWrittenTo += new EventHandler<DataStoreEventArgs>((s, e) =>
                {
                    this.BeginInvoke(Log, "DataStoreWrittenTo");
                });
                //数据读取事件
                slave.DataStore.DataStoreReadFrom += new EventHandler<DataStoreEventArgs>((s, e) =>
                {
                    this.BeginInvoke(Log, "DataStoreReadFrom");
                });

            }
            catch (Exception e)
            {
                MessageBox.Show($"开启失败:{e.Message}");
            }
        }

        private void StopSlave()
        {
            slave.Dispose();
            slave = null;
            Slave_KQ.Text = "开启";
        }

        #endregion

        #region 写入寄存器或线圈
        /// <summary>
        /// 写入寄存器或线圈
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Slave_dataGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
            string table = Slave_BLX.SelectedItem.ToString();//获取从站存储表
            object value = Slave_dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value;//获取当前dataGrid单元数据
            if (value != null)
            {
                ushort v = ushort.Parse(value.ToString());
                ModbusDataType dataType = (ModbusDataType)Enum.Parse(typeof(ModbusDataType), table);
                switch (dataType)
                {
                    case ModbusDataType.HoldingRegister:
                        slave.DataStore.HoldingRegisters[e.RowIndex + 1] = v;
                        break;
                    case ModbusDataType.InputRegister:
                        slave.DataStore.InputRegisters[e.RowIndex + 1] = v;
                        break;
                    case ModbusDataType.Coil:
                        slave.DataStore.CoilDiscretes[e.RowIndex + 1] = v != 0;
                        break;
                    case ModbusDataType.Input:
                        slave.DataStore.InputDiscretes[e.RowIndex + 1] = v != 0;
                        break;
                    default:
                        break;

                }
            }
        }
        #endregion

        #region 表类型选择
        /// <summary>
        /// 表类型选择
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Slave_BLX_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (slave == null) return;
            string table = Slave_BLX.SelectedItem.ToString();
            ModbusDataType t = (ModbusDataType)Enum.Parse(typeof(ModbusDataType), table);
            switch (t)
            {
                case ModbusDataType.HoldingRegister:
                    for (int i = 0; i < 10; i++)
                    {
                        Slave_dataGridView.Rows[i].Cells[1].Value = slave.DataStore.HoldingRegisters[i + 1];
                    }
                    break;
                case ModbusDataType.InputRegister:
                    for (int i = 0; i < 10; i++)
                    {
                        Slave_dataGridView.Rows[i].Cells[1].Value = slave.DataStore.InputRegisters[i + 1];
                    }
                    break;
                case ModbusDataType.Coil:
                    for (int i = 0; i < 10; i++)
                    {
                        int v = slave.DataStore.CoilDiscretes[i + 1] == true ? 1 : 0;
                        Slave_dataGridView.Rows[i].Cells[1].Value = v;
                    }
                    break;
                case ModbusDataType.Input:
                    for (int i = 0; i < 10; i++)
                    {
                        int v= slave.DataStore.InputDiscretes[i + 1]==true ? 1 : 0;
                        Slave_dataGridView.Rows[i].Cells[1].Value = v;
                    }
                    break;
            }
        }
        #endregion

        #region 刷新
        /// <summary>
        /// 刷新
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Slave_SX_Click(object sender, EventArgs e)
        {
            Slave_BLX_SelectedIndexChanged(null, null);
        }
        #endregion

        #region 窗体关闭
        /// <summary>
        /// 窗体关闭
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if(master!=null)
            {
                master.Dispose();
            }
            if(slave!=null)
            {
                slave.Dispose();
            }
        }
        #endregion
    }
}

 

posted @ 2024-05-07 16:48  野码  阅读(45)  评论(0编辑  收藏  举报