使用C#和NMODBUS快速搭建MODBUS从站模拟器

MODBUS是使用广泛的协议,通讯测试时进行有使用。Modbus通讯分为主站和从站,使用RS485通讯时同一个网络内只能有一个主站,多个从站。使用TCP通讯时没有这方面的限制,可以同时支持多个主站的通讯读写。

开发测试时有各种复杂的需求,现有的仪器仪表实物搭建费时费力,可以用C#使用NMODBUS组件快速编写自己的从站仿真器,从而实现各种复杂场景的模拟。

编程思路:

创建通讯侦听端口、添加Modbus从站设备、定时改变数据。

主要实现代码如下:

cs 复制代码
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using NModbus;
using System.Net.Sockets;
using System.Net;
using System.Linq;

namespace EasyModbusSlaveSimulator
{
    public partial class Form1 : Form
    {
        private TcpListener modbusListener;
        private byte CountSlave = 10;
        private ushort CountRegisterMax = 1000;
        //数据仿真
        ushort uValue = 0;
        ushort uStep = 1;
        private bool bEnableDataSimulation = true;
        private List<IModbusSlave> listSlave = new List<IModbusSlave>();
        IModbusSlaveNetwork modbusSlaveNetwork = null;
        public Form1()
        {
            InitializeComponent();
        }


        private void Form1_Load(object sender, EventArgs e)
        {
            comboBox1.Items.Add("0.0.0.0");
            var localIPs = getAllIpAddress();
            // 输出本地IP地址
            foreach (IPAddress ipAddress in localIPs)
            {
                comboBox1.Items.Add(ipAddress.ToString());
                //Console.WriteLine("IP地址: " + ipAddress.ToString());
            }
            comboBox1.SelectedIndex = 0;

            for (int i = 1; i <= 250; i++)
            {
                comboBox2.Items.Add(i);
            }
            comboBox2.SelectedIndex = 9;
        }
        private void button1_Click(object sender, EventArgs e)
        {
            string Ip = comboBox1.Text;
            int port = int.Parse(textBox2.Text);
            CountSlave = byte.Parse(comboBox2.Text);
            CountRegisterMax = ushort.Parse(textBox4.Text);

            //创建通讯绑定端口
            modbusListener = new TcpListener(IPAddress.Parse(Ip), port);
            modbusListener.Start();

            IModbusFactory factory = new ModbusFactory();

            modbusSlaveNetwork = factory.CreateSlaveNetwork(modbusListener);

            //创建两个Slave设备,Id分别为1和2
            for (byte i = 0; i < CountSlave; i++)
            {
                IModbusSlave slave = factory.CreateSlave((byte)(i + 1));
                listSlave.Add(slave);
                modbusSlaveNetwork.AddSlave(slave);
            }

            //接受连接
            modbusSlaveNetwork.ListenAsync();
            

            button1.Enabled = false;
            button2.Enabled = true;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            modbusListener.Stop();
            modbusSlaveNetwork.Dispose();
            listSlave.Clear();
            modbusListener = null;
            modbusSlaveNetwork = null;

            button1.Enabled = true;
            button2.Enabled = false;
        }
        private IEnumerable<IPAddress> getAllIpAddress()
        {
            // 获取本地主机的相关信息
            IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());

            // 使用LINQ查询语句筛选出符合条件的IP地址
            var localIPs = from ipAddress in host.AddressList
                           where ipAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork
                                 && !IPAddress.IsLoopback(ipAddress)
                           select ipAddress;
            return localIPs;

        }
        private void timer1_Tick(object sender, EventArgs e)
        {
            if (bEnableDataSimulation)
            {
                bool[] vb = new bool[CountRegisterMax];
                ushort[] vu = new ushort[CountRegisterMax];
                for (int i = 0; i < CountRegisterMax; i ++)
                {
                    vu[i] = uValue;
                    vb[i] = (uValue % 2 == 1) ? true:false ;
                }

                foreach(var slave in listSlave) {
                    slave.DataStore.CoilDiscretes.WritePoints(0, vb);
                    slave.DataStore.CoilInputs.WritePoints(0, vb);
                    slave.DataStore.InputRegisters.WritePoints(0,vu);
                    slave.DataStore.HoldingRegisters.WritePoints(0,vu);
                }

                uValue += uStep;
            }
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox1.Checked)
            {
                timer1.Interval = int.Parse(textBox5.Text)*1000;
                timer1.Enabled = true;
                uStep = ushort.Parse(textBox3.Text);
            }
            else
            {
                timer1.Enabled=false;
            }
        }
    }
}

NModbus的WritePoints方法,可以批量写入数据,不需要考虑同步造成的问题。各种ABCD、BADC、CDAB等格式调试,可以调整数据数组实现。浮点、32位整数、字符串等都可以通过调整slave.DataStore.InputRegisters.WritePoints和slave.DataStore.HoldingRegisters.WritePoints所对应的数组来实现。

参考:

C# 使用NModbus 多Slave站编程方法

相关推荐
hccee3 小时前
C# IO文件操作
开发语言·c#
广煜永不挂科5 小时前
Devexpress.Dashboard的调用二义性
c#·express
初九之潜龙勿用7 小时前
C#校验画布签名图片是否为空白
开发语言·ui·c#·.net
吾与谁归in8 小时前
【C#设计模式(13)——代理模式(Proxy Pattern)】
设计模式·c#·代理模式
吾与谁归in8 小时前
【C#设计模式(14)——责任链模式( Chain-of-responsibility Pattern)】
设计模式·c#·责任链模式
神仙别闹9 小时前
基于C#和Sql Server 2008实现的(WinForm)订单生成系统
开发语言·c#
向宇it19 小时前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎
九鼎科技-Leo19 小时前
什么是 WPF 中的依赖属性?有什么作用?
windows·c#·.net·wpf
Heaphaestus,RC20 小时前
【Unity3D】获取 GameObject 的完整层级结构
unity·c#
baivfhpwxf202320 小时前
C# 5000 转16进制 字节(激光器串口通讯生成指定格式命令)
开发语言·c#