经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C# » 查看文章
基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发
来源:cnblogs  作者:豆腐柠檬  时间:2022/1/17 11:17:27  对本文有异议

  上一篇我们介绍了如何配置连接PLC(注意网线记得插到PLC以太网口!!!还有一个好像是伺服的网口不要插错了),接下来将介绍欧姆FinsTcp协议及使用C#实现过程。

  1. FinsTcp协议报文格式

 

 

 获取PLC节点地址

 

 

 FINS command

 

 

 IO存储器地址标识

 

 

 

  2.实现过程

以上为FinsTCP协议主要核心内容,代码原理很简单就是通过SOCKET /TCP IP,发送连接、读取、写入报文数据,接收解析返回数据;

  • 基于TcpClient的发送与接收Byte[]方法

发送BYTE

  1. 1 public static bool SendData(out string msg,TcpClient tcpClient,byte[] sd)
  2. 2 {
  3. 3 msg = string.Empty;
  4. 4 try
  5. 5 {
  6. 6 tcpClient.GetStream().Write(sd, 0, sd.Length);
  7. 7 return true;
  8. 8 }
  9. 9 catch(Exception ex)
  10. 10 {
  11. 11 msg = ex.Message;
  12. 12 return false;
  13. 13 }
  14. 14 }
View Code

接收BYTE

  1. 1 public static bool ReceiveData(out string msg, TcpClient tcpClient,byte[] rd)
  2. 2 {
  3. 3 msg = string.Empty;
  4. 4 try
  5. 5 {
  6. 6 int index = 0;
  7. 7 do
  8. 8 {
  9. 9 int len = tcpClient.GetStream().Read(rd, index, rd.Length - index);
  10. 10 if (len == 0)
  11. 11 return false;//这里控制读取不到数据时就跳出,网络异常断开,数据读取不完整。
  12. 12 else
  13. 13 index += len;
  14. 14 } while (index < rd.Length);
  15. 15 return true;
  16. 16 }
  17. 17 catch(Exception ex)
  18. 18 {
  19. 19 msg = ex.Message;
  20. 20 return false;
  21. 21 }
  22. 22 }
View Code
  • 基于Socket的发送与接收Byte[]方法

发送BYTE

  1. 1 public bool SendData(out string msg,byte[] sd)
  2. 2 {
  3. 3 msg = string.Empty;
  4. 4 try
  5. 5 {
  6. 6 if(!(IsConnected && _Socket != null && _Socket.Connected))
  7. 7 {
  8. 8 if(!Connect(out msg))
  9. 9 {
  10. 10 Thread.Sleep(40);
  11. 11 if (!Connect(out msg)) return false;
  12. 12 }
  13. 13 }
  14. 14 _Socket.Send(sd, sd.Length, 0);
  15. 15 return true;
  16. 16 }
  17. 17 catch (Exception ex)
  18. 18 {
  19. 19 msg = ex.Message;
  20. 20 Disconnect(out string _msg);
  21. 21 return false;
  22. 22 }
  23. 23 }
View Code

接收BYTE

  1. 1 public bool ReceiveData(out string msg,byte[] rd)
  2. 2 {
  3. 3 msg = string.Empty;
  4. 4 try
  5. 5 {
  6. 6 if (!(IsConnected && _Socket != null && _Socket.Connected))
  7. 7 {
  8. 8 if (!Connect(out msg))
  9. 9 {
  10. 10 Thread.Sleep(40);
  11. 11 if (!Connect(out msg)) return false;
  12. 12 }
  13. 13 }
  14. 14 _Socket.Receive(rd, rd.Length, 0);
  15. 15 return true;
  16. 16 }
  17. 17 catch (Exception ex)
  18. 18 {
  19. 19 msg = ex.Message;
  20. 20 Disconnect(out string _msg);
  21. 21 return false;
  22. 22 }
  23. 23 }
View Code

这里由于当初写时的想法不同,有的在外层写了连接状态判断有的写在发送接收方法里面;

  • 网络判断
  1. 1 public static bool PingCheck(string ip, int connectTimeout = 10000)
  2. 2 {
  3. 3 Ping ping = new Ping();
  4. 4 PingReply pr = ping.Send(ip, connectTimeout);
  5. 5 if (pr.Status == IPStatus.Success)
  6. 6 return true;
  7. 7 else
  8. 8 return false;
  9. 9 }
View Code

欧姆龙PLC的连接与初始化

