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#代码

  1. public bool Connect(string ip,int port,int)
  2. {
  3. try
  4. {
  5. this.ip = ip;
  6. this.port = port;
  7. this.tcpClient = new TcpClient(ip, port);
  8. this.master = ModbusIpMaster.CreateIp(this.tcpClient);
  9. //超时 2s
  10. this.master.Transport.ReadTimeout = 2000;
  11. //重试3次
  12. this.master.Transport.Retries = 3;
  13. this.master.Transport.WaitToRetryMilliseconds = 250;
  14. this.IsRunning = true;
  15. this.checkTask = Task.Factory.StartNew(() =>
  16. {
  17. while (this.IsRunning)
  18. {
  19. Thread.Sleep(100);
  20. try
  21. {
  22. //读取红外信号
  23. ushort[] value = this.master.ReadHoldingRegisters(1, ModbusConst.CONST_ADDRESS_GPIO_SIGN, 1);
  24. float v = value[0];
  25. PLCControl.GetInstance().onGPIOData(placeId, v);
  26. //读料口1 | 2
  27. ushort[] mPort = this.master.ReadHoldingRegisters(1, ModbusConst.CONST_ADDRESS_MATERIAL_PORT, 1);
  28. int mPortType = mPort[0];
  29. PLCControl.GetInstance().OnMaterialPortType(placeId, mPortType);
  30. //对应的数据 是 float类型。可以放入到缓存中,并且发送事件
  31. ushort[] weightArr = this.master.ReadHoldingRegisters(1, ModbusConst.CONST_ADDRESS_WEIGHT, 2);
  32. byte[] t = ByteArrayLib.GetByteArrayFromUShortArray(weightArr);
  33. float weight = FloatLib.GetFloatFromByteArray(t, 0);
  34. }
  35. catch (Exception ex)
  36. {
  37. common.CommonCtrl.GetInstance().GetInfoLog().Info(ex.Message);
  38. if (this.tcpClient != null)
  39. {
  40. this.tcpClient.Close();
  41. this.master.Dispose();
  42. this.tcpClient = null;
  43. this.master = null;
  44. }
  45. Thread.Sleep(10);
  46. try
  47. {
  48. this.tcpClient = new TcpClient(ip, port);
  49. this.master = ModbusIpMaster.CreateIp(this.tcpClient);
  50. this.master.Transport.ReadTimeout = 2000;
  51. this.master.Transport.WaitToRetryMilliseconds = 250;
  52. }
  53. catch (Exception e)
  54. {
  55. }
  56. }
  57. finally
  58. {
  59. }
  60. }
  61. });
  62. this.checkTask.Start();
  63. return true;
  64. }
  65. catch (Exception ex)
  66. {
  67. //这事一个简单的log日志 记录
  68. common.CommonCtrl.GetInstance().GetInfoLog().Info($"{ip}:{port}连接失败");
  69. common.CommonCtrl.GetInstance().GetInfoLog().Info(ex.Message);
  70. }
  71. return false;
  72. }

以上代码包含了。不断通过Modbus协议读取关键数据。并且在读取失败的时候,认为是网络连接出现了错误,并且尝试断线重连。

  1. public bool WriteData(ushort address, string value)
  2. {
  3. try
  4. {
  5. //string vals = value;
  6. //float[] uVals02 = vals.Split(',').Select(s => float.Parse(s)).ToArray();
  7. //byte[] y = ByteArrayLib.GetByteArrayFromFloatArray(uVals02);
  8. //ushort[] ushorts = UShortLib.GetUShortArrayFromByteArray(y);
  9. ushort ushorts = ushort.Parse(value);
  10. this.master.WriteSingleRegister(1, address, ushorts);
  11. return true;
  12. }
  13. catch (Exception ex)
  14. {
  15. common.CommonCtrl.GetInstance().GetInfoLog().Info($"{address}写入数据{value}失败");
  16. common.CommonCtrl.GetInstance().GetInfoLog().Info(ex.Message);
  17. }
  18. return false;
  19. }

以上代码是往PLC中写入数据.

下面是读取数据和连接的地方循环读取数据的操作一样,只是封装成了方法.

  1. public string ReadData(ushort address,ushort numsOfPoint)
  2. {
  3. try
  4. {
  5. ushort[] value = this.master.ReadHoldingRegisters(1, address, numsOfPoint);
  6. byte[] bytesArr = ByteArrayLib.GetByteArrayFromUShortArray(value);
  7. return StringLib.GetStringFromByteArrayByBitConvert(bytesArr, 0, bytesArr.Length);
  8. }
  9. catch (Exception ex)
  10. {
  11. common.CommonCtrl.GetInstance().GetInfoLog().Info($"{address}读取数据失败");
  12. common.CommonCtrl.GetInstance().GetInfoLog().Info(ex.Message);
  13. }
  14. return "";
  15. }

以上代码能够完成C# winform(当前wpf也可以) 使用Modbus协议。进行读写的相应操作。

本人从事从事上位机开发。
有需要交流的同行请添加微信:17320170935

2024-09-18 16:12:41  user 阅读(35) 评论(0) 标签:C#,Modbus,PLC 分类:C#