使用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站编程方法

相关推荐
Envyᥫᩣ3 小时前
C#语言:从入门到精通
开发语言·c#
IT技术分享社区9 小时前
C#实战:使用腾讯云识别服务轻松提取火车票信息
开发语言·c#·云计算·腾讯云·共识算法
△曉風殘月〆15 小时前
WPF MVVM入门系列教程(二、依赖属性)
c#·wpf·mvvm
逐·風17 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
m0_6569747420 小时前
C#中的集合类及其使用
开发语言·c#
九鼎科技-Leo21 小时前
了解 .NET 运行时与 .NET 框架:基础概念与相互关系
windows·c#·.net
九鼎科技-Leo1 天前
什么是 ASP.NET Core?与 ASP.NET MVC 有什么区别?
windows·后端·c#·asp.net·mvc·.net
.net开发1 天前
WPF怎么通过RestSharp向后端发请求
前端·c#·.net·wpf
小乖兽技术1 天前
C#与C++交互开发系列(二十):跨进程通信之共享内存(Shared Memory)
c++·c#·交互·ipc
幼儿园园霸柒柒1 天前
第七章: 7.3求一个3*3的整型矩阵对角线元素之和
c语言·c++·算法·矩阵·c#·1024程序员节