using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using HslCommunication.BasicFramework;
using HslCommunication.Core;
using HslCommunication.Core.Net;
using HslCommunication.Core.IMessage;
using HslCommunication.Core.Address;
namespace HslCommunication.Profinet.Melsec
{
///
/// 三菱MC协议的虚拟服务器,支持M,X,Y,D,W的数据池读写操作,使用二进制进行读写操作
///
public class MelsecMcServer : NetworkDataServerBase
{
#region Constructor
///
/// 实例化一个mc协议的服务器
///
public MelsecMcServer( )
{
// 共计使用了五个数据池
xBuffer = new SoftBuffer( DataPoolLength );
yBuffer = new SoftBuffer( DataPoolLength );
mBuffer = new SoftBuffer( DataPoolLength );
dBuffer = new SoftBuffer( DataPoolLength * 2 );
wBuffer = new SoftBuffer( DataPoolLength * 2 );
WordLength = 1;
ByteTransform = new RegularByteTransform( );
}
#endregion
#region NetworkDataServerBase Override
///
/// 读取自定义的寄存器的值。按照字为单位
///
/// 起始地址,示例:"D100","M100"
/// 数据长度
///
/// byte数组值
public override OperateResult Read( string address, ushort length )
{
// 分析地址
OperateResult analysis = McAddressData.ParseMelsecFrom( address, length );
if (!analysis.IsSuccess) return OperateResult.CreateFailedResult( analysis );
if(analysis.Content.McDataType.DataCode == MelsecMcDataType.M.DataCode)
{
bool[] buffer = mBuffer.GetBytes( analysis.Content.AddressStart, length * 16 ).Select( m => m != 0x00 ).ToArray( );
return OperateResult.CreateSuccessResult( SoftBasic.BoolArrayToByte( buffer ) );
}
else if(analysis.Content.McDataType.DataCode == MelsecMcDataType.X.DataCode)
{
bool[] buffer = xBuffer.GetBytes( analysis.Content.AddressStart, length * 16 ).Select( m => m != 0x00 ).ToArray( );
return OperateResult.CreateSuccessResult( SoftBasic.BoolArrayToByte( buffer ) );
}
else if (analysis.Content.McDataType.DataCode == MelsecMcDataType.Y.DataCode)
{
bool[] buffer = yBuffer.GetBytes( analysis.Content.AddressStart, length * 16 ).Select( m => m != 0x00 ).ToArray( );
return OperateResult.CreateSuccessResult( SoftBasic.BoolArrayToByte( buffer ) );
}
else if (analysis.Content.McDataType.DataCode == MelsecMcDataType.D.DataCode)
{
return OperateResult.CreateSuccessResult( dBuffer.GetBytes( analysis.Content.AddressStart * 2, length * 2 ) );
}
else if (analysis.Content.McDataType.DataCode == MelsecMcDataType.W.DataCode)
{
return OperateResult.CreateSuccessResult( wBuffer.GetBytes( analysis.Content.AddressStart * 2, length * 2 ) );
}
else
{
return new OperateResult( StringResources.Language.NotSupportedDataType );
}
}
///
/// 写入自定义的数据到数据内存中去
///
/// 地址
/// 数据值
/// 是否写入成功的结果对象
public override OperateResult Write( string address, byte[] value )
{
// 分析地址
OperateResult analysis = McAddressData.ParseMelsecFrom( address, 0 );
if (!analysis.IsSuccess) return OperateResult.CreateFailedResult( analysis );
if (analysis.Content.McDataType.DataCode == MelsecMcDataType.M.DataCode)
{
byte[] buffer = SoftBasic.ByteToBoolArray( value ).Select( m => m ? (byte)1 : (byte)0 ).ToArray( );
mBuffer.SetBytes( buffer, analysis.Content.AddressStart );
return OperateResult.CreateSuccessResult( );
}
else if (analysis.Content.McDataType.DataCode == MelsecMcDataType.X.DataCode)
{
byte[] buffer = SoftBasic.ByteToBoolArray( value ).Select( m => m ? (byte)1 : (byte)0 ).ToArray( );
xBuffer.SetBytes( buffer, analysis.Content.AddressStart );
return OperateResult.CreateSuccessResult( );
}
else if (analysis.Content.McDataType.DataCode == MelsecMcDataType.Y.DataCode)
{
byte[] buffer = SoftBasic.ByteToBoolArray( value ).Select( m => m ? (byte)1 : (byte)0 ).ToArray( );
yBuffer.SetBytes( buffer, analysis.Content.AddressStart );
return OperateResult.CreateSuccessResult( );
}
else if (analysis.Content.McDataType.DataCode == MelsecMcDataType.D.DataCode)
{
dBuffer.SetBytes( value, analysis.Content.AddressStart * 2 );
return OperateResult.CreateSuccessResult( );
}
else if (analysis.Content.McDataType.DataCode == MelsecMcDataType.W.DataCode)
{
wBuffer.SetBytes( value, analysis.Content.AddressStart * 2 );
return OperateResult.CreateSuccessResult( );
}
else
{
return new OperateResult( StringResources.Language.NotSupportedDataType );
}
}
#endregion
#region Bool Read Write Operate
///
/// 读取指定地址的bool数据对象
///
/// 西门子的地址信息
/// 带有成功标志的结果对象
public OperateResult ReadBool( string address )
{
OperateResult read = ReadBool( address, 1 );
if (!read.IsSuccess) return OperateResult.CreateFailedResult( read );
return OperateResult.CreateSuccessResult( read.Content[0] );
}
///
/// 读取指定地址的bool数据对象
///
/// 三菱的地址信息
/// 数组的长度
/// 带有成功标志的结果对象
public OperateResult ReadBool( string address, ushort length )
{
// 分析地址
OperateResult analysis = McAddressData.ParseMelsecFrom( address, 0 );
if (!analysis.IsSuccess) return OperateResult.CreateFailedResult( analysis );
if (analysis.Content.McDataType.DataType == 0) return new OperateResult( StringResources.Language.MelsecCurrentTypeNotSupportedWordOperate );
if (analysis.Content.McDataType.DataCode == MelsecMcDataType.M.DataCode)
{
return OperateResult.CreateSuccessResult( mBuffer.GetBytes( analysis.Content.AddressStart, length ).Select( m => m != 0x00 ).ToArray( ) );
}
else if (analysis.Content.McDataType.DataCode == MelsecMcDataType.X.DataCode)
{
return OperateResult.CreateSuccessResult( xBuffer.GetBytes( analysis.Content.AddressStart, length ).Select( m => m != 0x00 ).ToArray( ) );
}
else if (analysis.Content.McDataType.DataCode == MelsecMcDataType.Y.DataCode)
{
return OperateResult.CreateSuccessResult( yBuffer.GetBytes( analysis.Content.AddressStart, length ).Select( m => m != 0x00 ).ToArray( ) );
}
else
{
return new OperateResult( StringResources.Language.NotSupportedDataType );
}
}
///
/// 往指定的地址里写入bool数据对象
///
/// 三菱的地址信息
/// 值
/// 是否成功的结果
public OperateResult Write( string address, bool value )
{
return Write( address, new bool[] { value } );
}
///
/// 往指定的地址里写入bool数组对象
///
/// 三菱的地址信息
/// 值
/// 是否成功的结果
public OperateResult Write( string address, bool[] value )
{
// 分析地址
OperateResult analysis = McAddressData.ParseMelsecFrom( address, 0 );
if (!analysis.IsSuccess) return OperateResult.CreateFailedResult( analysis );
if (analysis.Content.McDataType.DataType == 0) return new OperateResult( StringResources.Language.MelsecCurrentTypeNotSupportedWordOperate );
if (analysis.Content.McDataType.DataCode == MelsecMcDataType.M.DataCode)
{
mBuffer.SetBytes( value.Select( m => m ? (byte)1 : (byte)0 ).ToArray( ), analysis.Content.AddressStart );
return OperateResult.CreateSuccessResult( );
}
else if (analysis.Content.McDataType.DataCode == MelsecMcDataType.X.DataCode)
{
xBuffer.SetBytes( value.Select( m => m ? (byte)1 : (byte)0 ).ToArray( ), analysis.Content.AddressStart );
return OperateResult.CreateSuccessResult( );
}
else if (analysis.Content.McDataType.DataCode == MelsecMcDataType.Y.DataCode)
{
yBuffer.SetBytes( value.Select( m => m ? (byte)1 : (byte)0 ).ToArray( ), analysis.Content.AddressStart );
return OperateResult.CreateSuccessResult( );
}
else
{
return new OperateResult( StringResources.Language.NotSupportedDataType );
}
}
#endregion
#region NetServer Override
///
/// 当客户端登录后,进行Ip信息的过滤,然后触发本方法,也就是说之后的客户端需要
///
/// 网络套接字
/// 终端节点
protected override void ThreadPoolLoginAfterClientCheck( Socket socket, System.Net.IPEndPoint endPoint )
{
// 开始接收数据信息
AppSession appSession = new AppSession( );
appSession.IpEndPoint = endPoint;
appSession.WorkSocket = socket;
try
{
socket.BeginReceive( new byte[0], 0, 0, SocketFlags.None, new AsyncCallback( SocketAsyncCallBack ), appSession );
AddClient( appSession );
}
catch
{
socket.Close( );
LogNet?.WriteDebug( ToString( ), string.Format( StringResources.Language.ClientOfflineInfo, endPoint ) );
}
}
private void SocketAsyncCallBack( IAsyncResult ar )
{
if (ar.AsyncState is AppSession session)
{
try
{
int receiveCount = session.WorkSocket.EndReceive( ar );
MelsecQnA3EBinaryMessage mcMessage = new MelsecQnA3EBinaryMessage( );
OperateResult read1 = ReceiveByMessage( session.WorkSocket, 5000, mcMessage );
if (!read1.IsSuccess)
{
LogNet?.WriteDebug( ToString( ), string.Format( StringResources.Language.ClientOfflineInfo, session.IpEndPoint ) );
RemoveClient( session );
return;
};
byte[] back = ReadFromMcCore( read1.Content );
if(back != null)
{
session.WorkSocket.Send( back );
}
else
{
session.WorkSocket.Close( );
RemoveClient( session );
return;
}
RaiseDataReceived( read1.Content );
session.WorkSocket.BeginReceive( new byte[0], 0, 0, SocketFlags.None, new AsyncCallback( SocketAsyncCallBack ), session );
}
catch
{
// 关闭连接,记录日志
session.WorkSocket?.Close( );
LogNet?.WriteDebug( ToString( ), string.Format( StringResources.Language.ClientOfflineInfo, session.IpEndPoint ) );
RemoveClient( session );
return;
}
}
}
///
/// 当收到mc协议的报文的时候应该触发的方法,允许继承重写,来实现自定义的返回,或是数据监听。
///
/// mc报文
/// 返回的报文信息
protected virtual byte[] ReadFromMcCore( byte[] mcCore )
{
if (mcCore[11] == 0x01 && mcCore[12] == 0x04)
{
// 读数据
return PackCommand( ReadByCommand( SoftBasic.BytesArrayRemoveBegin( mcCore, 11 ) ) );
}
else if (mcCore[11] == 0x01 && mcCore[12] == 0x14)
{
// 写数据
return PackCommand( WriteByMessage( SoftBasic.BytesArrayRemoveBegin( mcCore, 11 ) ) );
}
else
{
return null;
}
}
private byte[] PackCommand( byte[] data )
{
byte[] back = new byte[11 + data.Length];
SoftBasic.HexStringToBytes( "D0 00 00 FF FF 03 00 00 00 00 00" ).CopyTo( back, 0 );
if (data.Length > 0) data.CopyTo( back, 11 );
BitConverter.GetBytes( (short)(data.Length + 2) ).CopyTo( back, 7 );
return back;
}
private byte[] ReadByCommand( byte[] command )
{
if (command[2] == 0x01)
{
// 位读取
ushort length = ByteTransform.TransUInt16( command, 8 );
int startIndex = (command[6] * 65536 + command[5] * 256 + command[4]);
if (command[7] == MelsecMcDataType.M.DataCode)
{
byte[] buffer = mBuffer.GetBytes( startIndex, length );
return MelsecHelper.TransBoolArrayToByteData( buffer );
}
else if (command[7] == MelsecMcDataType.X.DataCode)
{
byte[] buffer = xBuffer.GetBytes( startIndex, length );
return MelsecHelper.TransBoolArrayToByteData( buffer );
}
else if (command[7] == MelsecMcDataType.Y.DataCode)
{
byte[] buffer = yBuffer.GetBytes( startIndex, length );
return MelsecHelper.TransBoolArrayToByteData( buffer );
}
else
{
throw new Exception( StringResources.Language.NotSupportedDataType );
}
}
else
{
// 字读取
ushort length = ByteTransform.TransUInt16( command, 8 );
int startIndex = (command[6] * 65536 + command[5] * 256 + command[4]);
if (command[7] == MelsecMcDataType.M.DataCode)
{
bool[] buffer = mBuffer.GetBytes( startIndex, length * 16 ).Select( m => m != 0x00 ).ToArray( );
return SoftBasic.BoolArrayToByte( buffer );
}
else if(command[7] == MelsecMcDataType.X.DataCode)
{
bool[] buffer = xBuffer.GetBytes( startIndex, length * 16 ).Select( m => m != 0x00 ).ToArray( );
return SoftBasic.BoolArrayToByte( buffer );
}
else if (command[7] == MelsecMcDataType.Y.DataCode)
{
bool[] buffer = yBuffer.GetBytes( startIndex, length * 16 ).Select( m => m != 0x00 ).ToArray( );
return SoftBasic.BoolArrayToByte( buffer );
}
else if (command[7] == MelsecMcDataType.D.DataCode)
{
return dBuffer.GetBytes( startIndex * 2, length * 2 );
}
else if (command[7] == MelsecMcDataType.W.DataCode)
{
return wBuffer.GetBytes( startIndex * 2, length * 2 );
}
else
{
throw new Exception( StringResources.Language.NotSupportedDataType );
}
}
}
private byte[] WriteByMessage( byte[] command )
{
if (command[2] == 0x01)
{
// 位写入
ushort length = ByteTransform.TransUInt16( command, 8 );
int startIndex = (command[6] * 65536 + command[5] * 256 + command[4]);
if (command[7] == MelsecMcDataType.M.DataCode)
{
byte[] buffer = MelsecMcNet.ExtractActualData( SoftBasic.BytesArrayRemoveBegin( command, 10 ), true ).Content;
mBuffer.SetBytes( buffer.Take( length ).ToArray( ), startIndex );
return new byte[0];
}
else if (command[7] == MelsecMcDataType.X.DataCode)
{
byte[] buffer = MelsecMcNet.ExtractActualData( SoftBasic.BytesArrayRemoveBegin( command, 10 ), true ).Content;
xBuffer.SetBytes( buffer.Take( length ).ToArray( ), startIndex );
return new byte[0];
}
else if (command[7] == MelsecMcDataType.Y.DataCode)
{
byte[] buffer = MelsecMcNet.ExtractActualData( SoftBasic.BytesArrayRemoveBegin( command, 10 ), true ).Content;
yBuffer.SetBytes( buffer.Take( length ).ToArray( ), startIndex );
return new byte[0];
}
else
{
throw new Exception( StringResources.Language.NotSupportedDataType );
}
}
else
{
// 字写入
ushort length = ByteTransform.TransUInt16( command, 8 );
int startIndex = (command[6] * 65536 + command[5] * 256 + command[4]);
if (command[7] == MelsecMcDataType.M.DataCode)
{
byte[] buffer = SoftBasic.ByteToBoolArray( SoftBasic.BytesArrayRemoveBegin( command, 10 ) ).Select( m => m ? (byte)1 : (byte)0 ).ToArray( );
mBuffer.SetBytes( buffer, startIndex );
return new byte[0];
}
else if (command[7] == MelsecMcDataType.X.DataCode)
{
byte[] buffer = SoftBasic.ByteToBoolArray( SoftBasic.BytesArrayRemoveBegin( command, 10 ) ).Select( m => m ? (byte)1 : (byte)0 ).ToArray( );
xBuffer.SetBytes( buffer, startIndex );
return new byte[0];
}
else if (command[7] == MelsecMcDataType.Y.DataCode)
{
byte[] buffer = SoftBasic.ByteToBoolArray( SoftBasic.BytesArrayRemoveBegin( command, 10 ) ).Select( m => m ? (byte)1 : (byte)0 ).ToArray( );
yBuffer.SetBytes( buffer, startIndex );
return new byte[0];
}
else if (command[7] == MelsecMcDataType.D.DataCode)
{
dBuffer.SetBytes( SoftBasic.BytesArrayRemoveBegin( command, 10 ), startIndex * 2 );
return new byte[0];
}
else if (command[7] == MelsecMcDataType.W.DataCode)
{
wBuffer.SetBytes( SoftBasic.BytesArrayRemoveBegin( command, 10 ), startIndex * 2 );
return new byte[0];
}
else
{
throw new Exception( StringResources.Language.NotSupportedDataType );
}
}
}
#endregion
#region Data Save Load Override
///
/// 从字节数据加载数据信息
///
/// 字节数据
protected override void LoadFromBytes( byte[] content )
{
if (content.Length < DataPoolLength * 7) throw new Exception( "File is not correct" );
mBuffer.SetBytes( content, 0, 0, DataPoolLength );
xBuffer.SetBytes( content, DataPoolLength, 0, DataPoolLength );
yBuffer.SetBytes( content, DataPoolLength * 2, 0, DataPoolLength );
dBuffer.SetBytes( content, DataPoolLength * 3, 0, DataPoolLength );
wBuffer.SetBytes( content, DataPoolLength * 5, 0, DataPoolLength );
}
///
/// 将数据信息存储到字节数组去
///
/// 所有的内容
protected override byte[] SaveToBytes( )
{
byte[] buffer = new byte[DataPoolLength * 7];
Array.Copy( mBuffer.GetBytes( ), 0, buffer, 0, DataPoolLength );
Array.Copy( xBuffer.GetBytes( ), 0, buffer, DataPoolLength, DataPoolLength );
Array.Copy( yBuffer.GetBytes( ), 0, buffer, DataPoolLength * 2, DataPoolLength );
Array.Copy( dBuffer.GetBytes( ), 0, buffer, DataPoolLength * 3, DataPoolLength );
Array.Copy( wBuffer.GetBytes( ), 0, buffer, DataPoolLength * 5, DataPoolLength );
return buffer;
}
#endregion
#region IDisposable Support
///
/// 释放当前的对象
///
/// 是否托管对象
protected override void Dispose( bool disposing )
{
if (disposing)
{
xBuffer?.Dispose( );
yBuffer?.Dispose( );
mBuffer?.Dispose( );
dBuffer?.Dispose( );
wBuffer?.Dispose( );
}
base.Dispose( disposing );
}
#endregion
#region Private Member
private SoftBuffer xBuffer; // x寄存器的数据池
private SoftBuffer yBuffer; // y寄存器的数据池
private SoftBuffer mBuffer; // m寄存器的数据池
private SoftBuffer dBuffer; // d寄存器的数据池
private SoftBuffer wBuffer; // w寄存器的数据池
private const int DataPoolLength = 65536; // 数据的长度
#endregion
#region Object Override
///
/// 返回表示当前对象的字符串
///
/// 字符串信息
public override string ToString( )
{
return $"MelsecMcServer[{Port}]";
}
#endregion
}
}