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 } }