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 } }