Files
DevToolsAvalonia/常用工具集/Utility/Network/EIP/CIPHelper.cs
2025-08-26 08:37:44 +08:00

588 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace EIP调试.EIP
{
/// <summary>
/// CIP通信工具类
/// </summary>
public class CIPHelper
{
/// <summary>
/// IP地址
/// </summary>
public string IpAddress { get; set; }
/// <summary>
/// 通信端口号
/// </summary>
public int Port { get; set; }
public bool IsConnected
{
get
{
if (client == null)
return false;
return client.Connected;
}
}
private Socket client;
public int SessionHandle = 0;
/// <summary>
/// 默认无参构造
/// </summary>
public CIPHelper()
{
}
/// <summary>
/// 构造方法传递通信IP与端口
/// </summary>
public CIPHelper(string ip, int port)
{
this.IpAddress = ip;
this.Port = port;
}
/// <summary>
/// 建立连接
/// </summary>
public void Connect()
{
//连接
try
{
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.Connect(new IPEndPoint(IPAddress.Parse(IpAddress), Port));
}
catch (Exception ex)
{
}
}
/// <summary>
/// 注册会话
/// </summary>
public bool RegisterSession()
{
//注册会话
if (client != null && client.Connected)
{
byte[] registerSessionBytes = { (byte)0x65, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 };
client.Send(registerSessionBytes);
Thread.Sleep(20);
byte[] rev = new byte[1024];
try
{
int length = client.Receive(rev);
byte[] data = new byte[length];
Array.Copy(rev, 0, data, 0, length);
if (data[0] == 0x65)
return GetSessionHandle(data);
}
catch
{
return false;
}
}
return false;
}
/// <summary>
/// 读取标签值
/// </summary>
/// <param name="tag"></param>
/// <returns></returns>
public object ReadTag(string tag)
{
if (client == null)
return null;
if (!client.Connected)
return null;
if (SessionHandle == 0)
return null;
byte[] d = GetReadTagBytes(tag);
client.Send(d);
Thread.Sleep(20);
byte[] rev = new byte[1024];
try
{
int length = client.Receive(rev);
byte[] data = new byte[length];
Array.Copy(rev, 0, data, 0, length);
if (data[0] == 0x6f)
return ReadTagVal(data);
return null;
}
catch
{
return null;
}
}
public bool WriteTag(string tag, object value)
{
if (client == null)
return false;
if (!client.Connected)
return false;
if (SessionHandle == 0)
return false;
byte[] d = GetWriteTagBytes(tag, value);
client.Send(d);
Thread.Sleep(20);
byte[] rev = new byte[1024];
try
{
int length = client.Receive(rev);
byte[] data = new byte[length];
Array.Copy(rev, 0, data, 0, length);
if (data[0] == 0x6f)
return WriteTagVal(data);
return false;
}
catch
{
return false;
}
}
/// <summary>
/// 注销会话
/// </summary>
/// <returns></returns>
public bool UnRegisterSession()
{
if (client == null)
return false;
if (!client.Connected)
return false;
if (SessionHandle == 0)
return false;
List<byte> UnregisterSession = new List<byte>();
UnregisterSession.AddRange(new List<byte> { (byte)0x66, (byte)0x00, (byte)0x00, (byte)0x00 });
UnregisterSession.AddRange(BitConverter.GetBytes(SessionHandle));
UnregisterSession.AddRange(new List<byte> { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 });
try
{
client.Send(UnregisterSession.ToArray());
return true;
}
catch
{
return false;
}
}
/// <summary>
/// 断开连接
/// </summary>
public void DisConnect()
{
if (client == null)
return;
//断开
try
{
if (IsConnected)
{
client.Close();
}
client = null;
SessionHandle = 0;
}
catch (Exception ex)
{
client = null;
SessionHandle = 0;
}
}
#region Private
/// <summary>
/// 解析写标签数据
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private bool WriteTagVal(byte[] data)
{
int byte_index = 0;
short var1 = BitConverter.ToInt16(data, 0);//命令码
short var2 = BitConverter.ToInt16(data, 2);//去除Hearder后的长度
if ((var2 + 24) != data.Length)//24是Hearder封装头的长度 封装头长度+后面报文长度 应该等于总长度 否则报文错误
{
return false;
}
int sessionHandle = BitConverter.ToInt32(data, 4);
if (sessionHandle != this.SessionHandle)
{
//如果会话句柄不一致 返回
return false;
}
int state = BitConverter.ToInt32(data, 8);
if (state != 0)
{
//会话状态不正确
return false;
}
//00 00 00 00 01 00 02 00 00 00 00 00 B2 00 08 00 CC 00 00 00 C1 00 00 00
//发送方描述
byte_index = 12; //8
//选项 4字节
byte_index = 20;
//接口句柄 4字节
byte_index = 24;
//超时 2字节
byte_index = 28;
//项数 2字节
byte_index = 30;
//连接的地址项 2字节
byte_index = 32;
//连接地址项长度 2字节
byte_index = 34;
//未连接数据项 2字节
byte_index = 36;
//连接长度 2字节
byte_index = 38;
//数据开始
byte_index = 40;
for (int i = byte_index; i < data.Length; i++)
{
if (data[i] != 0xCD)
continue;
//填充字节 1字节
if (BitConverter.ToInt16(data, i + 2) != 0x00)
{
return true;
}
return false;
}
return false;
}
/// <summary>
/// 解析读标签数据
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private object ReadTagVal(byte[] data)
{
int byte_index = 0;
short var1 = BitConverter.ToInt16(data, 0);//命令码
short var2 = BitConverter.ToInt16(data, 2);//去除Hearder后的长度
if ((var2 + 24) != data.Length)//24是Hearder封装头的长度 封装头长度+后面报文长度 应该等于总长度 否则报文错误
{
return null;
}
int sessionHandle = BitConverter.ToInt32(data, 4);
if (sessionHandle != this.SessionHandle)
{
//如果会话句柄不一致 返回
return null;
}
int state = BitConverter.ToInt32(data, 8);
if (state != 0)
{
//会话状态不正确
return null;
}
//发送方描述
byte_index = 12; //8
//选项 4字节
byte_index = 20;
//接口句柄 4字节
byte_index = 24;
//超时 2字节
byte_index = 28;
//项数 2字节
byte_index = 30;
//连接的地址项 2字节
byte_index = 32;
//连接地址项长度 2字节
byte_index = 34;
//未连接数据项 2字节
byte_index = 36;
//连接长度 2字节
byte_index = 38;
//数据开始
byte_index = 40;
for (int i = byte_index; i < data.Length; i++)
{
if (data[i] != 0xCC)
continue;
//服务标识 0xCC
//填充字节 1字节
if (BitConverter.ToInt16(data, i + 2) != 0x00) // data[var4] != 0x00 || data[var4 + 1] != 0x00)
{
return null;
}
//数据类型
short dataType = BitConverter.ToInt16(data, i + 4);
if (dataType == 0x00C1)//BOOL
{
bool value = Convert.ToBoolean(((short)data[i + 6] & 0x00ff) | ((short)data[i + 7] << 8));
return value;
}
if (dataType == 0x00D1)//byte
{
byte value = data[i + 6];
return value;
}
if (dataType == 0x00C3)//int
{
short value = BitConverter.ToInt16(data, i + 6);// ((short)data[i+6] & 0x00ff) | ((short)data[i+7] << 8);
return value;
}
if (dataType == 0x00C4)//long型
{
int value = BitConverter.ToInt32(data, i + 6);
return value;
}
if (dataType == 0x00CA)//float
{
float value = BitConverter.ToSingle(data, i + 6);
return value;
}
}
return null;
}
/// <summary>
/// 写标签报文数据
/// </summary>
/// <param name="tag"></param>
/// <param name="value"></param>
/// <returns></returns>
private byte[] GetWriteTagBytes(string tag, object value)
{
byte[] TagStringToAscii;
byte[] Var4 = Encoding.Default.GetBytes(tag);
if (Var4.Length % 2 == 0)
{
TagStringToAscii = new byte[Var4.Length];
for (int i = 0; i < Var4.Length; i++)
{
TagStringToAscii[i] = Var4[i];
}
}
else
{
TagStringToAscii = new byte[Var4.Length + 1];
for (int i = 0; i < Var4.Length; i++)
{
TagStringToAscii[i] = Var4[i];
}
TagStringToAscii[Var4.Length] = 0x00;
}
//CIP协议数据 =================================================
List<byte> Cip_Msg = new List<byte>();
Cip_Msg.Add((byte)0x4D);//服务标识
Cip_Msg.Add((byte)((TagStringToAscii.Length + 2) / 2));//CIP长度多少字 从标识开始 大读取标签长度结束
Cip_Msg.Add((byte)0x91);//固定
Cip_Msg.Add((byte)TagStringToAscii.Length);//PLC标签长度 多少个字节
Cip_Msg.AddRange(TagStringToAscii);//添加标签
//根据类型
if (value is bool)
{
Cip_Msg.AddRange(new byte[] { 0xC1, 0x00 });
if ((bool)value)
{
Cip_Msg.AddRange(new byte[] { 0x01, 0x00 });
}
else
{
Cip_Msg.AddRange(new byte[] { 0x00, 0x00 });
}
}
else if (value is byte)
{
Cip_Msg.AddRange(new byte[] { 0xD1, 0x00 });
Cip_Msg.Add((byte)value);
}
else if (value is short)
{
Cip_Msg.AddRange(new byte[] { 0xC3, 0x00 });
Cip_Msg.AddRange(BitConverter.GetBytes((short)value));
}
else if (value is int)
{
Cip_Msg.AddRange(new byte[] { 0xC4, 0x00 });
Cip_Msg.AddRange(BitConverter.GetBytes((int)value));
}
else if (value is float)
{
Cip_Msg.AddRange(new byte[] { 0xCA, 0x00 });
Cip_Msg.AddRange(BitConverter.GetBytes((float)value));
}
List<byte> Slot = new List<byte> { (byte)0x01, (byte)0x00, (byte)0x01, (byte)0x00 }; //槽号
List<byte> Cip_Msg0 = new List<byte>();
Cip_Msg0.Add((byte)0x52);//命令
Cip_Msg0.Add((byte)0X02);//请求路径长度
Cip_Msg0.AddRange(new byte[] { 0x20, 0x06, 0x24, 0x01 });//默认请求路径
Cip_Msg0.AddRange(new byte[] { 0x0A, 0xF0 });//默认超时
Cip_Msg0.AddRange(BitConverter.GetBytes((short)Cip_Msg.Count)); //后面报文长度 不包含PLC槽号
Cip_Msg0.AddRange(Cip_Msg);//添加CIP报文
List<byte> C_S_D = new List<byte>();
C_S_D.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00 });//接口句柄 CIP
C_S_D.AddRange(new byte[] { 0x01, 0x00 });//超时时间 默认
C_S_D.AddRange(new byte[] { 0x02, 0x00 });//项数 默认
C_S_D.AddRange(new byte[] { 0x00, 0x00 });//空地址项 默认
C_S_D.AddRange(new byte[] { 0x00, 0x00 });//空地址长度 默认
C_S_D.AddRange(new byte[] { 0xB2, 0x00 });//未连接项 默认
C_S_D.AddRange(BitConverter.GetBytes((short)(Cip_Msg0.Count + Slot.Count)));//CIP报文包的长度 包括槽号
List<byte> Header = new List<byte>();
Header.AddRange(new byte[] { 0x6F, 0x00 });//命令码
Header.AddRange(BitConverter.GetBytes((short)(Cip_Msg0.Count + C_S_D.Count + Slot.Count)));//后面报文的长度 特定命令数据 和 CIP报文长度 和PLC槽号
Header.AddRange(BitConverter.GetBytes(SessionHandle));//添加会话句柄
Header.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00 });//添加状态
Header.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); //发送方描述
Header.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00 });//选项默认
List<byte> EtherNet_IP_CIP_MSG = new List<byte>();//单标签读取完整报文
EtherNet_IP_CIP_MSG.AddRange(Header);//添加Header 封装头
EtherNet_IP_CIP_MSG.AddRange(C_S_D);//添加特定命令数据
EtherNet_IP_CIP_MSG.AddRange(Cip_Msg0);//添加CIP报文
EtherNet_IP_CIP_MSG.AddRange(Slot);//添加槽号
return EtherNet_IP_CIP_MSG.ToArray();
}
/// <summary>
/// 读标签报文数据
/// </summary>
/// <param name="tag"></param>
/// <returns></returns>
private byte[] GetReadTagBytes(string tag)
{
byte[] TagStringToAscii;
byte[] Var4 = Encoding.Default.GetBytes(tag);
if (Var4.Length % 2 == 0)
{
TagStringToAscii = new byte[Var4.Length];
for (int i = 0; i < Var4.Length; i++)
{
TagStringToAscii[i] = Var4[i];
}
}
else
{
TagStringToAscii = new byte[Var4.Length + 1];
for (int i = 0; i < Var4.Length; i++)
{
TagStringToAscii[i] = Var4[i];
}
TagStringToAscii[Var4.Length] = 0x00;
}
//CIP协议数据 =================================================
List<byte> Cip_Msg = new List<byte>();
Cip_Msg.Add((byte)0x4C);//服务标识
Cip_Msg.Add((byte)((TagStringToAscii.Length + 2) / 2));//CIP长度多少字 从标识开始 大读取标签长度结束
Cip_Msg.Add((byte)0x91);//固定
Cip_Msg.Add((byte)TagStringToAscii.Length);//PLC标签长度 多少个字节
Cip_Msg.AddRange(TagStringToAscii);//添加标签
Cip_Msg.Add((byte)0x01);
Cip_Msg.Add((byte)0x00); // 读取长度
List<byte> Slot = new List<byte> { (byte)0x01, (byte)0x00, (byte)0x01, (byte)0x00 }; //槽号
List<byte> Cip_Msg0 = new List<byte>();
Cip_Msg0.Add((byte)0x52);//命令
Cip_Msg0.Add((byte)0X02);//请求路径长度
Cip_Msg0.AddRange(new byte[] { 0x20, 0x06, 0x24, 0x01 });//默认请求路径
Cip_Msg0.AddRange(new byte[] { 0x0A, 0xF0 });//默认超时
Cip_Msg0.AddRange(BitConverter.GetBytes((short)Cip_Msg.Count)); //后面报文长度 不包含PLC槽号
Cip_Msg0.AddRange(Cip_Msg);//添加CIP报文
List<byte> C_S_D = new List<byte>();
C_S_D.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00 });//接口句柄 CIP
C_S_D.AddRange(new byte[] { 0x01, 0x00 });//超时时间 默认
C_S_D.AddRange(new byte[] { 0x02, 0x00 });//项数 默认
C_S_D.AddRange(new byte[] { 0x00, 0x00 });//空地址项 默认
C_S_D.AddRange(new byte[] { 0x00, 0x00 });//空地址长度 默认
C_S_D.AddRange(new byte[] { 0xB2, 0x00 });//未连接项 默认
C_S_D.AddRange(BitConverter.GetBytes((short)(Cip_Msg0.Count + Slot.Count)));//CIP报文包的长度 包括槽号
List<byte> Header = new List<byte>();
Header.Add((byte)0x6F); Header.Add((byte)0x00);//命令码
Header.AddRange(BitConverter.GetBytes((short)(Cip_Msg0.Count + C_S_D.Count + Slot.Count)));//后面报文的长度 特定命令数据 和 CIP报文长度 和PLC槽号
Header.AddRange(BitConverter.GetBytes(SessionHandle));//添加会话句柄
Header.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00 });//添加状态
Header.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); //发送方描述
Header.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00 });//选项默认
List<byte> EtherNet_IP_CIP_MSG = new List<byte>();//单标签读取完整报文
EtherNet_IP_CIP_MSG.AddRange(Header);//添加Header 封装头
EtherNet_IP_CIP_MSG.AddRange(C_S_D);//添加特定命令数据
EtherNet_IP_CIP_MSG.AddRange(Cip_Msg0);//添加CIP报文
EtherNet_IP_CIP_MSG.AddRange(Slot);//添加槽号
return EtherNet_IP_CIP_MSG.ToArray();
}
/// <summary>
/// 注册会话结果解析
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private bool GetSessionHandle(byte[] data)
{
//short sessionLength = BitConverter.ToInt16(data, 2);// (Convert.ToInt16(data[3]) * 256) | Convert.ToInt16(data[2]);
//byte[] var1 = new byte[4];
//for (int i = 0; i < 4; i++)
//{
// var1[i] = data[sessionLength + 4 + i];
//}
int state = BitConverter.ToInt32(data, 8);
if (state == 0)//判断报文状态
{
SessionHandle = BitConverter.ToInt32(data, 4);
//SessionHandle = new byte[sessionLength];
//for (int i = 0; i < sessionLength; i++)
//{
// SessionHandle[i] = data[4 + i];
//}
return true;
}
else
{
return false;
}
}
#endregion
}
}