using HslCommunication.BasicFramework;
using HslCommunication.Core;
using HslCommunication.Serial;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace HslCommunication.Profinet.Fuji
{
///
/// 富士PLC的SPB协议
///
public class FujiSPB : SerialDeviceBase
{
#region Constructor
///
/// 使用默认的构造方法实例化对象
///
public FujiSPB( )
{
WordLength = 1;
}
#endregion
#region Public Member
///
/// PLC的站号信息
///
public byte Station { get => station; set => station = value; }
#endregion
#region Read Write Support
///
/// 批量读取PLC的数据,以字为单位,支持读取X,Y,L,M,D,TN,CN,TC,CC,R具体的地址范围需要根据PLC型号来确认
///
/// 地址信息
/// 数据长度
/// 读取结果信息
public override OperateResult Read( string address, ushort length )
{
// 解析指令
OperateResult command = BuildReadCommand( this.station, address, length, false );
if (!command.IsSuccess) return OperateResult.CreateFailedResult( command );
// 核心交互
OperateResult read = ReadBase( command.Content );
if (!read.IsSuccess) return OperateResult.CreateFailedResult( read );
// 结果验证
if (read.Content[0] != ':') return new OperateResult( read.Content[0], "Read Faild:" + BasicFramework.SoftBasic.ByteToHexString( read.Content, ' ' ) );
if (Encoding.ASCII.GetString(read.Content, 9, 2) != "00") return new OperateResult( read.Content[5], GetErrorDescriptionFromCode( Encoding.ASCII.GetString( read.Content, 9, 2 ) ) );
// 提取结果
byte[] Content = new byte[length * 2];
for (int i = 0; i < Content.Length / 2; i++)
{
ushort tmp = Convert.ToUInt16( Encoding.ASCII.GetString( read.Content, i * 4 + 6, 4 ), 16 );
BitConverter.GetBytes( tmp ).CopyTo( Content, i * 2 );
}
return OperateResult.CreateSuccessResult( Content );
}
///
/// 批量写入PLC的数据,以字为单位,也就是说最少2个字节信息,支持读取X,Y,L,M,D,TN,CN,TC,CC,R具体的地址范围需要根据PLC型号来确认
///
/// 地址信息,举例,D100,R200,RC100,RT200
/// 数据值
/// 是否写入成功
public override OperateResult Write( string address, byte[] value )
{
// 解析指令
OperateResult command = BuildWriteByteCommand( this.station, address, value );
if (!command.IsSuccess) return command;
// 核心交互
OperateResult read = ReadBase( command.Content );
if (!read.IsSuccess) return read;
// 结果验证
if (read.Content[0] != ':') return new OperateResult( read.Content[0], "Read Faild:" + BasicFramework.SoftBasic.ByteToHexString( read.Content, ' ' ) );
if (Encoding.ASCII.GetString( read.Content, 9, 2 ) != "00") return new OperateResult( read.Content[5], GetErrorDescriptionFromCode( Encoding.ASCII.GetString( read.Content, 9, 2 ) ) );
// 提取结果
return OperateResult.CreateSuccessResult( );
}
#endregion
#region Private Member
private byte station = 0x01; // PLC的站号信息
#endregion
#region Object Override
///
/// 返回表示当前对象的字符串
///
/// 字符串
public override string ToString( )
{
return $"FujiSPB[{PortName}:{BaudRate}]";
}
#endregion
#region Static Helper
private static string AnalysisIntegerAddress( int address )
{
string tmp = address.ToString( "D4" );
return tmp.Substring( 2 ) + tmp.Substring( 0, 2 );
}
///
/// 解析数据地址成不同的三菱地址类型
///
/// 数据地址
/// 地址结果对象
private static OperateResult FujikAnalysisAddress( string address )
{
var result = new OperateResult( );
try
{
switch (address[0])
{
case 'X':
case 'x':
{
result.Content = "01" + AnalysisIntegerAddress( Convert.ToUInt16( address.Substring( 1 ), 10 ) );
break;
}
case 'Y':
case 'y':
{
result.Content = "00" + AnalysisIntegerAddress( Convert.ToUInt16( address.Substring( 1 ), 10 ) );
break;
}
case 'M':
case 'm':
{
result.Content = "02" + AnalysisIntegerAddress( Convert.ToUInt16( address.Substring( 1 ), 10 ) );
break;
}
case 'L':
case 'l':
{
result.Content = "03" + AnalysisIntegerAddress( Convert.ToUInt16( address.Substring( 1 ), 10 ) );
break;
}
case 'T':
case 't':
{
if (address[1] == 'N' || address[1] == 'n')
{
result.Content = "0A" + AnalysisIntegerAddress( Convert.ToUInt16( address.Substring( 1 ), 10 ) );
break;
}
else if (address[1] == 'C' || address[1] == 'c')
{
result.Content = "04" + AnalysisIntegerAddress( Convert.ToUInt16( address.Substring( 1 ), 10 ) );
break;
}
else
{
throw new Exception( StringResources.Language.NotSupportedDataType );
}
}
case 'C':
case 'c':
{
if (address[1] == 'N' || address[1] == 'n')
{
result.Content = "0B" + AnalysisIntegerAddress( Convert.ToUInt16( address.Substring( 1 ), 10 ) );
break;
}
else if (address[1] == 'C' || address[1] == 'c')
{
result.Content = "05" + AnalysisIntegerAddress( Convert.ToUInt16( address.Substring( 1 ), 10 ) );
break;
}
else
{
throw new Exception( StringResources.Language.NotSupportedDataType );
}
}
case 'D':
case 'd':
{
result.Content = "0C" + AnalysisIntegerAddress( Convert.ToUInt16( address.Substring( 1 ), 10 ) );
break;
}
case 'R':
case 'r':
{
result.Content = "0D" + AnalysisIntegerAddress( Convert.ToUInt16( address.Substring( 1 ), 10 ) );
break;
}
default: throw new Exception( StringResources.Language.NotSupportedDataType );
}
}
catch (Exception ex)
{
result.Message = ex.Message;
return result;
}
result.IsSuccess = true;
return result;
}
///
/// 计算指令的和校验码
///
/// 指令
/// 校验之后的信息
public static string CalculateAcc( string data )
{
byte[] buffer = Encoding.ASCII.GetBytes( data );
int count = 0;
for (int i = 0; i < buffer.Length; i++)
{
count += buffer[i];
}
return count.ToString( "X4" ).Substring( 2 );
}
///
/// 创建一条读取的指令信息,需要指定一些参数
///
/// PLCd的站号
/// 地址信息
/// 数据长度
/// 是否位读取
/// 是否成功的结果对象
public static OperateResult BuildReadCommand( byte station, string address, ushort length, bool isBool )
{
OperateResult addressAnalysis = FujikAnalysisAddress( address );
if (!addressAnalysis.IsSuccess) return OperateResult.CreateFailedResult( addressAnalysis );
StringBuilder stringBuilder = new StringBuilder( );
stringBuilder.Append( ':' );
stringBuilder.Append( station.ToString( "X2" ) );
stringBuilder.Append( "09" );
stringBuilder.Append( "FFFF" );
stringBuilder.Append( "00" );
stringBuilder.Append( "00" );
stringBuilder.Append( addressAnalysis.Content );
stringBuilder.Append( length.ToString( "D4" ) );
stringBuilder.Append( "\r\n" );
return OperateResult.CreateSuccessResult( Encoding.ASCII.GetBytes( stringBuilder.ToString( ) ) );
}
///
/// 创建一条别入byte数据的指令信息,需要指定一些参数,按照字单位
///
/// 站号
/// 地址
/// 数组值
/// 是否创建成功
public static OperateResult BuildWriteByteCommand( byte station, string address, byte[] value )
{
OperateResult addressAnalysis = FujikAnalysisAddress( address );
if (!addressAnalysis.IsSuccess) return OperateResult.CreateFailedResult( addressAnalysis );
StringBuilder stringBuilder = new StringBuilder( );
stringBuilder.Append( ':' );
stringBuilder.Append( station.ToString( "X2" ) );
stringBuilder.Append( (9 + value.Length / 2).ToString( "X2" ) );
stringBuilder.Append( "FFFF" );
stringBuilder.Append( "01" );
stringBuilder.Append( "00" );
stringBuilder.Append( addressAnalysis.Content );
stringBuilder.Append( (value.Length / 2).ToString( "D4" ) );
byte[] buffer = new byte[value.Length * 2];
for (int i = 0; i < value.Length / 2; i++)
{
SoftBasic.BuildAsciiBytesFrom( BitConverter.ToUInt16( value, i * 2 ) ).CopyTo( buffer, 4 * i );
}
stringBuilder.Append( Encoding.ASCII.GetString( buffer ) );
stringBuilder.Append( "\r\n" );
return OperateResult.CreateSuccessResult( Encoding.ASCII.GetBytes( stringBuilder.ToString( ) ) );
}
///
/// 根据错误码获取到真实的文本信息
///
/// 错误码
/// 错误的文本描述
public static string GetErrorDescriptionFromCode( string code )
{
switch (code)
{
case "01": return StringResources.Language.FujiSpbStatus01;
case "02": return StringResources.Language.FujiSpbStatus02;
case "03": return StringResources.Language.FujiSpbStatus03;
case "04": return StringResources.Language.FujiSpbStatus04;
case "05": return StringResources.Language.FujiSpbStatus05;
case "06": return StringResources.Language.FujiSpbStatus06;
case "07": return StringResources.Language.FujiSpbStatus07;
case "09": return StringResources.Language.FujiSpbStatus09;
case "0C": return StringResources.Language.FujiSpbStatus0C;
default: return StringResources.Language.UnknownError;
}
}
#endregion
}
}