C#上位机实现ModbusTCP服务器
大家好!我是付工。
上位机在开发完成后,如果需要提供数据接口,供第三方平台进行获取。
常用的方式有以下几种:
1、数据库方式
2、WebApi方式
3、ModbusTCP方式
今天这篇文章给大家分享一下,如何基于EasyModbusTCP来实现ModbusTCP服务器搭建。
PLC采集
为了更好展示数据,我们写个简单的PLC采集程序,如果没有PLC,可以使用随机数。
1、PLC采集代码如下:
private void FrmMain_Load(object sender, EventArgs e){
//实例化PLC对象
Plc plc = new Plc(CpuType.S71200, "192.168.2.151", 0, 0);
try
{
//建立PLC连接
plc.Open();
Task.Run(() =>
{
while (true)
{
//读取PLC数据
PlcData plcData = plc.ReadClass<PlcData>(1, 0);
//更新界面
this.Invoke(new Action(() =>
{
this.lbl_PressureIn.Text = plcData.PressureIn.ToString("f2");
this.lbl_PressureOut.Text = plcData.PressureOut.ToString("f2");
this.lbl_TempIn.Text = plcData.TempIn1.ToString("f2");
this.lbl_TempOut.Text = plcData.TempOut.ToString("f2");
}));
//加点延时,不然太快了
Thread.Sleep(500);
}
});
}
catch (Exception ex)
{
MessageBox.Show("PLC连接失败:" + ex.Message);
}
}
2、运行结果如下:

ModbusTCP服务器
接下来我们实现ModbusTCP服务器部分。
1、首先通过Nuget安装EasyModbusTCP库。

2、我们创建一个ModbusTCPServer类,这个类主要是对库进行封装,代码如下:
public class ModbusTCPServer{
private ModbusServer modbusServer;
private bool isRunning = false;
public event EventHandler<string> OnLogMessage;
public event EventHandler<string> OnDataChanged;
public bool IsRunning { get { return isRunning; } }
public ModbusTCPServer(string ip, int port = 502)
{
modbusServer = new ModbusServer();
modbusServer.LocalIPAddress = IPAddress.Parse(ip);
modbusServer.Port = port;
}
public void StartServer()
{
if (!isRunning)
{
modbusServer.Listen();
modbusServer.HoldingRegistersChanged += ModbusServer_HoldingRegistersChanged;
isRunning = true;
OnLogMessage?.Invoke(this, "Modbus服务器启动成功");
}
else
{
OnLogMessage?.Invoke(this, "Modbus服务器已经运行");
}
}
public void StopServer()
{
if (isRunning)
{
modbusServer.HoldingRegistersChanged -= ModbusServer_HoldingRegistersChanged;
isRunning = false;
modbusServer.StopListening();
OnLogMessage?.Invoke(this, "Modbus服务器停止成功");
}
else
{
OnLogMessage?.Invoke(this, "Modbus服务器未运行");
}
}
private void ModbusServer_HoldingRegistersChanged(int registerAddress, int numberOfRegisters)
{
//这里可以监测数据变化
OnDataChanged(this, "寄存器地址:" + registerAddress + ",寄存器数量:" + numberOfRegisters);
}
}
3、我们最常用的是保持型寄存器,因此,我们封装一些读写保持型寄存器的方法,代码如下:
public short[] GetHoldingRegisters(int startAddress, int quantity){
if (startAddress + quantity <= modbusServer.holdingRegisters.localArray.Length)
{
short[] result = new short[quantity];
Array.Copy(modbusServer.holdingRegisters.localArray, startAddress + 1, result, 0, quantity);
return result;
}
return null;
}
public short GetHoldingRegisterShort(int startAddress){
return GetHoldingRegisters(startAddress, 1).FirstOrDefault();
}
public float GetHoldingRegisterFloat(int startAddress){
short[] values = GetHoldingRegisters(startAddress, 2);
if (values != null)
{
List<byte> bytes = new List<byte>();
bytes.AddRange(BitConverter.GetBytes(values[1]));
bytes.AddRange(BitConverter.GetBytes(values[0]));
return BitConverter.ToSingle(bytes.ToArray(), 0);
}
return 0.0f;
}
public void SetHoldingRegisters(int startAddress, float value){
byte[] buffer = BitConverter.GetBytes(value);
short[] values = new short[2]
{
BitConverter.ToInt16(buffer, 2),
BitConverter.ToInt16(buffer, 0)
};
SetHoldingRegisters(startAddress, values);
}
public void SetHoldingRegisters(int startAddress, short value){
SetHoldingRegisters(startAddress, new short[] { value });
}
public void SetHoldingRegisters(int startAddress, short[] values){
if (startAddress + values.Length <= modbusServer.holdingRegisters.localArray.Length)
{
Array.Copy(values, 0, modbusServer.holdingRegisters.localArray, startAddress + 1, values.Length);
}
}
4、实现启动服务器和关闭服务器,代码如下:
private ModbusTCPServer server = null;
private void btn_StartServer_Click(object sender, EventArgs e){
try
{
server = new ModbusTCPServer(this.txt_ServerIp.Text, Convert.ToInt32(this.txt_ServerPort.Text));
server.StartServer();
}
catch (Exception)
{
MessageBox.Show("服务器开启失败");
}
}
private void btn_StopServer_Click(object sender, EventArgs e){
this.server?.StopServer();
}
5、将PLC数据写入到寄存器中,我们需要实时将数据写入PLC寄存器中,可以单独创建一个线程或定时器来实现,我们这里直接就在读取到PLC数据之后写入,在读取完成加一段代码即可,代码如下:
if (server!=null&&server.IsRunning){
server.SetHoldingRegisters(0, plcData.PressureIn);
server.SetHoldingRegisters(2, plcData.PressureOut);
server.SetHoldingRegisters(4, plcData.TempIn1);
server.SetHoldingRegisters(6, plcData.TempOut);
}
ModbusPoll测试
1、程序运行后,开启服务,运行如下:

正常应该使用502端口,因为我电脑502端口被占用了,所以我这里使用503端口。
2、我们使用ModbusPoll连接测试结果如下:

整体总结
1、ModbusTCP服务器的本质就是TCP服务器。
2、在通信库里开辟了四个数组,分别对应4个存储区,然后实现了报文的处理。
3、往服务器里写数据的本质就是对数据处理后,根据索引给数组赋值。
4、如果需要检测对方写入数据,可以在HoldingRegistersChanged中进行处理。