using HslCommunication.Core;
using HslCommunication.Core.Address;
using HslCommunication.Serial;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HslCommunication.BasicFramework;
namespace HslCommunication.Profinet.Melsec
{
///
/// 基于Qna 兼容3C帧的格式一的通讯,具体的地址需要参照三菱的基本地址
///
///
/// 地址的输入的格式说明如下:
///
///
/// 地址名称
/// 地址代号
/// 示例
/// 地址进制
/// 字操作
/// 位操作
/// 备注
///
/// -
/// 内部继电器
/// M
/// M100,M200
/// 10
/// √
/// √
///
///
/// -
/// 输入继电器
/// X
/// X100,X1A0
/// 16
/// √
/// √
///
///
/// -
/// 输出继电器
/// Y
/// Y100,Y1A0
/// 16
/// √
/// √
///
///
/// -
/// 锁存继电器
/// L
/// L100,L200
/// 10
/// √
/// √
///
///
/// -
/// 报警器
/// F
/// F100,F200
/// 10
/// √
/// √
///
///
/// -
/// 边沿继电器
/// V
/// V100,V200
/// 10
/// √
/// √
///
///
/// -
/// 链接继电器
/// B
/// B100,B1A0
/// 16
/// √
/// √
///
///
/// -
/// 步进继电器
/// S
/// S100,S200
/// 10
/// √
/// √
///
///
/// -
/// 数据寄存器
/// D
/// D1000,D2000
/// 10
/// √
/// ×
///
///
/// -
/// 链接寄存器
/// W
/// W100,W1A0
/// 16
/// √
/// ×
///
///
/// -
/// 文件寄存器
/// R
/// R100,R200
/// 10
/// √
/// ×
///
///
/// -
/// ZR文件寄存器
/// ZR
/// ZR100,ZR2A0
/// 16
/// √
/// ×
///
///
/// -
/// 变址寄存器
/// Z
/// Z100,Z200
/// 10
/// √
/// ×
///
///
/// -
/// 定时器的触点
/// TS
/// TS100,TS200
/// 10
/// √
/// √
///
///
/// -
/// 定时器的线圈
/// TC
/// TC100,TC200
/// 10
/// √
/// √
///
///
/// -
/// 定时器的当前值
/// TN
/// TN100,TN200
/// 10
/// √
/// ×
///
///
/// -
/// 累计定时器的触点
/// SS
/// SS100,SS200
/// 10
/// √
/// √
///
///
/// -
/// 累计定时器的线圈
/// SC
/// SC100,SC200
/// 10
/// √
/// √
///
///
/// -
/// 累计定时器的当前值
/// SN
/// SN100,SN200
/// 10
/// √
/// ×
///
///
/// -
/// 计数器的触点
/// CS
/// CS100,CS200
/// 10
/// √
/// √
///
///
/// -
/// 计数器的线圈
/// CC
/// CC100,CC200
/// 10
/// √
/// √
///
///
/// -
/// 计数器的当前值
/// CN
/// CN100,CN200
/// 10
/// √
/// ×
///
///
///
///
public class MelsecA3CNet1 : SerialDeviceBase
{
#region Constructor
///
/// 实例化默认的构造方法
///
public MelsecA3CNet1( )
{
WordLength = 1;
}
#endregion
#region Public Member
///
/// PLC的站号信息
///
public byte Station { get => station; set => station = value; }
#endregion
#region Read Write Support
///
/// 批量读取PLC的数据,以字为单位,支持读取X,Y,M,S,D,T,C,具体的地址范围需要根据PLC型号来确认
///
/// 地址信息
/// 数据长度
/// 读取结果信息
public override OperateResult Read( string address, ushort length )
{
// 分析地址
OperateResult addressResult = McAddressData.ParseMelsecFrom( address, length );
if (!addressResult.IsSuccess) return OperateResult.CreateFailedResult( addressResult );
// 解析指令
byte[] command = MelsecHelper.BuildAsciiReadMcCoreCommand( addressResult.Content, false );
// 核心交互
OperateResult read = ReadBase( PackCommand( command, this.station ) );
if (!read.IsSuccess) return read;
// 结果验证
if (read.Content[0] != 0x02) return new OperateResult( read.Content[0], "Read Faild:" + Encoding.ASCII.GetString( read.Content, 1, read.Content.Length - 1 ) );
// 提取结果
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 + 11, 4 ), 16 );
BitConverter.GetBytes( tmp ).CopyTo( Content, i * 2 );
}
return OperateResult.CreateSuccessResult( Content );
}
///
/// 批量写入PLC的数据,以字为单位,也就是说最少2个字节信息,支持X,Y,M,S,D,T,C,具体的地址范围需要根据PLC型号来确认
///
/// 地址信息
/// 数据值
/// 是否写入成功
public override OperateResult Write( string address, byte[] value )
{
// 分析地址
OperateResult addressResult = McAddressData.ParseMelsecFrom( address, 0 );
if (!addressResult.IsSuccess) return OperateResult.CreateFailedResult( addressResult );
// 解析指令
byte[] command = MelsecHelper.BuildAsciiWriteWordCoreCommand( addressResult.Content, value );
// 核心交互
OperateResult read = ReadBase( PackCommand( command, this.station ) );
if (!read.IsSuccess) return read;
// 结果验证
if (read.Content[0] != 0x06) return new OperateResult( read.Content[0], "Write Faild:" + Encoding.ASCII.GetString( read.Content, 1, read.Content.Length - 1 ) );
// 提取结果
return OperateResult.CreateSuccessResult( );
}
#endregion
#region Bool Read Write
///
/// 批量读取bool类型数据,支持的类型为X,Y,S,T,C,具体的地址范围取决于PLC的类型
///
/// 地址信息,比如X10,Y17,注意X,Y的地址是8进制的
/// 读取的长度
/// 读取结果信息
public OperateResult ReadBool( string address, ushort length )
{
// 分析地址
OperateResult addressResult = McAddressData.ParseMelsecFrom( address, length );
if (!addressResult.IsSuccess) return OperateResult.CreateFailedResult( addressResult );
// 解析指令
byte[] command = MelsecHelper.BuildAsciiReadMcCoreCommand( addressResult.Content, true );
// 核心交互
OperateResult read = ReadBase( PackCommand( command, this.station ) );
if (!read.IsSuccess) return OperateResult.CreateFailedResult( read );
// 结果验证
if (read.Content[0] != 0x02) return new OperateResult( read.Content[0], "Read Faild:" + Encoding.ASCII.GetString( read.Content, 1, read.Content.Length - 1 ) );
// 提取结果
byte[] buffer = new byte[length];
Array.Copy( read.Content, 11, buffer, 0, length );
return OperateResult.CreateSuccessResult( buffer.Select( m => m == 0x31 ).ToArray( ) );
}
///
/// 批量读取bool类型数据,支持的类型为X,Y,S,T,C,具体的地址范围取决于PLC的类型
///
/// 地址信息,比如X10,Y17,注意X,Y的地址是8进制的
/// 读取结果信息
public OperateResult ReadBool( string address )
{
OperateResult read = ReadBool( address, 1 );
if (!read.IsSuccess) return OperateResult.CreateFailedResult( read );
return OperateResult.CreateSuccessResult( read.Content[0] );
}
///
/// 批量写入bool类型的数值,支持的类型为X,Y,S,T,C,具体的地址范围取决于PLC的类型
///
/// PLC的地址信息
/// 数据信息
/// 是否写入成功
public OperateResult Write( string address, bool value )
{
return Write( address, new bool[] { value } );
}
///
/// 批量写入bool类型的数组,支持的类型为X,Y,S,T,C,具体的地址范围取决于PLC的类型
///
/// PLC的地址信息
/// 数据信息
/// 是否写入成功
public OperateResult Write( string address, bool[] value )
{
// 分析地址
OperateResult addressResult = McAddressData.ParseMelsecFrom( address, 0 );
if (!addressResult.IsSuccess) return OperateResult.CreateFailedResult( addressResult );
// 解析指令
byte[] command = MelsecHelper.BuildAsciiWriteBitCoreCommand( addressResult.Content, value );
// 核心交互
OperateResult read = ReadBase( PackCommand( command, this.station ) );
if (!read.IsSuccess) return read;
// 结果验证
if (read.Content[0] != 0x06) return new OperateResult( read.Content[0], "Write Faild:" + Encoding.ASCII.GetString( read.Content, 1, read.Content.Length - 1 ) );
// 提取结果
return OperateResult.CreateSuccessResult( );
}
#endregion
#region Remote Operate
///
/// 远程Run操作
///
/// 是否成功
public OperateResult RemoteRun( )
{
// 核心交互
OperateResult read = ReadBase( PackCommand( Encoding.ASCII.GetBytes( "1001000000010000" ), this.station ) );
if (!read.IsSuccess) return read;
// 结果验证
if (read.Content[0] != 0x06 && read.Content[0] != 0x02) return new OperateResult( read.Content[0], "Faild:" + Encoding.ASCII.GetString( read.Content, 1, read.Content.Length - 1 ) );
// 成功
return OperateResult.CreateSuccessResult( );
}
///
/// 远程Stop操作
///
/// 是否成功
public OperateResult RemoteStop( )
{
// 核心交互
OperateResult read = ReadBase( PackCommand( Encoding.ASCII.GetBytes( "100200000001" ), this.station ) );
if (!read.IsSuccess) return read;
// 结果验证
if (read.Content[0] != 0x06 && read.Content[0] != 0x02) return new OperateResult( read.Content[0], "Faild:" + Encoding.ASCII.GetString( read.Content, 1, read.Content.Length - 1 ) );
// 成功
return OperateResult.CreateSuccessResult( );
}
///
/// 读取PLC的型号信息
///
/// 返回型号的结果对象
public OperateResult ReadPlcType( )
{
// 核心交互
OperateResult read = ReadBase( PackCommand( Encoding.ASCII.GetBytes( "01010000" ), this.station ) );
if (!read.IsSuccess) return OperateResult.CreateFailedResult( read );
// 结果验证
if (read.Content[0] != 0x06 && read.Content[0] != 0x02) return new OperateResult( read.Content[0], "Faild:" + Encoding.ASCII.GetString( read.Content, 1, read.Content.Length - 1 ) );
// 成功
return OperateResult.CreateSuccessResult( Encoding.ASCII.GetString( read.Content, 11, 16 ).TrimEnd( ) );
}
#endregion
#region Object Override
///
/// 返回表示当前对象的字符串
///
/// 字符串信息
public override string ToString()
{
return $"MelsecA3CNet1";
}
#endregion
#region Private Member
private byte station = 0x00; // PLC的站号信息
#endregion
#region Static Helper
///
/// 将命令进行打包传送
///
/// mc协议的命令
/// PLC的站号
/// 最终的原始报文信息
public static byte[] PackCommand( byte[] mcCommand, byte station = 0)
{
byte[] command = new byte[13 + mcCommand.Length];
command[0] = 0x05;
command[1] = 0x46;
command[2] = 0x39;
command[3] = SoftBasic.BuildAsciiBytesFrom( station )[0];
command[4] = SoftBasic.BuildAsciiBytesFrom( station )[1];
command[5] = 0x30;
command[6] = 0x30;
command[7] = 0x46;
command[8] = 0x46;
command[9] = 0x30;
command[10] = 0x30;
mcCommand.CopyTo( command, 11 );
// 计算和校验
int sum = 0;
for (int i = 1; i < command.Length - 3; i++)
{
sum += command[i];
}
command[command.Length - 2] = SoftBasic.BuildAsciiBytesFrom( (byte)sum )[0];
command[command.Length - 1] = SoftBasic.BuildAsciiBytesFrom( (byte)sum )[1];
return command;
}
#endregion
}
}