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

相关推荐
枯萎穿心攻击3 小时前
ECS由浅入深第三节:进阶?System 的行为与复杂交互模式
开发语言·unity·c#·游戏引擎
小码编匠3 小时前
WPF 自定义TextBox带水印控件,可设置圆角
后端·c#·.net
水果里面有苹果3 小时前
17-C#的socket通信TCP-1
开发语言·tcp/ip·c#
阿蒙Amon11 小时前
C# Linq to SQL:数据库编程的解决方案
数据库·c#·linq
iCxhust14 小时前
c# U盘映像生成工具
开发语言·单片机·c#
emplace_back15 小时前
C# 集合表达式和展开运算符 (..) 详解
开发语言·windows·c#
阿蒙Amon16 小时前
为什么 12 版仍封神?《C# 高级编程》:从.NET 5 到实战架构,进阶者绕不开的必修课
开发语言·c#
深海潜水员17 小时前
【Behavior Tree】-- 行为树AI逻辑实现- Unity 游戏引擎实现
游戏·unity·c#
开开心心_Every17 小时前
便捷的Office批量转PDF工具
开发语言·人工智能·r语言·pdf·c#·音视频·symfony