1、S7通讯回顾
- (1)建立TCP连接 Socket.Connect-
- (2)发送访问请求 COTP-
- (3)交换通信信息 Setup Communication-
- (4)执行相关操作 读、写、PLC启停、时间、上传下载-
2、s7报文回顾
1、CTOP请求连接
2、SetupCommunication通讯数据交换
3、S7COMM-Read报文
4、S7COMM-Write报文
3、第3方通讯库回顾
S7.NET是一个广泛应用于.NET平台的西门子PLC通信库,在使用西门子PLC进行工业自动化控制的过程中,经常利用这个工具实现与PLC进行数据交换,它的方法通过上节操作应用可以看到格式是这样的,读取使用的命令是:plc.Read(varaddr),写入使用的命令是: plc.Write(varaddr, ts3),而我们的方法是一个个拼装字节数组,然后发送,然后解析报文,这对于应用者来说再痛苦了,能不能象第3方库那样,给个地址,调用命令就可以实现读取了,当然可以,这就是我们的目的,必须地强大武装起来,封装后的通讯库那是硬核,黄金。
4、开始封装
1、创建项目
2、添加类库项目
3、创建基础类
4、封装连接的方法
/// <summary>
/// 建立连接:包括3步:1》tcp连接,2》COTP连接,3》通信连接
/// </summary>
/// <param name="timeout">超时时间</param>
/// <returns></returns>
public Result Connect(int timeout = 50)
{
TimeoutObject.Reset();
Result result = new Result();
try
{
if (socket == null)
throw new Exception("通信对象未初始化");
int count = 0;
while (count < timeout)
{
// 第一步:建立tcp连接,即socket.Connected
// 断线重连:
// 1、被服务端主动踢掉(服务连接列表里已经没有当前客户端信息了)
// 2、断网(拔网线) 客户端(不知道-》新的端口进行连接)、服务端(可以知道、检查客户端的心跳)
if (!(!socket.Connected || (socket.Poll(200, SelectMode.SelectRead) && (socket.Available == 0))))
{
return result;
}
try
{
socket?.Close();
socket.Dispose();
socket = null;
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);
if (!connectState) throw new Exception();
else break;
}
catch (SocketException ex)
{
if (ex.ErrorCode == 10060)
throw new Exception(ex.Message);
}
catch (Exception) { }
finally
{
count++;
}
}
if (socket == null || !socket.Connected || ((socket.Poll(200, SelectMode.SelectRead) && (socket.Available == 0))))
{
throw new Exception("网络连接失败");
}
else
{
// 第二步:建立COTP连接请求
result = COTPConnection();
if (!result.IsSuccessed) return result;
// 第三步:建立Setup通信
result = SetupCommunication();
if (!result.IsSuccessed) return result;
}
}
catch (Exception ex)
{
result.IsSuccessed = false;
result.Message = ex.Message;
}
return result;
}
/// <summary>
/// 建立cotp连接
/// </summary>
/// <returns></returns>
private Result COTPConnection()
{
//COTP连接包括2个部分,共22个字节),22=4+18
Result result = new Result();
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)
{
throw new Exception("COTP连接响应异常");
}
}
catch (Exception ex)
{
result.IsSuccessed = false;
result.Message = "COTP连接未建立!" + ex.Message;
}
return result;
}
/// <summary>
/// 建立通讯连接
/// </summary>
/// <returns></returns>
private Result SetupCommunication()
{
//s7comm连接包括4个部分,共25个字节,即25=4+3+10+8
Result result = new Result();
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];
this._pduSize = BitConverter.ToInt16(pdu_size,0);
}
catch (Exception ex)
{
result.IsSuccessed = false;
result.Message = "Setup通信未建立!" + ex.Message;
}
return result;
}
5、封装读取数据的方法
/// <summary>
/// 读取数据
/// </summary>
/// <typeparam name="T">返回值类型</typeparam>
/// <param name="variable">变量地址</param>
/// <param name="count">变量个数</param>
/// <returns></returns>
public Result<T> Read<T>(string variable, int count = 1)
{
DataParameter dataParameter = new DataParameter();
Type mtype = typeof(T);
try
{
dataParameter.GetFromVariable(variable);// 解析地址,最终是需要一个DataParameter
if (mtype.Name == "Boolean")
{
//dataParameter.ParamItemType = ParameterItemType.BIT;
dataParameter.DataItemType = DataItemType.BIT;
}
dataParameter.Count = count;
// 获取数据
return this.Read<T>(
dataParameter.ParamItemType,
dataParameter.DataItemType,
dataParameter.Count,
dataParameter.AreaType,
dataParameter.DBNumber,
dataParameter.ByteAddress,
dataParameter.BitAddress);
}
catch (Exception ex)
{
return new Result<T>(false, ex.Message);
}
}
6、添加项目引用
5、测试效果
先看下PLC的数据
1、读取浮点数
2、读取short数
3、读取bool数
4、读取string
6、小结
自己的通讯库是够甘甜的,和新疆的葡萄一样的香甜,有人可能要问,前面不是有第3方通讯库写好的,拉来用不就行了吗?是的,没有错,别人的东西可以拉来用,但是我们的目标不仅是使用,而是形成自己的产品,实力,别人的库也是写的代码,只是我们看不到而已。同样的,我们自己也可以,强国工程,从小做起,不得不来个大大的鸡腿,真香啊。
原创不易,打字截图不易,走过路过,不要错过,欢迎点赞,收藏,转载,复制,抄袭,留言,动动你的金手指,早日实现财务自由