C# 使用Modbus协议与PLC通信
背景介绍
在开发一个放料系统的上位机系统。需要使用上位机(C# Winform 开发的桌面应用程序)通过Modbus协议,来操作PLC中寄存器数据。
读取部分数据,作为展示:比如:车牌号,料口的开合度—— 这些数据要么是通过其他设备设备,然后被写入PLC寄存器中,要么是PLC系统识别到的硬件状态。
PLC可以控制其他硬件设备。上位机只需要将对应的数据写入对应的寄存器即可。
Modbus 4个存储区区域的分类
区号 | 名称 | 读写 | 地址范围 |
---|---|---|---|
0区 | 输出线圈 | 可读写布尔值 | 00001 - 09999 |
1区 | 输入线圈 | 只读布尔值 | 10001-19999 |
2区 | 输入寄存器 | 只读寄存器 | 20001-29999 |
3区 | 保持寄存器 | 可读写寄存器 | 30001 -39999 |
以上寄存器的地址 和名称 参考网上资料 [一文了解Modbus协议]
主从概念
本人在学习的过程中,以为C# 上位机要连接PLC,因此以为 PLC才是主设备。上位机是从设备。这是错误的。
上位机是Master,PLC设备是Slave。是从。
连接方式
上位机和PLC之间可以通过串口也可以通过网口进行连接。
网口连接使用的是Modbus TCP/IP
1:在进行通信之前需要先进行连接。
2:写操作,只需要指定对应的地址,指定读取的位长度即可。
3:读操作,需要指定对应的地址,指定读取的长度即可。但是这里有个问题需要注意:Modubus是主->从结构,当PLC上的数据发生变化,上位机是不会得到通知的。上位机如果想实时获取数据,则需要不断的循环读取。
C#使用 NModbus4 sdk 操作对用的寄存器。
本文中主要使用 3区中 保持寄存器。可读写。
下面看一下主要的C#代码
public bool Connect(string ip,int port,int)
{
try
{
this.ip = ip;
this.port = port;
this.tcpClient = new TcpClient(ip, port);
this.master = ModbusIpMaster.CreateIp(this.tcpClient);
//超时 2s
this.master.Transport.ReadTimeout = 2000;
//重试3次
this.master.Transport.Retries = 3;
this.master.Transport.WaitToRetryMilliseconds = 250;
this.IsRunning = true;
this.checkTask = Task.Factory.StartNew(() =>
{
while (this.IsRunning)
{
Thread.Sleep(100);
try
{
//读取红外信号
ushort[] value = this.master.ReadHoldingRegisters(1, ModbusConst.CONST_ADDRESS_GPIO_SIGN, 1);
float v = value[0];
PLCControl.GetInstance().onGPIOData(placeId, v);
//读料口1 | 2
ushort[] mPort = this.master.ReadHoldingRegisters(1, ModbusConst.CONST_ADDRESS_MATERIAL_PORT, 1);
int mPortType = mPort[0];
PLCControl.GetInstance().OnMaterialPortType(placeId, mPortType);
//对应的数据 是 float类型。可以放入到缓存中,并且发送事件
ushort[] weightArr = this.master.ReadHoldingRegisters(1, ModbusConst.CONST_ADDRESS_WEIGHT, 2);
byte[] t = ByteArrayLib.GetByteArrayFromUShortArray(weightArr);
float weight = FloatLib.GetFloatFromByteArray(t, 0);
}
catch (Exception ex)
{
common.CommonCtrl.GetInstance().GetInfoLog().Info(ex.Message);
if (this.tcpClient != null)
{
this.tcpClient.Close();
this.master.Dispose();
this.tcpClient = null;
this.master = null;
}
Thread.Sleep(10);
try
{
this.tcpClient = new TcpClient(ip, port);
this.master = ModbusIpMaster.CreateIp(this.tcpClient);
this.master.Transport.ReadTimeout = 2000;
this.master.Transport.WaitToRetryMilliseconds = 250;
}
catch (Exception e)
{
}
}
finally
{
}
}
});
this.checkTask.Start();
return true;
}
catch (Exception ex)
{
//这事一个简单的log日志 记录
common.CommonCtrl.GetInstance().GetInfoLog().Info($"{ip}:{port}连接失败");
common.CommonCtrl.GetInstance().GetInfoLog().Info(ex.Message);
}
return false;
}
以上代码包含了。不断通过Modbus协议读取关键数据。并且在读取失败的时候,认为是网络连接出现了错误,并且尝试断线重连。
public bool WriteData(ushort address, string value)
{
try
{
//string vals = value;
//float[] uVals02 = vals.Split(',').Select(s => float.Parse(s)).ToArray();
//byte[] y = ByteArrayLib.GetByteArrayFromFloatArray(uVals02);
//ushort[] ushorts = UShortLib.GetUShortArrayFromByteArray(y);
ushort ushorts = ushort.Parse(value);
this.master.WriteSingleRegister(1, address, ushorts);
return true;
}
catch (Exception ex)
{
common.CommonCtrl.GetInstance().GetInfoLog().Info($"{address}写入数据{value}失败");
common.CommonCtrl.GetInstance().GetInfoLog().Info(ex.Message);
}
return false;
}
以上代码是往PLC中写入数据.
下面是读取数据和连接的地方循环读取数据的操作一样,只是封装成了方法.
public string ReadData(ushort address,ushort numsOfPoint)
{
try
{
ushort[] value = this.master.ReadHoldingRegisters(1, address, numsOfPoint);
byte[] bytesArr = ByteArrayLib.GetByteArrayFromUShortArray(value);
return StringLib.GetStringFromByteArrayByBitConvert(bytesArr, 0, bytesArr.Length);
}
catch (Exception ex)
{
common.CommonCtrl.GetInstance().GetInfoLog().Info($"{address}读取数据失败");
common.CommonCtrl.GetInstance().GetInfoLog().Info(ex.Message);
}
return "";
}
以上代码能够完成C# winform(当前wpf也可以) 使用Modbus协议。进行读写的相应操作。
本人从事从事上位机开发。
有需要交流的同行请添加微信:17320170935
很赞哦! (0)