You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

429 lines
18 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace HslCommunication.Profinet.AllenBradley
{
/// <summary>
/// AB PLC的辅助类用来辅助生成基本的指令信息
/// </summary>
public class AllenBradleyHelper
{
#region Static Service Code
/// <summary>
/// CIP命令中的读取数据的服务
/// </summary>
public const byte CIP_READ_DATA = 0x4C;
/// <summary>
/// CIP命令中的写数据的服务
/// </summary>
public const int CIP_WRITE_DATA = 0x4D;
/// <summary>
/// CIP命令中的读并写的数据服务
/// </summary>
public const int CIP_READ_WRITE_DATA = 0x4E;
/// <summary>
/// CIP命令中的读片段的数据服务
/// </summary>
public const int CIP_READ_FRAGMENT = 0x52;
/// <summary>
/// CIP命令中的写片段的数据服务
/// </summary>
public const int CIP_WRITE_FRAGMENT = 0x53;
/// <summary>
/// CIP命令中的对数据读取服务
/// </summary>
public const int CIP_MULTIREAD_DATA = 0x1000;
#endregion
#region DataType Code
/// <summary>
/// bool型数据一个字节长度
/// </summary>
public const ushort CIP_Type_Bool = 0xC1;
/// <summary>
/// byte型数据一个字节长度
/// </summary>
public const ushort CIP_Type_Byte = 0xC2;
/// <summary>
/// 整型,两个字节长度
/// </summary>
public const ushort CIP_Type_Word = 0xC3;
/// <summary>
/// 长整型,四个字节长度
/// </summary>
public const ushort CIP_Type_DWord = 0xC4;
/// <summary>
/// 特长整型8个字节
/// </summary>
public const ushort CIP_Type_LInt = 0xC5;
/// <summary>
/// 实数数据,四个字节长度
/// </summary>
public const ushort CIP_Type_Real = 0xCA;
/// <summary>
/// 实数数据,八个字节的长度
/// </summary>
public const ushort CIP_Type_Double = 0xCB;
/// <summary>
/// 结构体数据,不定长度
/// </summary>
public const ushort CIP_Type_Struct = 0xCC;
/// <summary>
/// 字符串数据内容
/// </summary>
public const ushort CIP_Type_String = 0xD0;
/// <summary>
/// 二进制数据内容
/// </summary>
public const ushort CIP_Type_BitArray = 0xD3;
#endregion
private static byte[] BuildRequestPathCommand( string address )
{
using (MemoryStream ms = new MemoryStream( ))
{
string[] tagNames = address.Split( new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries );
for (int i = 0; i < tagNames.Length; i++)
{
string strIndex = string.Empty;
int indexFirst = tagNames[i].IndexOf( '[' );
int indexSecond = tagNames[i].IndexOf( ']' );
if (indexFirst > 0 && indexSecond > 0 && indexSecond > indexFirst)
{
strIndex = tagNames[i].Substring( indexFirst + 1, indexSecond - indexFirst - 1 );
tagNames[i] = tagNames[i].Substring( 0, indexFirst );
}
ms.WriteByte( 0x91 ); // 固定
ms.WriteByte( (byte)tagNames[i].Length ); // 节点的长度值
byte[] nameBytes = Encoding.ASCII.GetBytes( tagNames[i] );
ms.Write( nameBytes, 0, nameBytes.Length );
if (nameBytes.Length % 2 == 1) ms.WriteByte( 0x00 );
if (!string.IsNullOrEmpty( strIndex ))
{
string[] indexs = strIndex.Split( new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries );
for (int j = 0; j < indexs.Length; j++)
{
int index = Convert.ToInt32( indexs[j] );
if (index < 256)
{
ms.WriteByte( 0x28 );
ms.WriteByte( (byte)index );
}
else
{
ms.WriteByte( 0x29 );
ms.WriteByte( 0x00 );
ms.WriteByte( BitConverter.GetBytes( index )[0] );
ms.WriteByte( BitConverter.GetBytes( index )[1] );
}
}
}
}
return ms.ToArray( );
}
}
/// <summary>
/// 将CommandSpecificData的命令打包成可发送的数据指令
/// </summary>
/// <param name="command">实际的命令暗号</param>
/// <param name="session">当前会话的id</param>
/// <param name="commandSpecificData">CommandSpecificData命令</param>
/// <returns>最终可发送的数据命令</returns>
public static byte[] PackRequestHeader( ushort command, uint session, byte[] commandSpecificData )
{
byte[] buffer = new byte[commandSpecificData.Length + 24];
Array.Copy( commandSpecificData, 0, buffer, 24, commandSpecificData.Length );
BitConverter.GetBytes( command ).CopyTo( buffer, 0 );
BitConverter.GetBytes( session ).CopyTo( buffer, 4 );
BitConverter.GetBytes( (ushort)commandSpecificData.Length ).CopyTo( buffer, 2 );
return buffer;
}
/// <summary>
/// 打包生成一个请求读取数据的节点信息CIP指令信息
/// </summary>
/// <param name="address">地址</param>
/// <param name="length">指代数组的长度</param>
/// <returns>CIP的指令信息</returns>
public static byte[] PackRequsetRead( string address, int length )
{
byte[] buffer = new byte[1024];
int offect = 0;
buffer[offect++] = CIP_READ_DATA;
offect++;
byte[] requestPath = BuildRequestPathCommand( address );
requestPath.CopyTo( buffer, offect );
offect += requestPath.Length;
buffer[1] = (byte)((offect - 2) / 2);
buffer[offect++] = BitConverter.GetBytes( length )[0];
buffer[offect++] = BitConverter.GetBytes( length )[1];
byte[] data = new byte[offect];
Array.Copy( buffer, 0, data, 0, offect );
return data;
}
/// <summary>
/// 打包生成一个请求读取数据片段的节点信息CIP指令信息
/// </summary>
/// <param name="address">节点的名称</param>
/// <param name="startIndex">起始的索引位置</param>
/// <param name="length">读取的数据长度对于short来说最大是489长度</param>
/// <returns>CIP的指令信息</returns>
public static byte[] PackRequestReadSegment(string address, int startIndex, int length )
{
byte[] buffer = new byte[1024];
int offect = 0;
buffer[offect++] = CIP_READ_FRAGMENT;
offect++;
byte[] requestPath = BuildRequestPathCommand( address + (address.EndsWith( "]" ) ? string.Empty : $"[{startIndex}]") );
requestPath.CopyTo( buffer, offect );
offect += requestPath.Length;
buffer[1] = (byte)((offect - 2) / 2);
buffer[offect++] = BitConverter.GetBytes( length )[0];
buffer[offect++] = BitConverter.GetBytes( length )[1];
buffer[offect++] = BitConverter.GetBytes( 0 )[0];
buffer[offect++] = BitConverter.GetBytes( 0 )[1];
buffer[offect++] = BitConverter.GetBytes( 0 )[2];
buffer[offect++] = BitConverter.GetBytes( 0 )[3];
byte[] data = new byte[offect];
Array.Copy( buffer, 0, data, 0, offect );
return data;
}
/// <summary>
/// 根据指定的数据和类型,生成对应的数据
/// </summary>
/// <param name="address">地址信息</param>
/// <param name="typeCode">数据类型</param>
/// <param name="value">字节值</param>
/// <param name="length">如果节点为数组,就是数组长度</param>
/// <returns>CIP的指令信息</returns>
public static byte[] PackRequestWrite( string address, ushort typeCode, byte[] value, int length = 1 )
{
byte[] buffer = new byte[1024];
int offect = 0;
buffer[offect++] = CIP_WRITE_DATA;
offect++;
byte[] requestPath = BuildRequestPathCommand( address );
requestPath.CopyTo( buffer, offect );
offect += requestPath.Length;
buffer[1] = (byte)((offect - 2) / 2);
buffer[offect++] = BitConverter.GetBytes( typeCode )[0]; // 数据类型
buffer[offect++] = BitConverter.GetBytes( typeCode )[1];
buffer[offect++] = BitConverter.GetBytes( length )[0]; // 固定
buffer[offect++] = BitConverter.GetBytes( length )[1];
value.CopyTo( buffer, offect ); // 数值
offect += value.Length;
byte[] data = new byte[offect];
Array.Copy( buffer, 0, data, 0, offect );
return data;
}
/// <summary>
/// 生成读取直接节点数据信息的内容
/// </summary>
/// <param name="slot">PLC所在的槽号</param>
/// <param name="cips">cip指令内容</param>
/// <returns>最终的指令值</returns>
public static byte[] PackCommandSpecificData( byte slot, params byte[][] cips )
{
System.IO.MemoryStream ms = new System.IO.MemoryStream( );
ms.WriteByte( 0x00 );
ms.WriteByte( 0x00 );
ms.WriteByte( 0x00 );
ms.WriteByte( 0x00 );
ms.WriteByte( 0x01 ); // 超时
ms.WriteByte( 0x00 );
ms.WriteByte( 0x02 ); // 项数
ms.WriteByte( 0x00 );
ms.WriteByte( 0x00 ); // 连接的地址项
ms.WriteByte( 0x00 );
ms.WriteByte( 0x00 ); // 长度
ms.WriteByte( 0x00 );
ms.WriteByte( 0xB2 ); // 连接的项数
ms.WriteByte( 0x00 );
ms.WriteByte( 0x00 ); // 后面数据包的长度,等全部生成后在赋值
ms.WriteByte( 0x00 );
ms.WriteByte( 0x52 ); // 服务
ms.WriteByte( 0x02 ); // 请求路径大小
ms.WriteByte( 0x20 ); // 请求路径
ms.WriteByte( 0x06 );
ms.WriteByte( 0x24 );
ms.WriteByte( 0x01 );
ms.WriteByte( 0x0A ); // 超时时间
ms.WriteByte( 0xF0 );
ms.WriteByte( 0x00 ); // CIP指令长度
ms.WriteByte( 0x00 );
int count = 0;
if (cips.Length == 1)
{
ms.Write( cips[0], 0, cips[0].Length );
count += cips[0].Length;
}
else
{
ms.WriteByte( 0x0A ); // 固定
ms.WriteByte( 0x02 );
ms.WriteByte( 0x20 );
ms.WriteByte( 0x02 );
ms.WriteByte( 0x24 );
ms.WriteByte( 0x01 );
count += 8;
ms.Write( BitConverter.GetBytes( (ushort)cips.Length ), 0, 2 ); // 写入项数
ushort offect = (ushort)(0x02 + 2 * cips.Length);
count += 2 * cips.Length;
for (int i = 0; i < cips.Length; i++)
{
ms.Write( BitConverter.GetBytes( offect ), 0, 2 );
offect = (ushort)(offect + cips[i].Length);
}
for (int i = 0; i < cips.Length; i++)
{
ms.Write( cips[i], 0, cips[i].Length );
count += cips[i].Length;
}
}
ms.WriteByte( 0x01 ); // Path Size
ms.WriteByte( 0x00 );
ms.WriteByte( 0x01 ); // port
ms.WriteByte( slot );
byte[] data = ms.ToArray( );
ms.Dispose( );
BitConverter.GetBytes( (short)count ).CopyTo( data, 24 );
data[14] = BitConverter.GetBytes( (short)(data.Length - 16) )[0];
data[15] = BitConverter.GetBytes( (short)(data.Length - 16) )[1];
return data;
}
/// <summary>
/// 从PLC反馈的数据解析
/// </summary>
/// <param name="response">PLC的反馈数据</param>
/// <param name="isRead">是否是返回的操作</param>
/// <returns>带有结果标识的最终数据</returns>
public static OperateResult<byte[]> ExtractActualData( byte[] response, bool isRead )
{
List<byte> data = new List<byte>( );
int offect = 38;
ushort count = BitConverter.ToUInt16( response, 38 ); // 剩余总字节长度,在剩余的字节里,有可能是一项数据,也有可能是多项
if (BitConverter.ToInt32( response, 40 ) == 0x8A)
{
// 多项数据
offect = 44;
int dataCount = BitConverter.ToUInt16( response, offect );
for (int i = 0; i < dataCount; i++)
{
int offectStart = BitConverter.ToUInt16( response, offect + 2 + i * 2 ) + offect;
int offectEnd = (i == dataCount - 1) ? response.Length : (BitConverter.ToUInt16( response, (offect + 4 + i * 2) ) + offect);
ushort err = BitConverter.ToUInt16( response, offectStart + 2 );
switch (err)
{
case 0x04: return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.AllenBradley04 };
case 0x05: return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.AllenBradley05 };
case 0x06:
{
// 06的错误码通常是数据长度太多了
// CC是符号返回D2是符号片段返回
if (response[offect + 2] == 0xD2 || response[offect + 2] == 0xCC)
return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.AllenBradley06 };
break;
}
case 0x0A: return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.AllenBradley0A };
case 0x13: return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.AllenBradley13 };
case 0x1C: return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.AllenBradley1C };
case 0x1E: return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.AllenBradley1E };
case 0x26: return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.AllenBradley26 };
case 0x00: break;
default: return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.UnknownError };
}
if (isRead)
{
for (int j = offectStart + 6; j < offectEnd; j++)
{
data.Add( response[j] );
}
}
}
}
else
{
// 单项数据
byte err = response[offect + 4];
switch (err)
{
case 0x04: return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.AllenBradley04 };
case 0x05: return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.AllenBradley05 };
case 0x06:
{
if (response[offect + 2] == 0xD2 || response[offect + 2] == 0xCC)
return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.AllenBradley06 };
break;
}
case 0x0A: return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.AllenBradley0A };
case 0x13: return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.AllenBradley13 };
case 0x1C: return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.AllenBradley1C };
case 0x1E: return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.AllenBradley1E };
case 0x26: return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.AllenBradley26 };
case 0x00: break;
default: return new OperateResult<byte[]>( ) { ErrorCode = err, Message = StringResources.Language.UnknownError };
}
if (isRead)
{
for (int i = offect + 8; i < offect + 2 + count; i++)
{
data.Add( response[i] );
}
}
}
return OperateResult.CreateSuccessResult( data.ToArray( ) );
}
}
}