协议

  1. 1 private byte[] HandShake()
  2. 2 {
  3. 3 byte[] array = new byte[20];
  4. 4 array[0] = 0x46;
  5. 5 array[1] = 0x49;
  6. 6 array[2] = 0x4E;
  7. 7 array[3] = 0x53;
  8. 8
  9. 9 array[4] = 0;
  10. 10 array[5] = 0;
  11. 11 array[6] = 0;
  12. 12 array[7] = 0x0C;
  13. 13
  14. 14 array[8] = 0;
  15. 15 array[9] = 0;
  16. 16 array[10] = 0;
  17. 17 array[11] = 0;
  18. 18
  19. 19 array[12] = 0;
  20. 20 array[13] = 0;
  21. 21 array[14] = 0;
  22. 22 array[15] = 0;
  23. 23
  24. 24 array[16] = 0;
  25. 25 array[17] = 0;
  26. 26 array[18] = 0;
  27. 27 array[19] = 0;
  28. 28
  29. 29 return array;
  30. 30 }
View Code
  1. 1 private byte[] FinsCommand(RorW rw, PlcMemory mr, MemoryType mt, short ch, short offset, short cnt)
  2. 2 {
  3. 3 byte[] array = new byte[34];
  4. 4 //TCP FINS header
  5. 5 array[0] = 0x46;//F
  6. 6 array[1] = 0x49;//I
  7. 7 array[2] = 0x4E;//N
  8. 8 array[3] = 0x53;//S
  9. 9
  10. 10 array[4] = 0;//cmd length
  11. 11 array[5] = 0;
  12. 12
  13. 13 if (rw == RorW.Read)
  14. 14 {
  15. 15 array[6] = 0;
  16. 16 array[7] = 0x1A;//26
  17. 17 }
  18. 18 else
  19. 19 {
  20. 20 //写数据的时候一个字占两个字节,而一个位只占一个字节
  21. 21 if (mt == MemoryType.Word)
  22. 22 {
  23. 23 array[6] = (byte)((cnt * 2 + 26) / 256);
  24. 24 array[7] = (byte)((cnt * 2 + 26) % 256);
  25. 25 }
  26. 26 else
  27. 27 {
  28. 28 array[6] = 0;
  29. 29 array[7] = 0x1B;
  30. 30 }
  31. 31 }
  32. 32
  33. 33 array[8] = 0;//frame command
  34. 34 array[9] = 0;
  35. 35 array[10] = 0;
  36. 36 array[11] = 0x02;
  37. 37
  38. 38 array[12] = 0;
  39. 39 array[13] = 0;
  40. 40 array[14] = 0;
  41. 41 array[15] = 0;
  42. 42 //command frame header
  43. 43 array[16] = 0x80;//ICF
  44. 44 array[17] = 0x00;//RSV
  45. 45 array[18] = 0x02;//GCT, less than 8 network layers
  46. 46 array[19] = 0x00;//DNA, local network
  47. 47
  48. 48 array[20] = PLCNode;//DA1
  49. 49 array[21] = 0x00;//DA2, CPU unit
  50. 50 array[22] = 0x00;//SNA, local network
  51. 51 array[23] = PCNode;//SA1
  52. 52
  53. 53 array[24] = 0x00;//SA2, CPU unit
  54. 54 array[25] = 0xFF;
  55. 55
  56. 56 //指令码
  57. 57 if (rw == RorW.Read)
  58. 58 {
  59. 59 array[26] = 0x01;//cmdCode--0101
  60. 60 array[27] = 0x01;
  61. 61 }
  62. 62 else
  63. 63 {
  64. 64 array[26] = 0x01;//write---0102
  65. 65 array[27] = 0x02;
  66. 66 }
  67. 67 //地址
  68. 68 //array[28] = (byte)mr;
  69. 69 array[28] = GetMemoryCode(mr, mt);
  70. 70 array[29] = (byte)(ch / 256);
  71. 71 array[30] = (byte)(ch % 256);
  72. 72 array[31] = (byte)offset;
  73. 73
  74. 74 array[32] = (byte)(cnt / 256);
  75. 75 array[33] = (byte)(cnt % 256);
  76. 76
  77. 77 return array;
  78. 78 }
View Code

