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

相关推荐
qq_297908012 小时前
c#车检车构客户管理系统软件车辆年审短信提醒软件
sqlserver·c#·开源软件
酷炫码神3 小时前
C#数组与集合
开发语言·c#
钢铁男儿3 小时前
C# 深入理解类(静态函数成员)
java·开发语言·c#
CoderIsArt12 小时前
参数系统的基类Parameter抽象类
c#
盛夏绽放13 小时前
Python字符串常用方法详解
开发语言·python·c#
Tummer836317 小时前
C#+WPF+prism+materialdesign创建工具主界面框架
开发语言·c#·wpf
ghost14317 小时前
C#学习第23天:面向对象设计模式
开发语言·学习·设计模式·c#
yngsqq18 小时前
(for 循环) VS (LINQ) 性能比拼 ——c#
c#·solr·linq
想做后端的小C19 小时前
C# 面向对象 构造函数带参无参细节解析
开发语言·c#·面向对象
炯哈哈19 小时前
【上位机——WPF】App.xml和Application类简介
xml·开发语言·c#·wpf·上位机