马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
1、S7通讯回首
- (1)创建TCP毗连 Socket.Connect-》已实现
- (2)发送访问哀求 COTP-》已实现
- (3)交换通讯信息 Setup Communication-》本节实现
- (4)实验相干使用 读、写、PLC启停、时间、上传下载-》待实现
2、S7COMM哀求先容
在乐成完成上面的socket毗连和COTP哀求后,如今客户端还不能举行读写使用,还须要举行哀求创建通讯,以是客户端须要发送S7 Communicaton报文给服务端,在S7Communicaton报文中包罗“通讯哀求”; 服务端反馈S7 Communicaton报文。同样的报文中每个字段都有寄义,必须严格服从,这个过程是交换通讯信息, 报文信息如下
留意下面几点:
1》从中可以看到发送是25个字节,相应是27个字节,可以看到发送报文由4个部分构成:TPKT占4个字节0-3,COTP占3个字节4-6,S7-Header占10个字节7-16,S7-Parameter占8个字节17-24,看到了不?但是相应报文27个,也是由4个部分构成,但S7-Header占12个字节,其他一样长度,留意了不?过细看这里,明确了,肯定要搞明确哦。
2》ROSCTR,它是个摆列值,具体如下:
看这个地方的差别,发送是01,相应是03,对应上面的值就是表现上位机哀求是job,PLC相应是从站相应并带数据
3》PDU Type,跟上节的不一样,这次是发送吸取都是f0,上节的COTP报文是e0和d0,再来看下
4》Error class已知摆列值,这个出如今相应报文中,可以看到00表现没有错误,这是我们必须看到的,假如不是00表现你的报文有bug
5》Error code已知摆列值,这个出如今相应报文中,可以看到00表现没有错误,这是我们必须看到的,假如不是00表现你的报文有bug
6》发送报文中的功能码,即17字节,Function已知摆列值,如今知道发送报文为什么是f0了吧
这些东西都要明确,否则写起代码起来就会堕落,就不能象我一样举行云云的分析和手撕,来个超等的啵啵啵。
3、开搞S7COMM通讯
完备代码 - using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net.Sockets;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- namespace west.siemenscomm
- {
- internal class Program
- {
- /// <summary>
- /// plc的ip地址
- /// </summary>
- static string _ip = "192.168.1.66";
- /// <summary>
- /// 端口号
- /// </summary>
- static int _port = 102;
- /// <summary>
- /// 机柜号,插槽号
- /// </summary>
- static byte _rack = 0, _slot = 1;
- /// <summary>
- /// socket对象
- /// </summary>
- static Socket socket = null;
- /// <summary>
- /// 时间事件
- /// </summary>
- static ManualResetEvent TimeoutObject = new ManualResetEvent(false);
- /// <summary>
- /// 连接状态
- /// </summary>
- static bool connectState = false;
- /// <summary>
- /// 通讯连接的pdu长度
- /// </summary>
- static short _pduSize = 240;
- static void Main(string[] args)
- {
- Connect();
- if (connectState)
- {
- COTPConnection();
- if (connectState)
- {
- SetupCommunication();
- }
- }
- Console.ReadKey();
- }
- /// <summary>
- /// 通讯连接
- /// </summary>
- /// <exception cref="NotImplementedException"></exception>
- private static bool SetupCommunication()
- {
- //s7comm连接包括4个部分,共25个字节,即25=4+3+10+8
- byte[] setupBytes = new byte[] {
- // 1)TPKT包括4个字节
- 0x03,//版本默认3
- 0x00,//保留默认0
- 0x00,//整个请求字节数高位
- 0x19,//整个请求字节数低位,0x19转换成10进制就是25
- //2)COTP包括3个字节
- 0x02,//当前字节以后的字节数(不包括自已,0x02转换成10进制就是2),注意这个“当前字节以后的字节数”是指COTP这部分,而不是整个字节部分
- 0xf0,//PDU Type,0xe0 连接请求,0xd0 连接确认,0x08 断开请求,0x0c 断开确认,0x05 拒绝访问,0x01 加急数据,0x02 加急数据确认,0x04 用户数据,0x07 TPDU错误,0xf0 数据传输
- 0x80,//TPDU number,固定值
- // 3)Header包括10个字节
- 0x32,//默认值,协议id
- 0x01,//ROSCTR,0x01 Job request。主站发送请求,0x02 Ack。从站响应请求不带数据,0x03 Ack_Data。从站响应请求并带有数据,0x07 Userdata。原始协议的扩展。读取编程/调试、SZL读取、安全功能、时间设置等
- 0x00,//Redundancy Identification (Reserved)固定值,占2个字节
- 0x00,
- 0x00,//Protocol Data Unit Reference固定值,占2个字节
- 0x00,
- 0x00,//Parameter length参数长度,占2个字节
- 0x08,
- 0x00,//Data length数据长度,占2个字节
- 0x00,
- // 4)Parameter包括8个字节
- 0xf0,//Function功能码,具体是:0x00 CPU服务,0xF0 设置通信,0x04 读取变量,0x05 写变量,0x1A 请求下载,0x1B 下载块,0x1C 下载结束,0x1D 开始上传,0x1E 上传,0x1F 结束上传,0x28 PLC 控制,0x29 PLC 停止
- 0x00,//保留默认值
- 0x00,//Max AmQ(parallel jobs with ack) calling,占2个字节
- 0x03,
- 0x00,//Max AmQ(parallel jobs with ack) called,占2个字节
- 0x03,
- 0x03,//PDU length,占2个字节,0x03co转换成10进制就是960
- 0xc0
- };
- try
- {
- socket.Send(setupBytes);
- //响应报文的长度就是固定的27个字节
- byte[] respBytes = new byte[27];
- int count = socket.Receive(respBytes);
- // 拿到PDU长度 后续进行报文组装和接收的时候可以参考
- byte[] pdu_size = new byte[2];
- pdu_size[0] = respBytes[26];
- pdu_size[1] = respBytes[25];
- _pduSize = BitConverter.ToInt16(pdu_size,0);
- if (respBytes[17] != 0x00&& respBytes[18] != 0x00)
- {
- Console.WriteLine("粗问题,COMM连接响应异常");
- connectState = false;
- }
- else
- {
- Console.WriteLine("太好了,COMM连接响应正常");
- connectState = true;
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine("Setup通信未建立!" + ex.Message);
- connectState = false;
- }
- return connectState;
- }
- /// <summary>
- /// cotp连接
- /// </summary>
- private static bool COTPConnection()
- {
- //COTP连接包括2个部分,共22个字节),22=4+18
- byte[] cotpBytes = new byte[] {
- //1)TPKT包括4个字节
- 0x03,//版本号,版本默认3
- 0x00,//默认保留为0
- 0x00,//整个请求字节高位
- 0x16,//整个请求字节低位(0x16转换成为10进制就是22)
- //2)COTP包括18个字节
- 0x11,//当前字节以后的字节数(不包括自已,0x11转换成10进制就是17)
- 0xe0,//PDU type,0xe0 连接请求,0xd0 连接确认,0x08 断开请求,0x0c 断开确认,0x05 拒绝访问,0x01 加急数据,0x02 加急数据确认,0x04 用户数据,0x07 TPDU错误,0xf0 数据传输
- 0x00,//DST reference(2个字节)
- 0x00,//
- 0x00,//SRC reference(2个字节)
- 0x00,//
- 0x00,//class(固定的)
- 0xc1, //Parameter-code src-tsap 上位机
- 0x02, //Parameter-Len
- 0x10 , //Source TSAP:01->PG;02->OP;03->S7单边(服务器模式);0x10->S7双边通
- 0x00, //机架与插槽号为0
- 0xc2,//Parameter-code dst-tsap PLC
- 0x02,//Parameter len
- 0x03,//Destination TSAP
- (byte)(_rack*32+_slot),//机架与插槽号:
- 0xc0, // Parameter code:tpdu-size
- 0x01, // Parameter length
- 0x0a // TPDU size
- };
- try
- {
- socket.Send(cotpBytes);
- //响应报文的长度是固定的22个字节
- byte[] respBytes = new byte[22];
- int count = socket.Receive(respBytes, 0, 22, SocketFlags.None);
- //第5个字节是pdu type,具体是:0xe0 连接请求,0xd0 连接确认,0x08 断开请求,0x0c 断开确认,0x05 拒绝访问,0x01 加急数据,0x02 加急数据确认,0x04 用户数据,0x07 TPDU错误,0xf0 数据传输
- if (respBytes[5] != 0xd0)
- {
- Console.WriteLine("粗问题,COTP连接响应异常");
- connectState = false;
- }
- else
- {
- Console.WriteLine("太好了,COTP连接响应正常");
- connectState = true;
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine("COTP连接未建立!" + ex.Message);
- connectState = false;
- }
- return connectState;
- }
- /// <summary>
- /// tcp连接
- /// </summary>
- /// <param name="timeout"></param>
- private static void Connect(int timeout = 50)
- {
- TimeoutObject.Reset();
- try
- {
- socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- socket.BeginConnect(_ip, _port, callback =>
- {
- connectState = false;
- var cbSocket = callback.AsyncState as Socket;
- if (cbSocket != null)
- {
- connectState = cbSocket.Connected;
- if (cbSocket.Connected)
- cbSocket.EndConnect(callback);
- }
- TimeoutObject.Set();
- }, socket);
- TimeoutObject.WaitOne(2000, false);
- }
- catch (SocketException ex)
- {
- if (ex.ErrorCode == 10060)
- Console.WriteLine(ex.Message);
- }
- if (socket == null || !socket.Connected || ((socket.Poll(200, SelectMode.SelectRead) && (socket.Available == 0))))
- {
- Console.WriteLine("网络连接失败");
- }
- Console.WriteLine(connectState == true ? "连接成功" : "连接失败");
- }
- }
- }
复制代码 4、运行测试
5、小结
这就是第3个流程,创建comm毗连,它是通过socket对象通讯实现的,小同伴们,明确了不?下节继承屌起来。
原创不易,打字截图不易,走过途经,不要错过,欢迎点赞,收藏,转载,复制,抄袭,留言,动动你的金手指,早日实现财政自由
|