基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发

  • A+
所属分类:.NET技术
摘要

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

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

  1. FinsTcp协议报文格式

基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发

 

 

 获取PLC节点地址基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发

 

 

 FINS command

基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发

 

 

 IO存储器地址标识基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发

 

 

 

  2.实现过程

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

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

发送BYTE

基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发

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

View Code

接收BYTE

基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发

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

View Code

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

发送BYTE

基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发

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

View Code

接收BYTE

基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发

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

View Code

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

  • 网络判断
基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发

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

View Code

欧姆龙PLC的连接与初始化

协议

基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发

 1   private byte[] HandShake()  2         {  3             byte[] array = new byte[20];  4             array[0] = 0x46;  5             array[1] = 0x49;  6             array[2] = 0x4E;  7             array[3] = 0x53;  8   9             array[4] = 0; 10             array[5] = 0; 11             array[6] = 0; 12             array[7] = 0x0C; 13  14             array[8] = 0; 15             array[9] = 0; 16             array[10] = 0; 17             array[11] = 0; 18  19             array[12] = 0; 20             array[13] = 0; 21             array[14] = 0; 22             array[15] = 0; 23  24             array[16] = 0; 25             array[17] = 0; 26             array[18] = 0; 27             array[19] = 0; 28  29             return array; 30         }

View Code

基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发

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

View Code

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

基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发

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

View Code

读取方法

基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发

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

View Code

写入方法

基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发

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

View Code

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

测试结果

基于欧姆龙PLC#FinsTcp协议上位机通讯(二)-C#通讯模块开发

 

 

 

 

完毕!