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

相关推荐
R-G-B5 小时前
【28】C# WinForm入门到精通 ——多文档窗体MDI【属性、方法、实例、源码】【多窗口重叠、水平平铺、垂直平铺、窗体传值】
c#·winform·多文档窗体mdi·多窗口重叠·水平平铺·垂直平铺·窗体传值
LZQqqqqo9 小时前
c#_文件的读写 IO
开发语言·c#
CodeCraft Studio13 小时前
图像处理控件Aspose.Imaging教程:使用 C# 编程将 CMX 转换为 PNG
图像处理·人工智能·c#·aspose·png·图片格式转换·cmx
csdn_aspnet14 小时前
C# 求梯形面积的程序(Program to find area of a Trapezoid)
c#
LZQqqqqo15 小时前
C#_创建自己的MyList列表
java·算法·c#
lzhdim17 小时前
C#开发的Panel里控件拖放例子 - 开源研究系列文章
开发语言·开源·c#
★YUI★20 小时前
学习游戏制作记录(冻结敌人时间与黑洞技能)7.30
学习·游戏·unity·c#
t1987512821 小时前
C# CAN通信上位机系统设计与实现
开发语言·c#
R-G-B1 天前
【31】C# WinForm入门到精通 ——保存文件SaveFileDialog 【属性、方法、事件、实例、源码】
开发语言·c#·c# winform·c#保存文件·savefiledialog
恣艺1 天前
LeetCode 68:文本左右对齐
算法·leetcode·c#