using System; using System.Collections.Generic; using System.Linq; using System.Text; using HslCommunication.BasicFramework; using HslCommunication.Core; using HslCommunication.Core.IMessage; using HslCommunication.Core.Net; namespace HslCommunication.ModBus { /// /// Modbus-Tcp协议的客户端通讯类,方便的和服务器进行数据交互 /// public class ModbusTcpNet : NetworkDoubleBase, IReadWriteNet { #region Constructor /// /// 实例化一个MOdbus-Tcp协议的客户端对象 /// public ModbusTcpNet( ) { softIncrementCount = new SoftIncrementCount( ushort.MaxValue ); } /// /// 指定服务器地址,端口号,客户端自己的站号来初始化 /// /// 服务器的Ip地址 /// 服务器的端口号 /// 客户端自身的站号 public ModbusTcpNet( string ipAddress, int port = 502, byte station = 0x01 ) { softIncrementCount = new SoftIncrementCount( ushort.MaxValue ); IpAddress = ipAddress; Port = port; this.station = station; } #endregion #region Private Member private byte station = ModbusInfo.ReadCoil; // 本客户端的站号 private SoftIncrementCount softIncrementCount; // 自增消息的对象 #endregion #region Address Analysis /// /// 解析数据地址,解析出地址类型,起始地址 /// /// 数据地址 /// 解析出地址类型,起始地址,DB块的地址 private OperateResult AnalysisAddress( string address ) { try { return OperateResult.CreateSuccessResult( Convert.ToInt32( address ) ); } catch (Exception ex) { return new OperateResult( ) { Message = ex.Message }; } } #endregion #region Build Command /// /// 读取数据的基础指令,需要指定指令码,地址,长度 /// /// /// /// /// private OperateResult BuildReadCommandBase( byte code, string address, ushort count ) { OperateResult analysis = AnalysisAddress( address ); if (!analysis.IsSuccess) return OperateResult.CreateFailedResult( analysis ); ushort messageId = (ushort)softIncrementCount.GetCurrentValue( ); byte[] buffer = new byte[12]; buffer[0] = (byte)(messageId / 256); buffer[1] = (byte)(messageId % 256); buffer[2] = 0x00; buffer[3] = 0x00; buffer[4] = 0x00; buffer[5] = 0x06; buffer[6] = station; buffer[7] = code; buffer[8] = (byte)(analysis.Content / 256); buffer[9] = (byte)(analysis.Content % 256); buffer[10] = (byte)(count / 256); buffer[11] = (byte)(count % 256); return OperateResult.CreateSuccessResult( buffer ); } /// /// 生成一个读取线圈的指令头 /// /// 地址 /// 长度 /// 携带有命令字节 private OperateResult BuildReadCoilCommand( string address, ushort count ) { return BuildReadCommandBase( ModbusInfo.ReadCoil, address, count ); } /// /// 生成一个读取离散信息的指令头 /// /// 地址 /// 长度 /// 携带有命令字节 private OperateResult BuildReadDiscreteCommand( string address, ushort count ) { return BuildReadCommandBase( ModbusInfo.ReadDiscrete, address, count ); } /// /// 生成一个读取寄存器的指令头 /// /// /// /// 携带有命令字节 private OperateResult BuildReadRegisterCommand( string address, ushort count ) { return BuildReadCommandBase( ModbusInfo.ReadRegister, address, count ); } private OperateResult BuildWriteOneCoilCommand( string address, bool value ) { OperateResult analysis = AnalysisAddress( address ); if (!analysis.IsSuccess) return OperateResult.CreateFailedResult( analysis ); ushort messageId = (ushort)softIncrementCount.GetCurrentValue( ); byte[] buffer = new byte[12]; buffer[0] = (byte)(messageId / 256); buffer[1] = (byte)(messageId % 256); buffer[2] = 0x00; buffer[3] = 0x00; buffer[4] = 0x00; buffer[5] = 0x06; buffer[6] = station; buffer[7] = ModbusInfo.WriteOneCoil; buffer[8] = (byte)(analysis.Content / 256); buffer[9] = (byte)(analysis.Content % 256); buffer[10] = (byte)(value ? 0xFF : 0x00); buffer[11] = 0x00; return OperateResult.CreateSuccessResult( buffer ); } private OperateResult BuildWriteOneRegisterCommand( string address, byte[] data ) { OperateResult analysis = AnalysisAddress( address ); if (!analysis.IsSuccess) return OperateResult.CreateFailedResult( analysis ); ushort messageId = (ushort)softIncrementCount.GetCurrentValue( ); byte[] buffer = new byte[12]; buffer[0] = (byte)(messageId / 256); buffer[1] = (byte)(messageId % 256); buffer[2] = 0x00; buffer[3] = 0x00; buffer[4] = 0x00; buffer[5] = 0x06; buffer[6] = station; buffer[7] = ModbusInfo.WriteOneRegister; buffer[8] = (byte)(analysis.Content / 256); buffer[9] = (byte)(analysis.Content % 256); buffer[10] = data[1]; buffer[11] = data[0]; return OperateResult.CreateSuccessResult( buffer ); } private OperateResult BuildWriteCoilCommand( string address, bool[] values ) { byte[] data = SoftBasic.BoolArrayToByte( values ); OperateResult analysis = AnalysisAddress( address ); if (!analysis.IsSuccess) return OperateResult.CreateFailedResult( analysis ); ushort messageId = (ushort)softIncrementCount.GetCurrentValue( ); byte[] buffer = new byte[13 + data.Length]; buffer[0] = (byte)(messageId / 256); buffer[1] = (byte)(messageId % 256); buffer[2] = 0x00; buffer[3] = 0x00; buffer[4] = (byte)((buffer.Length - 6) / 256); buffer[5] = (byte)((buffer.Length - 6) % 256); buffer[6] = station; buffer[7] = ModbusInfo.WriteCoil; buffer[8] = (byte)(analysis.Content / 256); buffer[9] = (byte)(analysis.Content % 256); buffer[10] = (byte)(values.Length / 256); buffer[11] = (byte)(values.Length % 256); buffer[12] = (byte)(data.Length); data.CopyTo( buffer, 13 ); return OperateResult.CreateSuccessResult( buffer ); } private OperateResult BuildWriteRegisterCommand( string address, byte[] data ) { OperateResult analysis = AnalysisAddress( address ); if (!analysis.IsSuccess) return OperateResult.CreateFailedResult( analysis ); ushort messageId = (ushort)softIncrementCount.GetCurrentValue( ); byte[] buffer = new byte[13 + data.Length]; buffer[0] = (byte)(messageId / 256); buffer[1] = (byte)(messageId % 256); buffer[2] = 0x00; buffer[3] = 0x00; buffer[4] = (byte)((buffer.Length - 6) / 256); buffer[5] = (byte)((buffer.Length - 6) % 256); buffer[6] = station; buffer[7] = ModbusInfo.WriteRegister; buffer[8] = (byte)(analysis.Content / 256); buffer[9] = (byte)(analysis.Content % 256); buffer[10] = (byte)(data.Length / 2 / 256); buffer[11] = (byte)(data.Length / 2 % 256); buffer[12] = (byte)(data.Length); data.CopyTo( buffer, 13 ); return OperateResult.CreateSuccessResult( buffer ); } #endregion #region Core Interative /// /// 通过错误码来获取到对应的文本消息 /// /// /// private string GetDescriptionByErrorCode( byte code ) { switch (code) { case ModbusInfo.FunctionCodeNotSupport: return StringResources.ModbusTcpFunctionCodeNotSupport; case ModbusInfo.FunctionCodeOverBound: return StringResources.ModbusTcpFunctionCodeOverBound; case ModbusInfo.FunctionCodeQuantityOver: return StringResources.ModbusTcpFunctionCodeQuantityOver; case ModbusInfo.FunctionCodeReadWriteException: return StringResources.ModbusTcpFunctionCodeReadWriteException; default: return StringResources.UnknownError; } } private OperateResult CheckModbusTcpResponse( byte[] send ) { OperateResult result = ReadFromCoreServer( send ); if (result.IsSuccess) { if ((send[7] + 0x80) == result.Content[7]) { // 发生了错误 result.IsSuccess = false; result.Message = GetDescriptionByErrorCode( result.Content[8] ); result.ErrorCode = result.Content[8]; } } return result; } #endregion #region Customer Support /// /// 读取自定义的数据类型,只针对寄存器而言,需要规定了写入和解析规则 /// /// 类型名称 /// 起始地址 /// 带是否成功的特定类型的对象 public OperateResult ReadCustomer( string address ) where T : IDataTransfer, new() { OperateResult result = new OperateResult( ); T Content = new T( ); OperateResult read = Read( address, Content.ReadCount ); if (read.IsSuccess) { Content.ParseSource( read.Content ); result.Content = Content; result.IsSuccess = true; } else { result.ErrorCode = read.ErrorCode; result.Message = read.Message; } return result; } /// /// 写入自定义的数据类型到寄存器去,只要规定了生成字节的方法即可 /// /// 自定义类型 /// 起始地址 /// 实例对象 /// 是否成功 public OperateResult WriteCustomer( string address, T data ) where T : IDataTransfer, new() { return Write( address, data.ToSource( ) ); } #endregion #region Read Support /// /// 读取服务器的数据,需要指定不同的功能码 /// /// 指令 /// 地址 /// 长度 /// private OperateResult ReadModBusBase( byte code, string address, ushort length ) { OperateResult command = BuildReadCommandBase( code, address, length ); if (!command.IsSuccess) return OperateResult.CreateFailedResult( command ); OperateResult resultBytes = CheckModbusTcpResponse( command.Content ); if (resultBytes.IsSuccess) { // 二次数据处理 if (resultBytes.Content?.Length >= 9) { byte[] buffer = new byte[resultBytes.Content.Length - 9]; Array.Copy( resultBytes.Content, 9, buffer, 0, buffer.Length ); resultBytes.Content = buffer; } } return resultBytes; } /// /// 读取线圈,需要指定起始地址 /// /// 起始地址,格式为"1234" /// 带有成功标志的bool对象 public OperateResult ReadCoil( string address ) { var read = ReadModBusBase( ModbusInfo.ReadCoil, address, 1 ); if (!read.IsSuccess) return OperateResult.CreateFailedResult( read ); return GetBoolResultFromBytes( read ); } /// /// 批量的读取线圈,需要指定起始地址,读取长度 /// /// 起始地址,格式为"1234" /// 读取长度 /// 带有成功标志的bool数组对象 public OperateResult ReadCoil( string address, ushort length ) { var read = ReadModBusBase( ModbusInfo.ReadCoil, address, length ); if (!read.IsSuccess) return OperateResult.CreateFailedResult( read ); return OperateResult.CreateSuccessResult( SoftBasic.ByteToBoolArray( read.Content, length ) ); } /// /// 批量的离散变量,需要指定起始地址,读取长度 /// /// 起始地址,格式为"1234" /// 读取长度 /// 带有成功标志的bool数组对象 public OperateResult ReadDiscrete( string address, ushort length ) { var read = ReadModBusBase( ModbusInfo.ReadDiscrete, address, length ); if (!read.IsSuccess) return OperateResult.CreateFailedResult( read ); return OperateResult.CreateSuccessResult( SoftBasic.ByteToBoolArray( read.Content, length ) ); } /// /// 从Modbus服务器批量读取寄存器的信息,需要指定起始地址,读取长度 /// /// 起始地址,格式为"1234" /// 读取的数量 /// 带有成功标志的字节信息 public OperateResult Read( string address, ushort length ) { OperateResult analysis = AnalysisAddress( address ); if (!analysis.IsSuccess) return OperateResult.CreateFailedResult( analysis ); List lists = new List( ); ushort alreadyFinished = 0; while (alreadyFinished < length) { ushort lengthTmp = (ushort)Math.Min( (length - alreadyFinished), 120 ); OperateResult read = ReadModBusBase( ModbusInfo.ReadRegister, (analysis.Content + alreadyFinished).ToString( ), lengthTmp ); if (!read.IsSuccess) return OperateResult.CreateFailedResult( read ); lists.AddRange( read.Content ); alreadyFinished += lengthTmp; } return OperateResult.CreateSuccessResult( lists.ToArray( ) ); } /// /// 读取指定地址的short数据 /// /// 起始地址,格式为"1234" /// 带有成功标志的short数据 public OperateResult ReadInt16( string address ) { return GetInt16ResultFromBytes( Read( address, 1 ) ); } /// /// 读取指定地址的ushort数据 /// /// 起始地址,格式为"1234" /// 带有成功标志的ushort数据 public OperateResult ReadUInt16( string address ) { return GetUInt16ResultFromBytes( Read( address, 1 ) ); } /// /// 读取指定地址的int数据 /// /// 起始地址,格式为"1234" /// 带有成功标志的int数据 public OperateResult ReadInt32( string address ) { return GetInt32ResultFromBytes( Read( address, 2 ) ); } /// /// 读取指定地址的uint数据 /// /// 起始地址,格式为"1234" /// 带有成功标志的uint数据 public OperateResult ReadUInt32( string address ) { return GetUInt32ResultFromBytes( Read( address, 2 ) ); } /// /// 读取指定地址的float数据 /// /// 起始地址,格式为"1234" /// 带有成功标志的float数据 public OperateResult ReadFloat( string address ) { return GetSingleResultFromBytes( Read( address, 2 ) ); } /// /// 读取指定地址的long数据 /// /// 起始地址,格式为"1234" /// 带有成功标志的long数据 public OperateResult ReadInt64( string address ) { return GetInt64ResultFromBytes( Read( address, 4 ) ); } /// /// 读取指定地址的ulong数据 /// /// 起始地址,格式为"1234" /// 带有成功标志的ulong数据 public OperateResult ReadUInt64( string address ) { return GetUInt64ResultFromBytes( Read( address, 4 ) ); } /// /// 读取指定地址的double数据 /// /// 起始地址,格式为"1234" /// 带有成功标志的double数据 public OperateResult ReadDouble( string address ) { return GetDoubleResultFromBytes( Read( address, 4 ) ); } /// /// 读取地址地址的String数据,字符串编码为ASCII /// /// 起始地址,格式为"1234" /// 字符串长度 /// 带有成功标志的string数据 public OperateResult ReadString( string address, ushort length ) { return GetStringResultFromBytes( Read( address, length ) ); } #endregion #region Write One Register /// /// 写一个寄存器数据 /// /// 起始地址 /// 高位 /// 地位 /// 返回写入结果 public OperateResult WriteOneRegister( string address, byte high, byte low ) { OperateResult command = BuildWriteOneRegisterCommand( address, new byte[] { high, low } ); if (!command.IsSuccess) { return command; } return CheckModbusTcpResponse( command.Content ); } /// /// 写一个寄存器数据 /// /// 起始地址 /// 写入值 /// 返回写入结果 public OperateResult WriteOneRegister( string address, short value ) { byte[] buffer = BitConverter.GetBytes( value ); return WriteOneRegister( address, buffer[1], buffer[0] ); } /// /// 写一个寄存器数据 /// /// 起始地址 /// 写入值 /// 返回写入结果 public OperateResult WriteOneRegister( string address, ushort value ) { byte[] buffer = BitConverter.GetBytes( value ); return WriteOneRegister( address, buffer[1], buffer[0] ); } #endregion #region Write Base /// /// 将数据写入到Modbus的寄存器上去,需要指定起始地址和数据内容 /// /// 起始地址,格式为"1234" /// 写入的数据,长度根据data的长度来指示 /// 返回写入结果 public OperateResult Write( string address, byte[] value ) { OperateResult command = BuildWriteRegisterCommand( address, value ); if (!command.IsSuccess) { return command; } return CheckModbusTcpResponse( command.Content ); } #endregion #region Write Coil /// /// 写一个线圈信息,指定是否通断 /// /// 起始地址 /// 写入值 /// 返回写入结果 public OperateResult WriteCoil( string address, bool value ) { OperateResult command = BuildWriteOneCoilCommand( address, value ); if (!command.IsSuccess) { return command; } return CheckModbusTcpResponse( command.Content ); } /// /// /// /// 起始地址 /// 写入值 /// 返回写入结果 public OperateResult WriteCoil( string address, bool[] values ) { OperateResult command = BuildWriteCoilCommand( address, values ); if (!command.IsSuccess) { return command; } return CheckModbusTcpResponse( command.Content ); } #endregion #region Write String /// /// 向寄存器中写入字符串,编码格式为ASCII /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult Write( string address, string value ) { byte[] temp = Encoding.ASCII.GetBytes( value ); return Write( address, temp ); } /// /// 向寄存器中写入指定长度的字符串,超出截断,不够补0,编码格式为ASCII /// /// 要写入的数据地址 /// 要写入的实际数据 /// 指定的字符串长度,必须大于0 /// 返回写入结果 public OperateResult Write( string address, string value, int length ) { byte[] temp = Encoding.ASCII.GetBytes( value ); temp = BasicFramework.SoftBasic.ArrayExpandToLength( temp, length ); return Write( address, temp ); } /// /// 向寄存器中写入字符串,编码格式为Unicode /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult WriteUnicodeString( string address, string value ) { byte[] temp = Encoding.Unicode.GetBytes( value ); return Write( address, temp ); } /// /// 向寄存器中写入指定长度的字符串,超出截断,不够补0,编码格式为Unicode /// /// 要写入的数据地址 /// 要写入的实际数据 /// 指定的字符串长度,必须大于0 /// 返回写入结果 public OperateResult WriteUnicodeString( string address, string value, int length ) { byte[] temp = Encoding.Unicode.GetBytes( value ); temp = SoftBasic.ArrayExpandToLength( temp, length * 2 ); return Write( address, temp ); } #endregion #region Write bool[] /// /// 向寄存器中写入bool数组,返回值说明,比如你写入M100,那么data[0]对应M100.0 /// /// 要写入的数据地址 /// 要写入的实际数据,长度为8的倍数 /// 返回写入结果 public OperateResult Write( string address, bool[] values ) { return Write( address, BasicFramework.SoftBasic.BoolArrayToByte( values ) ); } #endregion #region Write Short /// /// 向寄存器中写入short数组,返回值说明 /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult Write( string address, short[] values ) { return Write( address, ByteTransform.TransByte( values ) ); } /// /// 向寄存器中写入short数据,返回值说明 /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult Write( string address, short value ) { return Write( address, new short[] { value } ); } #endregion #region Write UShort /// /// 向寄存器中写入ushort数组,返回值说明 /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult Write( string address, ushort[] values ) { return Write( address, ByteTransform.TransByte( values ) ); } /// /// 向寄存器中写入ushort数据,返回值说明 /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult Write( string address, ushort value ) { return Write( address, new ushort[] { value } ); } #endregion #region Write Int /// /// 向寄存器中写入int数组,返回值说明 /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult Write( string address, int[] values ) { return Write( address, ByteTransform.TransByte( values ) ); } /// /// 向寄存器中写入int数据,返回值说明 /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult Write( string address, int value ) { return Write( address, new int[] { value } ); } #endregion #region Write UInt /// /// 向寄存器中写入uint数组,返回值说明 /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult Write( string address, uint[] values ) { return Write( address, ByteTransform.TransByte( values ) ); } /// /// 向寄存器中写入uint数据,返回值说明 /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult Write( string address, uint value ) { return Write( address, new uint[] { value } ); } #endregion #region Write Float /// /// 向寄存器中写入float数组,返回值说明 /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult Write( string address, float[] values ) { return Write( address, ByteTransform.TransByte( values ) ); } /// /// 向寄存器中写入float数据,返回值说明 /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult Write( string address, float value ) { return Write( address, new float[] { value } ); } #endregion #region Write Long /// /// 向寄存器中写入long数组,返回值说明 /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult Write( string address, long[] values ) { return Write( address, ByteTransform.TransByte( values ) ); } /// /// 向寄存器中写入long数据,返回值说明 /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult Write( string address, long value ) { return Write( address, new long[] { value } ); } #endregion #region Write ULong /// /// 向寄存器中写入ulong数组,返回值说明 /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult Write( string address, ulong[] values ) { return Write( address, ByteTransform.TransByte( values ) ); } /// /// 向寄存器中写入ulong数据,返回值说明 /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult Write( string address, ulong value ) { return Write( address, new ulong[] { value } ); } #endregion #region Write Double /// /// 向寄存器中写入double数组,返回值说明 /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult Write( string address, double[] values ) { return Write( address, ByteTransform.TransByte( values ) ); } /// /// 向寄存器中写入double数据,返回值说明 /// /// 要写入的数据地址 /// 要写入的实际数据 /// 返回写入结果 public OperateResult Write( string address, double value ) { return Write( address, new double[] { value } ); } #endregion #region Object Override /// /// 获取当前对象的字符串标识形式 /// /// 字符串信息 public override string ToString( ) { return "ModbusTcpNet"; } #endregion } }