这边连接初始化时需要获取网络节点号

  1. 1 public bool Open(out string msg)
  2. 2 {
  3. 3 msg = string.Empty;
  4. 4 try
  5. 5 {
  6. 6 if (!SocketHelper.PingCheck(Ip, ConnectTimeout))
  7. 7 {
  8. 8 msg = "网络故障!";
  9. 9 return false;
  10. 10 }
  11. 11 System.Diagnostics.Stopwatch sp = new System.Diagnostics.Stopwatch();
  12. 12 sp.Start();
  13. 13 tcpClient = new TcpClient();
  14. 14 tcpClient.ReceiveTimeout = ReceiveTimeout;
  15. 15 tcpClient.SendTimeout = SendTimeout;
  16. 16 tcpClient.Connect(Ip, Port);
  17. 17 Thread.Sleep(10);
  18. 18 if (!tcpClient.Connected)
  19. 19 {
  20. 20 throw new ApplicationException($"未连接到{Ip}");
  21. 21 }
  22. 22 if (!SocketHelper.SendData(out msg, tcpClient, HandShake()))
  23. 23 {
  24. 24 msg = $"连接,数据写入失败:{msg}!";
  25. 25 return false;
  26. 26 }
  27. 27
  28. 28 //开始读取返回信号
  29. 29 byte[] buffer = new byte[24];
  30. 30 if (!SocketHelper.ReceiveData(out msg, tcpClient, buffer))
  31. 31 {
  32. 32 msg = $"连接握手信号接收失败:{msg}!";
  33. 33 return false;
  34. 34 }
  35. 35
  36. 36 if (buffer[15] != 0)//TODO:这里的15号是不是ERR信息暂时不能完全肯定
  37. 37 {
  38. 38 msg = $"超过最大连接数或内部连接错误";
  39. 39 return false;
  40. 40 }
  41. 41 PCNode = buffer[19];
  42. 42 PLCNode = buffer[23];
  43. 43 msg = $"连接[{Ip}]成功,耗时{sp.Elapsed.TotalMilliseconds.ToString()}ms";
  44. 44 return true;
  45. 45
  46. 46 }
  47. 47 catch (Exception ex)
  48. 48 {
  49. 49 Close(out string _msg);//连接断开,重试
  50. 50 msg = $"连接失败:{ex.Message}";
  51. 51 return false;
  52. 52 }
  53. 53 }
View Code

读取方法

  1. 1 public bool ReadWordsByte_B(out string msg, PlcMemory mr, int startIndex, int len, out byte[] reData)
  2. 2 {
  3. 3 msg = string.Empty; reData = new byte[0];
  4. 4 try
  5. 5 {
  6. 6 System.Diagnostics.Stopwatch sp = new System.Diagnostics.Stopwatch();
  7. 7 sp.Start();
  8. 8 int i = 0;
  9. 9 for (int index = startIndex; index < startIndex + len; index += OmronConsts.MAXREADDATE)
  10. 10 {
  11. 11 int _newLen = len + startIndex- index;
  12. 12 if (_newLen > OmronConsts.MAXREADDATE) _newLen = OmronConsts.MAXREADDATE;
  13. 13 i++;
  14. 14 byte[] array = FinsCmd(RorW.Read, mr, MemoryType.Word, (short)(index/2), 00, (short)(_newLen/2));
  15. 15
  16. 16 if (!SocketHelper.SendData(out msg, tcpClient, array))
  17. 17 {
  18. 18 msg = $"读取,数据写入失败[{i}次]:{msg}!";
  19. 19 return false;
  20. 20 }
  21. 21 byte[] buffer = new byte[30 + _newLen];//用于接收数据的缓存区大小
  22. 22 if (!SocketHelper.ReceiveData(out msg, tcpClient, buffer))
  23. 23 {
  24. 24 msg = $"读取,数据接收失败[{i}次]:{msg}!";
  25. 25 return false;
  26. 26 }
  27. 27 //命令返回成功,继续查询是否有错误码,然后在读取数据
  28. 28 if (buffer[11] == 3)
  29. 29 {
  30. 30 if (!ErrorCode.CheckHeadError(buffer[15], out msg))
  31. 31 {
  32. 32 msg = $"读取数据失败[{i}次]:{msg}!";
  33. 33 return false;
  34. 34 }
  35. 35 }
  36. 36 //endcode为fins指令的返回错误码
  37. 37 if (!ErrorCode.CheckEndCode(buffer[28], buffer[29], out msg))
  38. 38 {
  39. 39 msg = $"读取数据失败[{i}次]:{msg}!";
  40. 40 return false;
  41. 41 }
  42. 42 byte[] _bytes = new byte[_newLen];
  43. 43
  44. 44 Array.Copy(buffer, 30, _bytes, 0, _newLen);
  45. 45
  46. 46 reData = reData.Concat(_bytes).ToArray();
  47. 47 }
  48. 48
  49. 49 msg = $"读取({reData.Length})字节数据成功,耗时{sp.Elapsed.TotalMilliseconds.ToString()}ms,{i}次读取";
  50. 50 return true;
  51. 51 }
  52. 52 catch (Exception ex)
  53. 53 {
  54. 54 msg = ex.Message;
  55. 55 return false;
  56. 56 }
  57. 57 }
