|
|
using HslCommunication.BasicFramework;
|
|
|
using HslCommunication.Core;
|
|
|
using HslCommunication.Core.Address;
|
|
|
using HslCommunication.Core.IMessage;
|
|
|
using HslCommunication.Core.Net;
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
using System.Linq;
|
|
|
using System.Text;
|
|
|
|
|
|
namespace HslCommunication.Profinet.Melsec
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// 三菱PLC通讯类,采用Qna兼容3E帧协议实现,需要在PLC侧先的以太网模块先进行配置,必须为ASCII通讯格式
|
|
|
/// </summary>
|
|
|
/// <remarks>
|
|
|
/// 地址的输入的格式说明如下:
|
|
|
/// <list type="table">
|
|
|
/// <listheader>
|
|
|
/// <term>地址名称</term>
|
|
|
/// <term>地址代号</term>
|
|
|
/// <term>示例</term>
|
|
|
/// <term>地址进制</term>
|
|
|
/// <term>字操作</term>
|
|
|
/// <term>位操作</term>
|
|
|
/// <term>备注</term>
|
|
|
/// </listheader>
|
|
|
/// <item>
|
|
|
/// <term>内部继电器</term>
|
|
|
/// <term>M</term>
|
|
|
/// <term>M100,M200</term>
|
|
|
/// <term>10</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>输入继电器</term>
|
|
|
/// <term>X</term>
|
|
|
/// <term>X100,X1A0</term>
|
|
|
/// <term>16</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>输出继电器</term>
|
|
|
/// <term>Y</term>
|
|
|
/// <term>Y100,Y1A0</term>
|
|
|
/// <term>16</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>锁存继电器</term>
|
|
|
/// <term>L</term>
|
|
|
/// <term>L100,L200</term>
|
|
|
/// <term>10</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>报警器</term>
|
|
|
/// <term>F</term>
|
|
|
/// <term>F100,F200</term>
|
|
|
/// <term>10</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>边沿继电器</term>
|
|
|
/// <term>V</term>
|
|
|
/// <term>V100,V200</term>
|
|
|
/// <term>10</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>链接继电器</term>
|
|
|
/// <term>B</term>
|
|
|
/// <term>B100,B1A0</term>
|
|
|
/// <term>16</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>步进继电器</term>
|
|
|
/// <term>S</term>
|
|
|
/// <term>S100,S200</term>
|
|
|
/// <term>10</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>数据寄存器</term>
|
|
|
/// <term>D</term>
|
|
|
/// <term>D1000,D2000</term>
|
|
|
/// <term>10</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>×</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>链接寄存器</term>
|
|
|
/// <term>W</term>
|
|
|
/// <term>W100,W1A0</term>
|
|
|
/// <term>16</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>×</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>文件寄存器</term>
|
|
|
/// <term>R</term>
|
|
|
/// <term>R100,R200</term>
|
|
|
/// <term>10</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>×</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>ZR文件寄存器</term>
|
|
|
/// <term>ZR</term>
|
|
|
/// <term>ZR100,ZR2A0</term>
|
|
|
/// <term>16</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>×</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>变址寄存器</term>
|
|
|
/// <term>Z</term>
|
|
|
/// <term>Z100,Z200</term>
|
|
|
/// <term>10</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>×</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>定时器的触点</term>
|
|
|
/// <term>TS</term>
|
|
|
/// <term>TS100,TS200</term>
|
|
|
/// <term>10</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>定时器的线圈</term>
|
|
|
/// <term>TC</term>
|
|
|
/// <term>TC100,TC200</term>
|
|
|
/// <term>10</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>定时器的当前值</term>
|
|
|
/// <term>TN</term>
|
|
|
/// <term>TN100,TN200</term>
|
|
|
/// <term>10</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>×</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>累计定时器的触点</term>
|
|
|
/// <term>SS</term>
|
|
|
/// <term>SS100,SS200</term>
|
|
|
/// <term>10</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>累计定时器的线圈</term>
|
|
|
/// <term>SC</term>
|
|
|
/// <term>SC100,SC200</term>
|
|
|
/// <term>10</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>累计定时器的当前值</term>
|
|
|
/// <term>SN</term>
|
|
|
/// <term>SN100,SN200</term>
|
|
|
/// <term>10</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>×</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>计数器的触点</term>
|
|
|
/// <term>CS</term>
|
|
|
/// <term>CS100,CS200</term>
|
|
|
/// <term>10</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>计数器的线圈</term>
|
|
|
/// <term>CC</term>
|
|
|
/// <term>CC100,CC200</term>
|
|
|
/// <term>10</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// <item>
|
|
|
/// <term>计数器的当前值</term>
|
|
|
/// <term>CN</term>
|
|
|
/// <term>CN100,CN200</term>
|
|
|
/// <term>10</term>
|
|
|
/// <term>√</term>
|
|
|
/// <term>×</term>
|
|
|
/// <term></term>
|
|
|
/// </item>
|
|
|
/// </list>
|
|
|
/// </remarks>
|
|
|
/// <example>
|
|
|
/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Profinet\MelsecAscii.cs" region="Usage" title="简单的短连接使用" />
|
|
|
/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Profinet\MelsecAscii.cs" region="Usage2" title="简单的长连接使用" />
|
|
|
/// </example>
|
|
|
public class MelsecMcAsciiNet : NetworkDeviceBase<MelsecQnA3EAsciiMessage, RegularByteTransform>
|
|
|
{
|
|
|
#region Constructor
|
|
|
|
|
|
/// <summary>
|
|
|
/// 实例化三菱的Qna兼容3E帧协议的通讯对象
|
|
|
/// </summary>
|
|
|
public MelsecMcAsciiNet( )
|
|
|
{
|
|
|
WordLength = 1;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 实例化一个三菱的Qna兼容3E帧协议的通讯对象
|
|
|
/// </summary>
|
|
|
/// <param name="ipAddress">PLC的Ip地址</param>
|
|
|
/// <param name="port">PLC的端口</param>
|
|
|
public MelsecMcAsciiNet( string ipAddress, int port )
|
|
|
{
|
|
|
WordLength = 1;
|
|
|
IpAddress = ipAddress;
|
|
|
Port = port;
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Public Member
|
|
|
|
|
|
/// <summary>
|
|
|
/// 网络号
|
|
|
/// </summary>
|
|
|
public byte NetworkNumber { get; set; } = 0x00;
|
|
|
|
|
|
/// <summary>
|
|
|
/// 网络站号
|
|
|
/// </summary>
|
|
|
public byte NetworkStationNumber { get; set; } = 0x00;
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Address Analysis
|
|
|
|
|
|
/// <summary>
|
|
|
/// 分析地址的方法,允许派生类里进行重写操作
|
|
|
/// </summary>
|
|
|
/// <param name="address">地址信息</param>
|
|
|
/// <param name="length">数据长度</param>
|
|
|
/// <returns>解析后的数据信息</returns>
|
|
|
protected virtual OperateResult<McAddressData> McAnalysisAddress( string address, ushort length )
|
|
|
{
|
|
|
return McAddressData.ParseMelsecFrom( address, length );
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Read Write Override
|
|
|
|
|
|
/// <summary>
|
|
|
/// 从三菱PLC中读取想要的数据,返回读取结果,读取的单位为字
|
|
|
/// </summary>
|
|
|
/// <param name="address">读取地址,格式为"M100","D100","W1A0"</param>
|
|
|
/// <param name="length">读取的数据长度,字最大值960,位最大值7168</param>
|
|
|
/// <returns>带成功标志的结果数据对象</returns>
|
|
|
/// <remarks>
|
|
|
/// 地址支持的列表参考 <seealso cref="MelsecMcAsciiNet"/> 的备注说明
|
|
|
/// </remarks>
|
|
|
/// <example>
|
|
|
/// 假设起始地址为D100,D100存储了温度,100.6℃值为1006,D101存储了压力,1.23Mpa值为123,D102,D103存储了产量计数,读取如下:
|
|
|
/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Profinet\MelsecAscii.cs" region="ReadExample2" title="Read示例" />
|
|
|
/// 以下是读取不同类型数据的示例
|
|
|
/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Profinet\MelsecAscii.cs" region="ReadExample1" title="Read示例" />
|
|
|
/// </example>
|
|
|
public override OperateResult<byte[]> Read( string address, ushort length )
|
|
|
{
|
|
|
// 分析地址
|
|
|
OperateResult<McAddressData> addressResult = McAnalysisAddress( address, length );
|
|
|
if (!addressResult.IsSuccess) return OperateResult.CreateFailedResult<byte[]>( addressResult );
|
|
|
|
|
|
List<byte> bytesContent = new List<byte>( );
|
|
|
ushort alreadyFinished = 0;
|
|
|
while (alreadyFinished < length)
|
|
|
{
|
|
|
ushort readLength = (ushort)Math.Min( length - alreadyFinished, 450 );
|
|
|
addressResult.Content.Length = readLength;
|
|
|
OperateResult<byte[]> read = ReadAddressData( addressResult.Content );
|
|
|
if (!read.IsSuccess) return read;
|
|
|
|
|
|
bytesContent.AddRange( read.Content );
|
|
|
alreadyFinished += readLength;
|
|
|
|
|
|
// 字的话就是正常的偏移位置,如果是位的话,就转到位的数据
|
|
|
if (addressResult.Content.McDataType.DataType == 0)
|
|
|
addressResult.Content.AddressStart += readLength;
|
|
|
else
|
|
|
addressResult.Content.AddressStart += readLength * 16;
|
|
|
}
|
|
|
return OperateResult.CreateSuccessResult( bytesContent.ToArray( ) );
|
|
|
}
|
|
|
|
|
|
private OperateResult<byte[]> ReadAddressData( McAddressData addressData )
|
|
|
{
|
|
|
// 地址分析
|
|
|
byte[] coreResult = MelsecHelper.BuildAsciiReadMcCoreCommand( addressData, false );
|
|
|
|
|
|
// 核心交互
|
|
|
var read = ReadFromCoreServer( PackMcCommand( coreResult, NetworkNumber, NetworkStationNumber ) );
|
|
|
if (!read.IsSuccess) return OperateResult.CreateFailedResult<byte[]>( read );
|
|
|
|
|
|
// 错误代码验证
|
|
|
ushort errorCode = Convert.ToUInt16( Encoding.ASCII.GetString( read.Content, 18, 4 ), 16 );
|
|
|
if (errorCode != 0) return new OperateResult<byte[]>( errorCode, StringResources.Language.MelsecPleaseReferToManulDocument );
|
|
|
|
|
|
// 数据解析,需要传入是否使用位的参数
|
|
|
return ExtractActualData( read.Content, false );
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 向PLC写入数据,数据格式为原始的字节类型
|
|
|
/// </summary>
|
|
|
/// <param name="address">初始地址</param>
|
|
|
/// <param name="value">原始的字节数据</param>
|
|
|
/// <example>
|
|
|
/// 假设起始地址为D100,D100存储了温度,100.6℃值为1006,D101存储了压力,1.23Mpa值为123,D102,D103存储了产量计数,写入如下:
|
|
|
/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Profinet\MelsecAscii.cs" region="WriteExample2" title="Write示例" />
|
|
|
/// 以下是读取不同类型数据的示例
|
|
|
/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Profinet\MelsecAscii.cs" region="WriteExample1" title="Write示例" />
|
|
|
/// </example>
|
|
|
/// <returns>结果</returns>
|
|
|
public override OperateResult Write( string address, byte[] value )
|
|
|
{
|
|
|
// 分析地址
|
|
|
OperateResult<McAddressData> addressResult = McAnalysisAddress( address, 0 );
|
|
|
if (!addressResult.IsSuccess) return OperateResult.CreateFailedResult<byte[]>( addressResult );
|
|
|
|
|
|
// 地址分析
|
|
|
byte[] coreResult = MelsecHelper.BuildAsciiWriteWordCoreCommand( addressResult.Content, value );
|
|
|
|
|
|
// 核心交互
|
|
|
OperateResult<byte[]> read = ReadFromCoreServer( PackMcCommand( coreResult, NetworkNumber, NetworkStationNumber ) );
|
|
|
if (!read.IsSuccess) return read;
|
|
|
|
|
|
// 错误码验证
|
|
|
ushort errorCode = Convert.ToUInt16( Encoding.ASCII.GetString( read.Content, 18, 4 ), 16 );
|
|
|
if (errorCode != 0) return new OperateResult<byte[]>( errorCode, StringResources.Language.MelsecPleaseReferToManulDocument );
|
|
|
|
|
|
// 写入成功
|
|
|
return OperateResult.CreateSuccessResult( );
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Bool Operate Support
|
|
|
|
|
|
/// <summary>
|
|
|
/// 从三菱PLC中批量读取位软元件,返回读取结果
|
|
|
/// </summary>
|
|
|
/// <param name="address">起始地址</param>
|
|
|
/// <param name="length">读取的长度</param>
|
|
|
/// <returns>带成功标志的结果数据对象</returns>
|
|
|
/// <remarks>
|
|
|
/// 地址支持的列表参考 <seealso cref="MelsecMcAsciiNet"/> 的备注说明
|
|
|
/// </remarks>
|
|
|
/// <example>
|
|
|
/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Profinet\MelsecAscii.cs" region="ReadBool" title="Bool类型示例" />
|
|
|
/// </example>
|
|
|
public virtual OperateResult<bool[]> ReadBool( string address, ushort length )
|
|
|
{
|
|
|
// 分析地址
|
|
|
OperateResult<McAddressData> addressResult = McAnalysisAddress( address, length );
|
|
|
if (!addressResult.IsSuccess) return OperateResult.CreateFailedResult<bool[]>( addressResult );
|
|
|
|
|
|
// 地址分析
|
|
|
byte[] coreResult = MelsecHelper.BuildAsciiReadMcCoreCommand( addressResult.Content, true );
|
|
|
|
|
|
// 核心交互
|
|
|
var read = ReadFromCoreServer( PackMcCommand( coreResult, NetworkNumber, NetworkStationNumber ) );
|
|
|
if (!read.IsSuccess) return OperateResult.CreateFailedResult<bool[]>( read );
|
|
|
|
|
|
// 错误代码验证
|
|
|
ushort errorCode = Convert.ToUInt16( Encoding.ASCII.GetString( read.Content, 18, 4 ), 16 );
|
|
|
if (errorCode != 0) return new OperateResult<bool[]>( errorCode, StringResources.Language.MelsecPleaseReferToManulDocument );
|
|
|
|
|
|
// 数据解析,需要传入是否使用位的参数
|
|
|
var extract = ExtractActualData( read.Content, true );
|
|
|
if(!extract.IsSuccess) return OperateResult.CreateFailedResult<bool[]>( extract );
|
|
|
|
|
|
// 转化bool数组
|
|
|
return OperateResult.CreateSuccessResult( extract.Content.Select( m => m == 0x01 ).Take( length ).ToArray( ) );
|
|
|
}
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// 从三菱PLC中批量读取位软元件,返回读取结果
|
|
|
/// </summary>
|
|
|
/// <param name="address">起始地址</param>
|
|
|
/// <returns>带成功标志的结果数据对象</returns>
|
|
|
/// <example>参照 <see cref="ReadBool(string, ushort)"/> 方法 </example>
|
|
|
public OperateResult<bool> ReadBool( string address )
|
|
|
{
|
|
|
OperateResult<bool[]> read = ReadBool( address, 1 );
|
|
|
if (!read.IsSuccess) return OperateResult.CreateFailedResult<bool>( read );
|
|
|
|
|
|
return OperateResult.CreateSuccessResult<bool>( read.Content[0] );
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 向PLC中位软元件写入bool数组,返回值说明,比如你写入M100,values[0]对应M100
|
|
|
/// </summary>
|
|
|
/// <param name="address">要写入的数据地址</param>
|
|
|
/// <param name="value">要写入的实际数据,长度为8的倍数</param>
|
|
|
/// <example>
|
|
|
/// 详细请查看<see cref="Write(string, bool[])"/>方法的示例
|
|
|
/// </example>
|
|
|
/// <returns>返回写入结果</returns>
|
|
|
public OperateResult Write( string address, bool value )
|
|
|
{
|
|
|
return Write( address, new bool[] { value } );
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 向PLC中位软元件写入bool数组,返回值说明,比如你写入M100,values[0]对应M100
|
|
|
/// </summary>
|
|
|
/// <param name="address">要写入的数据地址</param>
|
|
|
/// <param name="values">要写入的实际数据,可以指定任意的长度</param>
|
|
|
/// <example>
|
|
|
/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Profinet\MelsecAscii.cs" region="WriteBool" title="Write示例" />
|
|
|
/// </example>
|
|
|
/// <returns>返回写入结果</returns>
|
|
|
public virtual OperateResult Write( string address, bool[] values )
|
|
|
{
|
|
|
// 分析地址
|
|
|
OperateResult<McAddressData> addressResult = McAnalysisAddress( address, 0 );
|
|
|
if (!addressResult.IsSuccess) return addressResult;
|
|
|
|
|
|
// 解析指令
|
|
|
byte[] coreResult = MelsecHelper.BuildAsciiWriteBitCoreCommand( addressResult.Content, values );
|
|
|
|
|
|
// 核心交互
|
|
|
OperateResult<byte[]> read = ReadFromCoreServer( PackMcCommand( coreResult, NetworkNumber, NetworkStationNumber ) );
|
|
|
if (!read.IsSuccess) return read;
|
|
|
|
|
|
// 错误码验证
|
|
|
ushort errorCode = Convert.ToUInt16( Encoding.ASCII.GetString( read.Content, 18, 4 ), 16 );
|
|
|
if (errorCode != 0) return new OperateResult<byte[]>( errorCode, StringResources.Language.MelsecPleaseReferToManulDocument );
|
|
|
|
|
|
// 写入成功
|
|
|
return OperateResult.CreateSuccessResult( );
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Remote Operate
|
|
|
|
|
|
/// <summary>
|
|
|
/// 远程Run操作
|
|
|
/// </summary>
|
|
|
/// <returns>是否成功</returns>
|
|
|
public OperateResult RemoteRun()
|
|
|
{
|
|
|
// 核心交互
|
|
|
OperateResult<byte[]> read = ReadFromCoreServer( PackMcCommand( Encoding.ASCII.GetBytes("1001000000010000"), NetworkNumber, NetworkStationNumber ) );
|
|
|
if (!read.IsSuccess) return read;
|
|
|
|
|
|
// 错误码校验
|
|
|
ushort errorCode = Convert.ToUInt16( Encoding.ASCII.GetString( read.Content, 18, 4 ), 16 );
|
|
|
if (errorCode != 0) return new OperateResult( errorCode, StringResources.Language.MelsecPleaseReferToManulDocument );
|
|
|
|
|
|
// 成功
|
|
|
return OperateResult.CreateSuccessResult( );
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 远程Stop操作
|
|
|
/// </summary>
|
|
|
/// <returns>是否成功</returns>
|
|
|
public OperateResult RemoteStop()
|
|
|
{
|
|
|
// 核心交互
|
|
|
OperateResult<byte[]> read = ReadFromCoreServer( PackMcCommand( Encoding.ASCII.GetBytes( "100200000001" ), NetworkNumber, NetworkStationNumber ) );
|
|
|
if (!read.IsSuccess) return read;
|
|
|
|
|
|
// 错误码校验
|
|
|
ushort errorCode = Convert.ToUInt16( Encoding.ASCII.GetString( read.Content, 18, 4 ), 16 );
|
|
|
if (errorCode != 0) return new OperateResult( errorCode, StringResources.Language.MelsecPleaseReferToManulDocument );
|
|
|
|
|
|
// 成功
|
|
|
return OperateResult.CreateSuccessResult( );
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 读取PLC的型号信息
|
|
|
/// </summary>
|
|
|
/// <returns>返回型号的结果对象</returns>
|
|
|
public OperateResult<string> ReadPlcType()
|
|
|
{
|
|
|
// 核心交互
|
|
|
OperateResult<byte[]> read = ReadFromCoreServer( PackMcCommand( Encoding.ASCII.GetBytes( "01010000" ), NetworkNumber, NetworkStationNumber ) );
|
|
|
if (!read.IsSuccess) return OperateResult.CreateFailedResult<string>( read );
|
|
|
|
|
|
// 错误码校验
|
|
|
ushort errorCode = Convert.ToUInt16( Encoding.ASCII.GetString( read.Content, 18, 4 ), 16 );
|
|
|
if (errorCode != 0) return new OperateResult<string>( errorCode, StringResources.Language.MelsecPleaseReferToManulDocument );
|
|
|
|
|
|
// 成功
|
|
|
return OperateResult.CreateSuccessResult( Encoding.ASCII.GetString( read.Content, 22, 16 ).TrimEnd( ) );
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Object Override
|
|
|
|
|
|
/// <summary>
|
|
|
/// 获取当前对象的字符串标识形式
|
|
|
/// </summary>
|
|
|
/// <returns>字符串信息</returns>
|
|
|
public override string ToString( )
|
|
|
{
|
|
|
return $"MelsecMcAsciiNet[{IpAddress}:{Port}]";
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Static Method Helper
|
|
|
|
|
|
/// <summary>
|
|
|
/// 将MC协议的核心报文打包成一个可以直接对PLC进行发送的原始报文
|
|
|
/// </summary>
|
|
|
/// <param name="mcCore">MC协议的核心报文</param>
|
|
|
/// <param name="networkNumber">网络号</param>
|
|
|
/// <param name="networkStationNumber">网络站号</param>
|
|
|
/// <returns>原始报文信息</returns>
|
|
|
public static byte[] PackMcCommand( byte[] mcCore, byte networkNumber = 0, byte networkStationNumber = 0 )
|
|
|
{
|
|
|
byte[] plcCommand = new byte[22 + mcCore.Length];
|
|
|
plcCommand[ 0] = 0x35; // 副标题
|
|
|
plcCommand[ 1] = 0x30;
|
|
|
plcCommand[ 2] = 0x30;
|
|
|
plcCommand[ 3] = 0x30;
|
|
|
plcCommand[ 4] = SoftBasic.BuildAsciiBytesFrom( networkNumber )[0]; // 网络号
|
|
|
plcCommand[ 5] = SoftBasic.BuildAsciiBytesFrom( networkNumber )[1];
|
|
|
plcCommand[ 6] = 0x46; // PLC编号
|
|
|
plcCommand[ 7] = 0x46;
|
|
|
plcCommand[ 8] = 0x30; // 目标模块IO编号
|
|
|
plcCommand[ 9] = 0x33;
|
|
|
plcCommand[10] = 0x46;
|
|
|
plcCommand[11] = 0x46;
|
|
|
plcCommand[12] = SoftBasic.BuildAsciiBytesFrom( networkStationNumber )[0]; // 目标模块站号
|
|
|
plcCommand[13] = SoftBasic.BuildAsciiBytesFrom( networkStationNumber )[1];
|
|
|
plcCommand[14] = SoftBasic.BuildAsciiBytesFrom( (ushort)(plcCommand.Length - 18) )[0]; // 请求数据长度
|
|
|
plcCommand[15] = SoftBasic.BuildAsciiBytesFrom( (ushort)(plcCommand.Length - 18) )[1];
|
|
|
plcCommand[16] = SoftBasic.BuildAsciiBytesFrom( (ushort)(plcCommand.Length - 18) )[2];
|
|
|
plcCommand[17] = SoftBasic.BuildAsciiBytesFrom( (ushort)(plcCommand.Length - 18) )[3];
|
|
|
plcCommand[18] = 0x30; // CPU监视定时器
|
|
|
plcCommand[19] = 0x30;
|
|
|
plcCommand[20] = 0x31;
|
|
|
plcCommand[21] = 0x30;
|
|
|
mcCore.CopyTo( plcCommand, 22 );
|
|
|
|
|
|
return plcCommand;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 从PLC反馈的数据中提取出实际的数据内容,需要传入反馈数据,是否位读取
|
|
|
/// </summary>
|
|
|
/// <param name="response">反馈的数据内容</param>
|
|
|
/// <param name="isBit">是否位读取</param>
|
|
|
/// <returns>解析后的结果对象</returns>
|
|
|
public static OperateResult<byte[]> ExtractActualData( byte[] response, bool isBit )
|
|
|
{
|
|
|
if (isBit)
|
|
|
{
|
|
|
// 位读取
|
|
|
byte[] Content = new byte[response.Length - 22];
|
|
|
for (int i = 22; i < response.Length; i++)
|
|
|
{
|
|
|
Content[i - 22] = response[i] == 0x30 ? (byte)0x00 : (byte)0x01;
|
|
|
}
|
|
|
|
|
|
return OperateResult.CreateSuccessResult( Content );
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
// 字读取
|
|
|
byte[] Content = new byte[(response.Length - 22) / 2];
|
|
|
for (int i = 0; i < Content.Length / 2; i++)
|
|
|
{
|
|
|
ushort tmp = Convert.ToUInt16( Encoding.ASCII.GetString( response, i * 4 + 22, 4 ), 16 );
|
|
|
BitConverter.GetBytes( tmp ).CopyTo( Content, i * 2 );
|
|
|
}
|
|
|
|
|
|
return OperateResult.CreateSuccessResult( Content );
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
}
|
|
|
}
|