ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2022-01-05 09:34:13  阅读:147  来源: 互联网

标签:通讯 return C# 欧姆龙 msg false array byte out


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

  1. FinsTcp协议报文格式

 

 

 获取PLC节点地址

 

 

 FINS command

 

 

 IO存储器地址标识

 

 

 

  2.实现过程

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

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

发送BYTE

 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

 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

 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

 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

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

  • 网络判断
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的连接与初始化

协议

 1 private byte[] HandShake()
 2         {
 3             #region fins command
 4             byte[] array = new byte[20];
 5             array[0] = 0x46;
 6             array[1] = 0x49;
 7             array[2] = 0x4E;
 8             array[3] = 0x53;
 9 
10             array[4] = 0;
11             array[5] = 0;
12             array[6] = 0;
13             array[7] = 0x0C;
14 
15             array[8] = 0;
16             array[9] = 0;
17             array[10] = 0;
18             array[11] = 0;
19 
20             array[12] = 0;
21             array[13] = 0;
22             array[14] = 0;
23             array[15] = 0;//ERR?
24 
25             array[16] = 0;
26             array[17] = 0;
27             array[18] = 0;
28             array[19] = 0;//TODO:ask for client and server node number, the client node will allocated automatically
29             //array[19] = this.GetIPNode(lIP);//本机IP地址的末位
30             #endregion fins command
31             return array;
32         }
View Code
 1 private byte[] FinsCmd(RorW rw, PlcMemory mr, MemoryType mt, short ch, short offset, short cnt)
 2         {
 3             //byte[] array;
 4             //if (rw == RorW.Read)
 5             //    array = new byte[34];
 6             //else
 7             //    array = new byte[(int)(cnt * 2 + 33 + 1)];//长度是如何确定的在fins协议174页
 8             byte[] array = new byte[34];//写指令还有后面的写入数组需要拼接在一起!
 9             //TCP FINS header
10             array[0] = 0x46;//F
11             array[1] = 0x49;//I
12             array[2] = 0x4E;//N
13             array[3] = 0x53;//S
14 
15             array[4] = 0;//cmd length
16             array[5] = 0;
17             //指令长度从下面字节开始计算array[8]
18             if (rw == RorW.Read)
19             {
20                 array[6] = 0;
21                 array[7] = 0x1A;//26
22             }
23             else
24             {
25                 //写数据的时候一个字占两个字节,而一个位只占一个字节
26                 if (mt == MemoryType.Word)
27                 {
28                     array[6] = (byte)((cnt * 2 + 26) / 256);
29                     array[7] = (byte)((cnt * 2 + 26) % 256);
30                 }
31                 else
32                 {
33                     array[6] = 0;
34                     array[7] = 0x1B;
35                 }
36             }
37 
38             array[8] = 0;//frame command
39             array[9] = 0;
40             array[10] = 0;
41             array[11] = 0x02;
42 
43             array[12] = 0;//err
44             array[13] = 0;
45             array[14] = 0;
46             array[15] = 0;
47             //command frame header
48             array[16] = 0x80;//ICF
49             array[17] = 0x00;//RSV
50             array[18] = 0x02;//GCT, less than 8 network layers
51             array[19] = 0x00;//DNA, local network
52 
53             array[20] = PLCNode;//DA1
54             array[21] = 0x00;//DA2, CPU unit
55             array[22] = 0x00;//SNA, local network
56             array[23] = PCNode;//SA1
57 
58             array[24] = 0x00;//SA2, CPU unit
59             array[25] = 0xFF;
60             //TODO:array[25] = Convert.ToByte(21);//SID//?????----------------------------------00-FF任意值
61 
62             //指令码
63             if (rw == RorW.Read)
64             {
65                 array[26] = 0x01;//cmdCode--0101
66                 array[27] = 0x01;
67             }
68             else
69             {
70                 array[26] = 0x01;//write---0102
71                 array[27] = 0x02;
72             }
73             //地址
74             //array[28] = (byte)mr;
75             array[28] = GetMemoryCode(mr, mt);
76             array[29] = (byte)(ch / 256);
77             array[30] = (byte)(ch % 256);
78             array[31] = (byte)offset;
79 
80             array[32] = (byte)(cnt / 256);
81             array[33] = (byte)(cnt % 256);
82 
83             return array;
84         }
View Code

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

 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

读取方法

 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

写入方法

 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的交互

测试结果

 

 

完毕!

 

标签:通讯,return,C#,欧姆龙,msg,false,array,byte,out
来源: https://www.cnblogs.com/ToufuLemon/p/15751713.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有