View Code

写入方法

  1. 1 public bool WriteWordsByte_B(out string msg, PlcMemory mr, short startIndex, byte[] inData)
  2. 2 {
  3. 3 msg = string.Empty;
  4. 4 try
  5. 5 {
  6. 6 if (inData == null || inData.Length < 1)
  7. 7 {
  8. 8 msg = "写入数据失败,写入数据为空!";
  9. 9 return false;
  10. 10 }
  11. 11 //奇数补零,写入数据必须为一个字
  12. 12 if ((inData.Length % 2) > 0)
  13. 13 {
  14. 14 inData = inData.Concat(new byte[1] { 0 }).ToArray();
  15. 15 }
  16. 16 //写入长度大于2000
  17. 17 System.Diagnostics.Stopwatch sp = new System.Diagnostics.Stopwatch();
  18. 18 sp.Start();
  19. 19 int i = 0;int len = inData.Length;
  20. 20 for (int index= startIndex; index < startIndex + len; index += OmronConsts.MAXRWRIDATE)
  21. 21 {
  22. 22 int _newLen = len + startIndex - index;
  23. 23 if (_newLen > OmronConsts.MAXRWRIDATE) _newLen = OmronConsts.MAXRWRIDATE;
  24. 24 i++;
  25. 25 byte[] nData = new byte[_newLen];
  26. 26
  27. 27 Array.Copy(inData, index- startIndex, nData,0, _newLen);
  28. 28
  29. 29 byte[] dataHead = FinsCmd(RorW.Write, mr, MemoryType.Word, (short)(index/2), 00, (short)(_newLen /2));
  30. 30
  31. 31 byte[] zData = new byte[_newLen+34];
  32. 32
  33. 33 dataHead.CopyTo(zData,0);
  34. 34
  35. 35 nData.CopyTo(zData, 34);
  36. 36
  37. 37 if (!SocketHelper.SendData(out msg, tcpClient, zData))
  38. 38 {
  39. 39 msg = $"写入,数据写入失败[{i}次]:{msg}!";
  40. 40 return false;
  41. 41 }
  42. 42 byte[] rBuffer= new byte[30];
  43. 43 if (!SocketHelper.ReceiveData(out msg, tcpClient, rBuffer))
  44. 44 {
  45. 45 msg = $"写入,数据接收失败[{i}次]:{msg}!";
  46. 46 return false;
  47. 47 }
  48. 48 if (rBuffer[11] == 3)
  49. 49 {
  50. 50 if (!ErrorCode.CheckHeadError(rBuffer[15], out msg))
  51. 51 {
  52. 52 msg = $"写入数据失败[{i}次]:{msg}!";
  53. 53 return false;
  54. 54 }
  55. 55 }
  56. 56 if (!ErrorCode.CheckEndCode(rBuffer[28], rBuffer[29], out msg))
  57. 57 {
  58. 58 msg = $"写入数据失败[{i}次]:{msg}!";
  59. 59 return false;
  60. 60 }
  61. 61
  62. 62 }
  63. 63 msg = $"写入({len})字节数据成功,耗时{sp.Elapsed.TotalMilliseconds.ToString()}ms,{i}次写入";
  64. 64 return true;
  65. 65 }
  66. 66 catch (Exception ex)
  67. 67 {
  68. 68 msg = ex.Message;
  69. 69 return false;
  70. 70 }
  71. 71 }
View Code

通过读取与写入方法就完成了对欧姆龙PLC的交互

测试结果

 

 

 

 

完毕!

 

本文来自博客园,作者:豆腐柠檬,转载请注明原文链接:https://www.cnblogs.com/ToufuLemon/p/15751713.html

原文链接:http://www.cnblogs.com/ToufuLemon/p/15751713.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号