using HslCommunication ;
using HslCommunication.Core ;
using HslCommunication.ModBus ;
using HybirdFrameworkCore.Utils ;
using log4net ;
namespace HybirdFrameworkDriver.ModbusTcpMaster ;
public class ModbusTcpMaster
{
public delegate void MyReadAction ( ModbusTcpMaster str ) ;
private static readonly ILog Log = LogManager . GetLogger ( typeof ( ModbusTcpMaster ) ) ;
private ModbusTcpNet ? ModbusTcpNet ;
public string Ip { get ; set ; } = "127.0.0.1" ;
public int Port { get ; set ; } = 502 ;
public static DataFormat DataFormat { get ; set ; } = DataFormat . ABCD ;
public int Duration { get ; set ; } = 1000 ;
public bool Connected { get ; set ; }
/// <summary>
/// 自动重连开关
/// </summary>
public bool AutoReConnect { get ; set ; }
public string connectId { get ; set ; }
public MyReadAction ? ReadAction { get ; set ; }
private bool StopFlag { get ; set ; }
private ILog GetLog ( )
{
return Log ;
}
public bool Connect ( )
{
Thread . Sleep ( Duration ) ;
GetLog ( ) . Info ( $"begin to connect {Ip}:{Port}" ) ;
try
{
if ( ModbusTcpNet = = null )
{
ModbusTcpNet = new ModbusTcpNet ( Ip , Port ) ;
ModbusTcpNet . DataFormat = DataFormat ;
var result = ModbusTcpNet . ConnectServer ( ) ;
connectId = ModbusTcpNet . ConnectionId ;
if ( result . IsSuccess )
{
Connected = true ;
GetLog ( ) . Info ( $"connect {Ip}:{Port} success" ) ;
StartListen ( ) ;
}
else
{
GetLog ( ) . Info ( $"connect {Ip}:{Port} failed {result.Message}" ) ;
if ( AutoReConnect )
{
return Connect ( ) ;
}
}
}
}
catch ( Exception e )
{
GetLog ( ) . Error ( $"connect {Ip}:{Port} exception {e.Message}" ) ;
if ( AutoReConnect )
{
return Connect ( ) ;
}
}
return Connected ;
}
public void StartListen ( )
{
StopListen ( ) ;
StopFlag = false ;
var readThread = new Thread ( ReadFunc )
{
IsBackground = true ,
Name = $"{Ip}:{Port}-reader-thread"
} ;
readThread . Start ( ) ;
}
public void StopListen ( )
{
StopFlag = true ;
Thread . Sleep ( 500 ) ;
}
private void ReadFunc ( )
{
if ( ReadAction ! = null )
while ( ! StopFlag )
try
{
ReadAction ( this ) ;
Thread . Sleep ( Duration ) ;
}
catch ( Exception e )
{
Log . Error ( e ) ;
}
GetLog ( ) . Info ( "stop listen" ) ;
}
public byte [ ] ? ReadRegister ( int registerNo , int start , int length )
{
start = start % 16 = = 0 ? start / 16 : start / 16 + 1 ;
length = length % 8 = = 0 ? length / 8 : length / 8 + 1 ;
OperateResult < byte [ ] > result = ModbusTcpNet . Read ( "x=3;" + ( registerNo + start ) , ( ushort ) length ) ;
if ( result . IsSuccess ) return result . Content ;
return null ;
}
public byte [ ] ? BatchReadHolderRegister ( int registerNo , int length )
{
OperateResult < byte [ ] > result = ModbusTcpNet . Read ( "x=3;" + registerNo , ( ushort ) length ) ;
if ( result . IsSuccess ) return result . Content ;
return null ;
}
public byte [ ] ? BatchReadInputRegister ( int registerNo , int length )
{
OperateResult < byte [ ] > result = ModbusTcpNet . Read ( "x=4;" + registerNo , ( ushort ) length ) ;
if ( result . IsSuccess ) return result . Content ;
return null ;
}
/// <summary>
/// 读取线圈,需要指定起始地址,如果富文本地址不指定,默认使用的功能码是 0x01<br />
/// To read the coil, you need to specify the start address. If the rich text address is not specified, the default function code is 0x01.
/// </summary>
/// <param name="address">起始地址,格式为"1234"</param>
/// <returns>带有成功标志的bool对象</returns>
public OperateResult < bool > ReadCoil ( string address ) = > ModbusTcpNet . ReadBool ( address ) ;
/// <summary>
/// 批量的读取线圈,需要指定起始地址,读取长度,如果富文本地址不指定,默认使用的功能码是 0x01<br />
/// For batch reading coils, you need to specify the start address and read length. If the rich text address is not specified, the default function code is 0x01.
/// </summary>
/// <param name="address">起始地址,格式为"1234"</param>
/// <param name="length">读取长度</param>
/// <returns>带有成功标志的bool数组对象</returns>
public OperateResult < bool [ ] > ReadCoil ( string address , ushort length )
{
return ModbusTcpNet . ReadBool ( address , length ) ;
}
/// <summary>
/// 读取输入线圈,需要指定起始地址,如果富文本地址不指定,默认使用的功能码是 0x02<br />
/// To read the input coil, you need to specify the start address. If the rich text address is not specified, the default function code is 0x02.
/// </summary>
/// <param name="address">起始地址,格式为"1234"</param>
/// <returns>带有成功标志的bool对象</returns>
public OperateResult < bool > ReadDiscrete ( string address )
{
return ModbusTcpNet . ReadDiscrete ( address ) ;
}
/// <summary>
/// 批量的读取输入点,需要指定起始地址,读取长度,如果富文本地址不指定,默认使用的功能码是 0x02<br />
/// To read input points in batches, you need to specify the start address and read length. If the rich text address is not specified, the default function code is 0x02
/// </summary>
/// <param name="address">起始地址,格式为"1234"</param>
/// <param name="length">读取长度</param>
/// <returns>带有成功标志的bool数组对象</returns>
public OperateResult < bool [ ] > ReadDiscrete ( string address , ushort length )
{
return ModbusTcpNet . ReadDiscrete ( address , length ) ;
}
public bool WriteValue < T > ( ModbusProperty < T > property )
{
var value = property . Value ;
if ( value = = null )
{
GetLog ( ) . Warn ( $"write property{property.RegisterNo} null value" ) ;
return false ;
}
var result = false ;
var dataType = property . Type ;
var start = property . Start ;
var length = property . Length ;
var registerNo = property . RegisterNo - 40000 ;
var setValue = BitUtls . Value2Bytes ( value , property . Scale , property . Offset ) ;
OperateResult operateResult ;
switch ( dataType )
{
case ModbusDataType . Register :
if ( setValue . Length < length * 2 )
{
//byte 需要读取寄存器中的内容
if ( typeof ( T ) = = typeof ( byte ) )
{
OperateResult < byte [ ] > readResultRegister = ModbusTcpNet . Read ( "x=3;" + registerNo , 1 ) ;
if ( readResultRegister . IsSuccess )
{
switch ( DataFormat )
{
case DataFormat . ABCD :
readResultRegister . Content [ 1 ] = setValue [ 0 ] ;
break ;
case DataFormat . BADC :
readResultRegister . Content [ 0 ] = setValue [ 0 ] ;
break ;
}
operateResult = ModbusTcpNet . Write ( "x=16;" + registerNo , readResultRegister . Content ) ;
result = operateResult . IsSuccess ;
}
}
//其他类型 String 占用几个寄存器 直接补0
else
{
var preWriteCont = new byte [ length * 2 ] ;
switch ( DataFormat )
{
case DataFormat . ABCD :
for ( var i = 0 ; i < setValue . Length ; i + + )
if ( i % 2 = = 0 )
preWriteCont [ i + 1 ] = setValue [ i ] ;
else
preWriteCont [ i - 1 ] = setValue [ i ] ;
break ;
case DataFormat . BADC :
Array . Copy ( setValue , preWriteCont , setValue . Length ) ;
break ;
}
operateResult = ModbusTcpNet . Write ( "x=16;" + registerNo , preWriteCont ) ;
result = operateResult . IsSuccess ;
}
}
else if ( setValue . Length = = length * 2 )
{
var preWriteCont = new byte [ setValue . Length ] ;
switch ( DataFormat )
{
case DataFormat . ABCD :
for ( var i = 0 ; i < setValue . Length ; i + + )
if ( i % 2 = = 0 )
preWriteCont [ i + 1 ] = setValue [ i ] ;
else
preWriteCont [ i - 1 ] = setValue [ i ] ;
break ;
case DataFormat . BADC :
//Array.Copy(setValue, preWriteCont, setValue.Length);
break ;
}
operateResult = ModbusTcpNet . Write ( "x=16;" + registerNo , preWriteCont ) ;
result = operateResult . IsSuccess ;
}
break ;
case ModbusDataType . Bit :
result = WriteRegisterOneBit ( registerNo , start , Convert . ToBoolean ( value ) ) ;
break ;
}
return result ;
}
/// <summary>
/// 写寄存器中的一个bit
/// </summary>
/// <param name="addr"></param>
/// <param name="location"></param>
/// <param name="boolResult"></param>
/// <returns></returns>
public bool WriteRegisterOneBit ( int addr , int location , bool boolResult )
{
if ( ! Connected ) return false ;
var result = false ;
ushort registerValue1 = 0x1234 ;
ushort mask = 0x1234 ;
OperateResult < byte [ ] > readResult = ModbusTcpNet . Read ( "x=3;" + addr , 1 ) ;
if ( readResult . IsSuccess )
if ( location > = 0 & & location < = 15 )
{
registerValue1 = ModbusTcpNet . ByteTransform . TransUInt16 ( readResult . Content , 0 ) ;
if ( boolResult )
{
mask = ( ushort ) ( 1 < < location ) ;
registerValue1 | = mask ;
}
else
{
mask = ( ushort ) ~ ( 1 < < location ) ;
registerValue1 & = mask ;
}
var writeResult = ModbusTcpNet . Write ( "x=6;" + addr , registerValue1 ) ;
if ( writeResult . IsSuccess ) result = true ;
}
return result ;
}
}