C#使用NModbus库,编写从站仿真器,支持Modbus TCP访问,支持多个从站地址和动态启用/停用从站(模拟离线),支持数据变化,可以很方便实现,最终效果如图所示。
项目采用.net framework 4.7的Windows窗体模式快速实现,NuGet包和版本如下:
界面设计如下:
窗体设计部分Form1.Designer.cs代码如下:
namespace ModbusSlaveSimulatorTest
{
partial class Form1
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.checkBox1 = new System.Windows.Forms.CheckBox();
this.checkBox2 = new System.Windows.Forms.CheckBox();
this.checkBox3 = new System.Windows.Forms.CheckBox();
this.checkBox4 = new System.Windows.Forms.CheckBox();
this.checkBox5 = new System.Windows.Forms.CheckBox();
this.checkBox6 = new System.Windows.Forms.CheckBox();
this.checkBox7 = new System.Windows.Forms.CheckBox();
this.checkBox8 = new System.Windows.Forms.CheckBox();
this.checkBox9 = new System.Windows.Forms.CheckBox();
this.checkBox10 = new System.Windows.Forms.CheckBox();
this.timer1 = new System.Windows.Forms.Timer(this.components);
this.label1 = new System.Windows.Forms.Label();
this.comboBox1 = new System.Windows.Forms.ComboBox();
this.SuspendLayout();
//
// checkBox1
//
this.checkBox1.AutoSize = true;
this.checkBox1.Checked = true;
this.checkBox1.CheckState = System.Windows.Forms.CheckState.Checked;
this.checkBox1.Location = new System.Drawing.Point(12, 12);
this.checkBox1.Name = "checkBox1";
this.checkBox1.Size = new System.Drawing.Size(66, 16);
this.checkBox1.TabIndex = 0;
this.checkBox1.Text = "Slave 1";
this.checkBox1.UseVisualStyleBackColor = true;
this.checkBox1.MouseClick += new System.Windows.Forms.MouseEventHandler(this.checkBox9_MouseClick);
//
// checkBox2
//
this.checkBox2.AutoSize = true;
this.checkBox2.Checked = true;
this.checkBox2.CheckState = System.Windows.Forms.CheckState.Checked;
this.checkBox2.Location = new System.Drawing.Point(84, 12);
this.checkBox2.Name = "checkBox2";
this.checkBox2.Size = new System.Drawing.Size(66, 16);
this.checkBox2.TabIndex = 1;
this.checkBox2.Text = "Slave 2";
this.checkBox2.UseVisualStyleBackColor = true;
this.checkBox2.MouseClick += new System.Windows.Forms.MouseEventHandler(this.checkBox9_MouseClick);
//
// checkBox3
//
this.checkBox3.AutoSize = true;
this.checkBox3.Checked = true;
this.checkBox3.CheckState = System.Windows.Forms.CheckState.Checked;
this.checkBox3.Location = new System.Drawing.Point(167, 12);
this.checkBox3.Name = "checkBox3";
this.checkBox3.Size = new System.Drawing.Size(66, 16);
this.checkBox3.TabIndex = 2;
this.checkBox3.Text = "Slave 3";
this.checkBox3.UseVisualStyleBackColor = true;
this.checkBox3.MouseClick += new System.Windows.Forms.MouseEventHandler(this.checkBox9_MouseClick);
//
// checkBox4
//
this.checkBox4.AutoSize = true;
this.checkBox4.Checked = true;
this.checkBox4.CheckState = System.Windows.Forms.CheckState.Checked;
this.checkBox4.Location = new System.Drawing.Point(251, 12);
this.checkBox4.Name = "checkBox4";
this.checkBox4.Size = new System.Drawing.Size(66, 16);
this.checkBox4.TabIndex = 3;
this.checkBox4.Text = "Slave 4";
this.checkBox4.UseVisualStyleBackColor = true;
this.checkBox4.MouseClick += new System.Windows.Forms.MouseEventHandler(this.checkBox9_MouseClick);
//
// checkBox5
//
this.checkBox5.AutoSize = true;
this.checkBox5.Checked = true;
this.checkBox5.CheckState = System.Windows.Forms.CheckState.Checked;
this.checkBox5.Location = new System.Drawing.Point(342, 12);
this.checkBox5.Name = "checkBox5";
this.checkBox5.Size = new System.Drawing.Size(66, 16);
this.checkBox5.TabIndex = 4;
this.checkBox5.Text = "Slave 5";
this.checkBox5.UseVisualStyleBackColor = true;
this.checkBox5.MouseClick += new System.Windows.Forms.MouseEventHandler(this.checkBox9_MouseClick);
//
// checkBox6
//
this.checkBox6.AutoSize = true;
this.checkBox6.Checked = true;
this.checkBox6.CheckState = System.Windows.Forms.CheckState.Checked;
this.checkBox6.Location = new System.Drawing.Point(432, 12);
this.checkBox6.Name = "checkBox6";
this.checkBox6.Size = new System.Drawing.Size(66, 16);
this.checkBox6.TabIndex = 5;
this.checkBox6.Text = "Slave 6";
this.checkBox6.UseVisualStyleBackColor = true;
this.checkBox6.MouseClick += new System.Windows.Forms.MouseEventHandler(this.checkBox9_MouseClick);
//
// checkBox7
//
this.checkBox7.AutoSize = true;
this.checkBox7.Checked = true;
this.checkBox7.CheckState = System.Windows.Forms.CheckState.Checked;
this.checkBox7.Location = new System.Drawing.Point(516, 12);
this.checkBox7.Name = "checkBox7";
this.checkBox7.Size = new System.Drawing.Size(66, 16);
this.checkBox7.TabIndex = 6;
this.checkBox7.Text = "Slave 7";
this.checkBox7.UseVisualStyleBackColor = true;
this.checkBox7.MouseClick += new System.Windows.Forms.MouseEventHandler(this.checkBox9_MouseClick);
//
// checkBox8
//
this.checkBox8.AutoSize = true;
this.checkBox8.Checked = true;
this.checkBox8.CheckState = System.Windows.Forms.CheckState.Checked;
this.checkBox8.Location = new System.Drawing.Point(588, 12);
this.checkBox8.Name = "checkBox8";
this.checkBox8.Size = new System.Drawing.Size(66, 16);
this.checkBox8.TabIndex = 7;
this.checkBox8.Text = "Slave 8";
this.checkBox8.UseVisualStyleBackColor = true;
this.checkBox8.MouseClick += new System.Windows.Forms.MouseEventHandler(this.checkBox9_MouseClick);
//
// checkBox9
//
this.checkBox9.AutoSize = true;
this.checkBox9.Checked = true;
this.checkBox9.CheckState = System.Windows.Forms.CheckState.Checked;
this.checkBox9.Location = new System.Drawing.Point(660, 12);
this.checkBox9.Name = "checkBox9";
this.checkBox9.Size = new System.Drawing.Size(66, 16);
this.checkBox9.TabIndex = 8;
this.checkBox9.Text = "Slave 9";
this.checkBox9.UseVisualStyleBackColor = true;
this.checkBox9.MouseClick += new System.Windows.Forms.MouseEventHandler(this.checkBox9_MouseClick);
//
// checkBox10
//
this.checkBox10.AutoSize = true;
this.checkBox10.Checked = true;
this.checkBox10.CheckState = System.Windows.Forms.CheckState.Checked;
this.checkBox10.Location = new System.Drawing.Point(12, 57);
this.checkBox10.Name = "checkBox10";
this.checkBox10.Size = new System.Drawing.Size(96, 16);
this.checkBox10.TabIndex = 9;
this.checkBox10.Text = "数据随机变化";
this.checkBox10.UseVisualStyleBackColor = true;
this.checkBox10.CheckedChanged += new System.EventHandler(this.checkBox10_CheckedChanged);
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.Interval = 2000;
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(148, 58);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(101, 12);
this.label1.TabIndex = 10;
this.label1.Text = "仿真频率(ms):";
//
// comboBox1
//
this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBox1.FormattingEnabled = true;
this.comboBox1.Items.AddRange(new object[] {
"100",
"200",
"500",
"1000",
"2000",
"5000",
"10000"});
this.comboBox1.Location = new System.Drawing.Point(247, 56);
this.comboBox1.Name = "comboBox1";
this.comboBox1.Size = new System.Drawing.Size(70, 20);
this.comboBox1.TabIndex = 11;
this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(802, 125);
this.Controls.Add(this.comboBox1);
this.Controls.Add(this.label1);
this.Controls.Add(this.checkBox10);
this.Controls.Add(this.checkBox9);
this.Controls.Add(this.checkBox8);
this.Controls.Add(this.checkBox7);
this.Controls.Add(this.checkBox6);
this.Controls.Add(this.checkBox5);
this.Controls.Add(this.checkBox4);
this.Controls.Add(this.checkBox3);
this.Controls.Add(this.checkBox2);
this.Controls.Add(this.checkBox1);
this.Name = "Form1";
this.Text = "Modbus从站仿真";
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.CheckBox checkBox1;
private System.Windows.Forms.CheckBox checkBox2;
private System.Windows.Forms.CheckBox checkBox3;
private System.Windows.Forms.CheckBox checkBox4;
private System.Windows.Forms.CheckBox checkBox5;
private System.Windows.Forms.CheckBox checkBox6;
private System.Windows.Forms.CheckBox checkBox7;
private System.Windows.Forms.CheckBox checkBox8;
private System.Windows.Forms.CheckBox checkBox9;
private System.Windows.Forms.CheckBox checkBox10;
private System.Windows.Forms.Timer timer1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.ComboBox comboBox1;
}
}
窗体实现部分代码如下:
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System.Windows.Forms;
using NModbus;
namespace ModbusSlaveSimulatorTest
{
public partial class Form1 : Form
{
class AppData
{
public string BindIp = "0.0.0.0";
public ushort BindPort = 502;
public byte SlaveCount = 9;
public bool SimulationEnable = true;
public int SimulationFreq = 1000;
public string SimulationUnitId = "1,2,3,4,5,6,7,8,9";
public ushort SimulationRegisterMax = 1000;
public ushort SimulationStep = 1;
}
TcpListener modbusListener;
List<IModbusSlave> listSlave = new List<IModbusSlave>();
IModbusSlaveNetwork modbusSlaveNetwork = null;
Dictionary<ushort, bool> dictSlaves = new Dictionary<ushort, bool>();
//数据仿真
ushort uValue = 0;
ushort uStep = 1;
AppData appData = new AppData();
IModbusFactory factory = new ModbusFactory();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//创建通讯绑定端口
modbusListener = new TcpListener(IPAddress.Parse(appData.BindIp), appData.BindPort);
modbusListener.Start();
modbusSlaveNetwork = factory.CreateSlaveNetwork(modbusListener);
//创建Slave设备
for (byte i = 0; i < appData.SlaveCount; i++)
{
IModbusSlave slave = factory.CreateSlave((byte)(i + 1));
listSlave.Add(slave);
modbusSlaveNetwork.AddSlave(slave);
dictSlaves[(ushort)(i + 1)] = true;
}
//接受连接
modbusSlaveNetwork.ListenAsync();
comboBox1.SelectedIndex = 4;
}
private void checkBox9_MouseClick(object sender, MouseEventArgs e)
{
CheckBox cb = sender as CheckBox;
byte slaveId = byte.Parse(cb.Text.Replace("Slave", ""));
if (cb.Checked)
{
modbusSlaveNetwork.AddSlave(listSlave[slaveId - 1]);
dictSlaves[slaveId] = true;
}
else
{
modbusSlaveNetwork.RemoveSlave(slaveId);
dictSlaves.Remove(slaveId);
}
}
private void checkBox10_CheckedChanged(object sender, EventArgs e)
{
timer1.Enabled = checkBox10.Checked;
}
private void timer1_Tick(object sender, EventArgs e)
{
bool[] vb = new bool[appData.SimulationRegisterMax];
ushort[] vu = new ushort[appData.SimulationRegisterMax];
for (int i = 0; i < appData.SimulationRegisterMax; i++)
{
vu[i] = uValue;
vb[i] = (uValue % 2 == 1) ? true : false;
}
foreach (var slave in listSlave)
{
if (!dictSlaves.ContainsKey(slave.UnitId))
continue;
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 comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
timer1.Interval = int.Parse(comboBox1.Text);
}
}
}
代码托管地址:PascalMing/ModbusSlaveSimulatorTest: Modbus从站仿真
编译后的程序下载:https://github.com/PascalMing/ModbusSlaveSimulatorTest/releases/tag/v20250508