mirror of
				https://gitee.com/ThingsGateway/ThingsGateway.git
				synced 2025-10-31 15:43:59 +08:00 
			
		
		
		
	Compare commits
	
		
			40 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | affe9a44e0 | ||
|   | 43730fa519 | ||
|   | d39aa22b09 | ||
|   | e232a6b6ea | ||
|   | 71ebb36fe9 | ||
|   | 78a0b86327 | ||
|   | 2636c16a97 | ||
|   | fd77c0242d | ||
|   | e74819a900 | ||
|   | 9b7f696c9b | ||
|   | 0230d614e7 | ||
|   | 252d99ad78 | ||
|   | 1ffc200350 | ||
|   | 807d89b2b2 | ||
|   | 4013afa1f1 | ||
|   | a580927ceb | ||
|   | bf2cf52034 | ||
|   | 81bb8b7c31 | ||
|   | a825007fb5 | ||
|   | 988124d96a | ||
|   | f0de815296 | ||
|   | 0e2d58c887 | ||
|   | b155382626 | ||
|   | f362d740af | ||
|   | 4a85e31a4f | ||
|   | 302c270ad5 | ||
|   | 3c1517d0f3 | ||
|   | f9fb222044 | ||
|   | e8edc02ba3 | ||
|   | 95a44e3053 | ||
|   | 74a9fe9a87 | ||
|   | 4d03f9ea1a | ||
|   | 67c96ca991 | ||
|   | 88fb793c68 | ||
|   | d6d02d8cc5 | ||
|   | c5a3f8e2e3 | ||
|   | 27e8653a1a | ||
|   | 863beda82c | ||
|   | bac84c3ecd | ||
|   | 2fca2ad9f8 | 
| @@ -5,3 +5,99 @@ dotnet_diagnostic.CA1848.severity = none | ||||
|  | ||||
| # CA2254: 模板应为静态表达式 | ||||
| dotnet_diagnostic.CA2254.severity = suggestion | ||||
|  | ||||
| [*.cs] | ||||
| #### 命名样式 #### | ||||
|  | ||||
| # 命名规则 | ||||
|  | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i | ||||
|  | ||||
| dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion | ||||
| dotnet_naming_rule.types_should_be_pascal_case.symbols = types | ||||
| dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case | ||||
|  | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case | ||||
|  | ||||
| # 符号规范 | ||||
|  | ||||
| dotnet_naming_symbols.interface.applicable_kinds = interface | ||||
| dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.interface.required_modifiers =  | ||||
|  | ||||
| dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum | ||||
| dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.types.required_modifiers =  | ||||
|  | ||||
| dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method | ||||
| dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.non_field_members.required_modifiers =  | ||||
|  | ||||
| # 命名样式 | ||||
|  | ||||
| dotnet_naming_style.begins_with_i.required_prefix = I | ||||
| dotnet_naming_style.begins_with_i.required_suffix =  | ||||
| dotnet_naming_style.begins_with_i.word_separator =  | ||||
| dotnet_naming_style.begins_with_i.capitalization = pascal_case | ||||
|  | ||||
| dotnet_naming_style.pascal_case.required_prefix =  | ||||
| dotnet_naming_style.pascal_case.required_suffix =  | ||||
| dotnet_naming_style.pascal_case.word_separator =  | ||||
| dotnet_naming_style.pascal_case.capitalization = pascal_case | ||||
|  | ||||
| dotnet_naming_style.pascal_case.required_prefix =  | ||||
| dotnet_naming_style.pascal_case.required_suffix =  | ||||
| dotnet_naming_style.pascal_case.word_separator =  | ||||
| dotnet_naming_style.pascal_case.capitalization = pascal_case | ||||
|  | ||||
| [*.vb] | ||||
| #### 命名样式 #### | ||||
|  | ||||
| # 命名规则 | ||||
|  | ||||
| dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion | ||||
| dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface | ||||
| dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始 | ||||
|  | ||||
| dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion | ||||
| dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型 | ||||
| dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 | ||||
|  | ||||
| dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion | ||||
| dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员 | ||||
| dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 | ||||
|  | ||||
| # 符号规范 | ||||
|  | ||||
| dotnet_naming_symbols.interface.applicable_kinds = interface | ||||
| dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected | ||||
| dotnet_naming_symbols.interface.required_modifiers =  | ||||
|  | ||||
| dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum | ||||
| dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected | ||||
| dotnet_naming_symbols.类型.required_modifiers =  | ||||
|  | ||||
| dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method | ||||
| dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected | ||||
| dotnet_naming_symbols.非字段成员.required_modifiers =  | ||||
|  | ||||
| # 命名样式 | ||||
|  | ||||
| dotnet_naming_style.以_i_开始.required_prefix = I | ||||
| dotnet_naming_style.以_i_开始.required_suffix =  | ||||
| dotnet_naming_style.以_i_开始.word_separator =  | ||||
| dotnet_naming_style.以_i_开始.capitalization = pascal_case | ||||
|  | ||||
| dotnet_naming_style.帕斯卡拼写法.required_prefix =  | ||||
| dotnet_naming_style.帕斯卡拼写法.required_suffix =  | ||||
| dotnet_naming_style.帕斯卡拼写法.word_separator =  | ||||
| dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case | ||||
|  | ||||
| dotnet_naming_style.帕斯卡拼写法.required_prefix =  | ||||
| dotnet_naming_style.帕斯卡拼写法.required_suffix =  | ||||
| dotnet_naming_style.帕斯卡拼写法.word_separator =  | ||||
| dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| <Project> | ||||
| 	<PropertyGroup> | ||||
| 		<Version>3.0.0.12</Version> | ||||
| 		<Version>3.0.0.22</Version> | ||||
| 		<LangVersion>latest</LangVersion> | ||||
| 		<TargetFrameworks>net6.0;net7.0</TargetFrameworks> | ||||
| 		<Authors>Diego</Authors> | ||||
|   | ||||
| @@ -101,7 +101,7 @@ public abstract class DriverDebugUIBase : ComponentBase, IDisposable | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             Messages.Add((LogLevel.Error, | ||||
|             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 错误:{ex.Message}")); | ||||
|             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 错误:{ex}")); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| @@ -126,7 +126,7 @@ public abstract class DriverDebugUIBase : ComponentBase, IDisposable | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             Messages.Add((LogLevel.Error, | ||||
|             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 写入前失败:{ex.Message}")); | ||||
|             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 写入前失败:{ex}")); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -49,7 +49,7 @@ public partial class DriverDebugUIPage : DriverDebugUIBase | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + ex.Message)); | ||||
|                     Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + ex)); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|   | ||||
| @@ -116,16 +116,16 @@ | ||||
|  | ||||
|  | ||||
| 	<ItemGroup > | ||||
| 		<PackageReference Include="ThingsGateway.Foundation.Adapter.DLT645" Version="*" /> | ||||
| 		<!--<PackageReference Include="ThingsGateway.Foundation.Adapter.DLT645" Version="*" /> | ||||
| 		<PackageReference Include="ThingsGateway.Foundation.Adapter.Modbus" Version="*" /> | ||||
| 		<PackageReference Include="ThingsGateway.Foundation.Adapter.OPCDA" Version="*" /> | ||||
| 		<PackageReference Include="ThingsGateway.Foundation.Adapter.OPCUA" Version="*" /> | ||||
| 		<PackageReference Include="ThingsGateway.Foundation.Adapter.Siemens" Version="*" /> | ||||
| 		<!--<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj" /> | ||||
| 		<PackageReference Include="ThingsGateway.Foundation.Adapter.Siemens" Version="*" />--> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj" /> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj" /> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCDA\ThingsGateway.Foundation.Adapter.OPCDA.csproj" /> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCUA\ThingsGateway.Foundation.Adapter.OPCUA.csproj" /> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj" />--> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj" /> | ||||
|  | ||||
| 	</ItemGroup> | ||||
| 	<ItemGroup > | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| <Project> | ||||
| 	<PropertyGroup> | ||||
| 		<Version>3.0.0.12</Version> | ||||
| 		<Version>3.0.0.22</Version> | ||||
| 		<GenerateDocumentationFile>True</GenerateDocumentationFile> | ||||
| 		<LangVersion>latest</LangVersion> | ||||
| 		<TargetFrameworks>net45;netstandard2.0;net6.0;net7.0</TargetFrameworks> | ||||
|   | ||||
| @@ -444,7 +444,7 @@ public class DLT645_2007 : ReadWriteDevicesSerialSessionBase | ||||
|  | ||||
|             if (Station.IsNullOrEmpty()) Station = string.Empty; | ||||
|             if (Station.Length < 12) Station = Station.PadLeft(12, '0'); | ||||
|             string str = $"04000C{(level + 1):D2}"; | ||||
|             string str = $"04000C{level + 1:D2}"; | ||||
|  | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WritePassword, | ||||
|                 str.ByHexStringToBytes().Reverse().ToArray() | ||||
|   | ||||
| @@ -92,15 +92,15 @@ public class DLT645_2007Address : DeviceAddressBase | ||||
|         StringBuilder stringGeter = new(); | ||||
|         if (Station.Length > 0) | ||||
|         { | ||||
|             stringGeter.Append("s=" + Station.Reverse().ToArray().ToHexString() + ";"); | ||||
|             stringGeter.Append($"s={Station.Reverse().ToArray().ToHexString()};"); | ||||
|         } | ||||
|         if (DataId.Length > 0) | ||||
|         { | ||||
|             stringGeter.Append(DataId.Reverse().ToArray().ToHexString() + ";"); | ||||
|             stringGeter.Append($"{DataId.Reverse().ToArray().ToHexString()};"); | ||||
|         } | ||||
|         if (!Reverse) | ||||
|         { | ||||
|             stringGeter.Append("s=" + Reverse.ToString() + ";"); | ||||
|             stringGeter.Append($"s={Reverse.ToString()};"); | ||||
|         } | ||||
|         return stringGeter.ToString(); | ||||
|     } | ||||
|   | ||||
| @@ -134,7 +134,7 @@ public class DLT645_2007DataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter | ||||
|  | ||||
|             if ((response[headCodeIndex + 8] != send[sendHeadCodeIndex + 8] + 0x80))//控制码不符合时,返回错误 | ||||
|             { | ||||
|                 request.Message = "返回控制码:" + $"0x{response[headCodeIndex + 8]:X2},请求控制码:" + $"0x{send[sendHeadCodeIndex + 8]:X2},不符合规则"; | ||||
|                 request.Message = $"返回控制码:0x{response[headCodeIndex + 8]:X2},请求控制码:0x{send[sendHeadCodeIndex + 8]:X2},不符合规则"; | ||||
|                 request.ErrorCode = 999; | ||||
|                 return FilterResult.Success; | ||||
|             } | ||||
| @@ -146,7 +146,7 @@ public class DLT645_2007DataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter | ||||
|             { | ||||
|                 byte byte1 = (byte)(response[headCodeIndex + 10] - 0x33); | ||||
|                 var error = DLT645Helper.Get2007ErrorMessage(byte1); | ||||
|                 request.Message = "异常控制码:" + $"0x{response[headCodeIndex + 8]:X2},错误信息:{error}"; | ||||
|                 request.Message = $"异常控制码:0x{response[headCodeIndex + 8]:X2},错误信息:{error}"; | ||||
|                 request.ErrorCode = 999; | ||||
|                 return FilterResult.Success; | ||||
|             } | ||||
|   | ||||
| @@ -147,15 +147,15 @@ public class ModbusAddress : DeviceAddressBase | ||||
|         StringBuilder stringGeter = new(); | ||||
|         if (Station > 0) | ||||
|         { | ||||
|             stringGeter.Append("s=" + Station.ToString() + ";"); | ||||
|             stringGeter.Append($"s={Station.ToString()};"); | ||||
|         } | ||||
|         if (WriteFunction > 0) | ||||
|         { | ||||
|             stringGeter.Append("w=" + WriteFunction.ToString() + ";"); | ||||
|             stringGeter.Append($"w={WriteFunction.ToString()};"); | ||||
|         } | ||||
|         if (!string.IsNullOrEmpty(SocketId)) | ||||
|         { | ||||
|             stringGeter.Append("id=" + SocketId + ";"); | ||||
|             stringGeter.Append($"id={SocketId};"); | ||||
|         } | ||||
|         stringGeter.Append(GetFunctionString(ReadFunction) + (AddressStart + 1).ToString()); | ||||
|         return stringGeter.ToString(); | ||||
|   | ||||
| @@ -117,7 +117,7 @@ internal class ModbusHelper | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[], FilterResult>(ex.Message) { Content2 = FilterResult.Success }; | ||||
|             return new OperResult<byte[], FilterResult>(ex) { Content2 = FilterResult.Success }; | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
| @@ -165,7 +165,7 @@ internal class ModbusHelper | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex.Message); | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
| @@ -185,7 +185,7 @@ internal class ModbusHelper | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex.Message); | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -207,7 +207,7 @@ internal class ModbusHelper | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex.Message); | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
| @@ -244,7 +244,7 @@ internal class ModbusHelper | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex.Message); | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -296,7 +296,7 @@ internal class ModbusHelper | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex.Message); | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -101,9 +101,17 @@ public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase | ||||
|         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     EasyLock easyLock = new(); | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             easyLock.Wait(); | ||||
|  | ||||
|             ModbusAddress mAddress; | ||||
|             try | ||||
|             { | ||||
| @@ -158,6 +166,12 @@ public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase | ||||
|             } | ||||
|             return new OperResult<byte[]>("功能码错误"); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|  | ||||
|             easyLock.Release(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||
| @@ -173,9 +187,13 @@ public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase | ||||
|         SerialSession.SetDataHandlingAdapter(dataHandleAdapter); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             easyLock.Wait(); | ||||
|             ModbusAddress mAddress; | ||||
|             try | ||||
|             { | ||||
| @@ -211,11 +229,21 @@ public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|             } | ||||
|             return new OperResult("功能码错误"); | ||||
|  | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|  | ||||
|             easyLock.Release(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             easyLock.Wait(); | ||||
|             ModbusAddress mAddress; | ||||
|             try | ||||
|             { | ||||
| @@ -254,6 +282,13 @@ public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase | ||||
|                     return (OperResult.CreateSuccessResult()); | ||||
|             } | ||||
|             return new OperResult("功能码错误"); | ||||
|  | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|  | ||||
|             easyLock.Release(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
| @@ -409,13 +444,9 @@ public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase | ||||
|  | ||||
|     private void Init(ModbusAddress mAddress) | ||||
|     { | ||||
|         if (ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         if (ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         if (ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         if (ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||
|         ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||
|         ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||
|         ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -57,7 +57,7 @@ public class ModbusSerialServerDataHandleAdapter : ReadWriteDevicesTcpDataHandle | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex.Message); | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -186,7 +186,7 @@ public class ModbusTcpDtu : ReadWriteDevicesTcpServerBase | ||||
|                 var item = commandResult.Content; | ||||
|                 if (FrameTime != 0) | ||||
|                     Thread.Sleep(FrameTime); | ||||
|                 var WaitingClientEx = client.GetWaitingClient(new() { ThrowBreakException = true }); | ||||
|                 var WaitingClientEx = client.CreateWaitingClient(new() { ThrowBreakException = true }); | ||||
|                 var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken); | ||||
|                 return (MessageBase)result.RequestInfo; | ||||
|             } | ||||
| @@ -213,7 +213,7 @@ public class ModbusTcpDtu : ReadWriteDevicesTcpServerBase | ||||
|  | ||||
|                 var item = commandResult.Content; | ||||
|                 await Task.Delay(FrameTime, cancellationToken); | ||||
|                 var WaitingClientEx = client.GetWaitingClient(new() { ThrowBreakException = true }); | ||||
|                 var WaitingClientEx = client.CreateWaitingClient(new() { ThrowBreakException = true }); | ||||
|                 var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken); | ||||
|                 return (MessageBase)result.RequestInfo; | ||||
|             } | ||||
|   | ||||
| @@ -104,9 +104,16 @@ public class ModbusTcpServer : ReadWriteDevicesTcpServerBase | ||||
|         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     EasyLock easyLock = new(); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             easyLock.Wait(); | ||||
|  | ||||
|             ModbusAddress mAddress; | ||||
|             try | ||||
|             { | ||||
| @@ -161,6 +168,12 @@ public class ModbusTcpServer : ReadWriteDevicesTcpServerBase | ||||
|             } | ||||
|             return new OperResult<byte[]>("功能码错误"); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|  | ||||
|             easyLock.Release(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||
| @@ -193,6 +206,9 @@ public class ModbusTcpServer : ReadWriteDevicesTcpServerBase | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             easyLock.Wait(); | ||||
|             ModbusAddress mAddress; | ||||
|             try | ||||
|             { | ||||
| @@ -228,11 +244,21 @@ public class ModbusTcpServer : ReadWriteDevicesTcpServerBase | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|             } | ||||
|             return new OperResult("功能码错误"); | ||||
|  | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|  | ||||
|             easyLock.Release(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             easyLock.Wait(); | ||||
|             ModbusAddress mAddress; | ||||
|             try | ||||
|             { | ||||
| @@ -271,6 +297,13 @@ public class ModbusTcpServer : ReadWriteDevicesTcpServerBase | ||||
|                     return (OperResult.CreateSuccessResult()); | ||||
|             } | ||||
|             return new OperResult("功能码错误"); | ||||
|  | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|  | ||||
|             easyLock.Release(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
| @@ -429,13 +462,9 @@ public class ModbusTcpServer : ReadWriteDevicesTcpServerBase | ||||
|  | ||||
|     private void Init(ModbusAddress mAddress) | ||||
|     { | ||||
|         if (ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         if (ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         if (ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         if (ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||
|         ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||
|         ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||
|         ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -57,7 +57,7 @@ public class ModbusTcpServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAda | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex.Message); | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -176,7 +176,7 @@ internal class OpcServer : IDisposable | ||||
|                 { | ||||
|                     status = (OPCSERVERSTATUS)o; | ||||
|                     serverStatus = new(); | ||||
|                     serverStatus.Version = status.wMajorVersion.ToString() + "." + status.wMinorVersion.ToString() + "." + status.wBuildNumber.ToString(); | ||||
|                     serverStatus.Version = $"{status.wMajorVersion.ToString()}.{status.wMinorVersion.ToString()}.{status.wBuildNumber.ToString()}"; | ||||
|                     serverStatus.ServerState = status.dwServerState; | ||||
|                     serverStatus.StartTime = Comn.Convert.FileTimeToDateTime(status.ftStartTime); | ||||
|                     serverStatus.CurrentTime = Comn.Convert.FileTimeToDateTime(status.ftCurrentTime); | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| #region copyright | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| @@ -8,6 +9,7 @@ | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| #endregion | ||||
|  | ||||
| using Newtonsoft.Json.Linq; | ||||
| @@ -17,22 +19,23 @@ using Opc.Ua.Client; | ||||
| using Opc.Ua.Client.ComplexTypes; | ||||
| using Opc.Ua.Configuration; | ||||
|  | ||||
|  | ||||
| //修改自https://github.com/dathlin/OpcUaHelper 与OPC基金会net库 | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.OPCUA; | ||||
|  | ||||
| /// <summary> | ||||
| /// 订阅委托 | ||||
| /// </summary> | ||||
| /// <param name="value"></param> | ||||
| public delegate void DataChangedEventHandler((VariableNode variableNode, DataValue dataValue, JToken jToken) value); | ||||
|  | ||||
| /// <summary> | ||||
| /// OPCUAClient | ||||
| /// </summary> | ||||
| public class OPCUAClient : IDisposable | ||||
| { | ||||
|  | ||||
|     #region 属性,变量等 | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 当前配置 | ||||
|     /// </summary> | ||||
| @@ -48,30 +51,35 @@ public class OPCUAClient : IDisposable | ||||
|     /// </summary> | ||||
|     public List<string> Variables = new(); | ||||
|  | ||||
|     private readonly Action<byte, object, string, Exception> _logAction; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 当前的变量名称/OPC变量节点 | ||||
|     /// </summary> | ||||
|     private readonly Dictionary<string, VariableNode> _variableDicts = new(); | ||||
|  | ||||
|     private readonly object checkLock = new(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 当前的订阅组,组名称/组 | ||||
|     /// </summary> | ||||
|     private readonly Dictionary<string, Subscription> dic_subscriptions = new(); | ||||
|  | ||||
|     private readonly ApplicationInstance m_application = new(); | ||||
|  | ||||
|     private readonly ApplicationConfiguration m_configuration; | ||||
|     private SessionReconnectHandler m_reConnectHandler; | ||||
|     private EventHandler m_ReconnectComplete; | ||||
|     private EventHandler m_ReconnectStarting; | ||||
|     private EventHandler<KeepAliveEventArgs> m_KeepAliveComplete; | ||||
|     private EventHandler<bool> m_ConnectComplete; | ||||
|     private EventHandler<OpcUaStatusEventArgs> m_OpcStatusChange; | ||||
|  | ||||
|     private ISession m_session; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 默认的构造函数,实例化一个新的OPC UA类 | ||||
|     /// </summary> | ||||
|     public OPCUAClient(Action<byte, object, string, Exception> log) | ||||
|     public OPCUAClient() | ||||
|     { | ||||
|         _logAction = log; | ||||
|         var certificateValidator = new CertificateValidator(); | ||||
|         certificateValidator.CertificateValidation += CertificateValidation; | ||||
|  | ||||
| @@ -90,7 +98,6 @@ public class OPCUAClient : IDisposable | ||||
|                 MaxMessageQueueSize = 1000000, | ||||
|                 MaxNotificationQueueSize = 1000000, | ||||
|                 MaxPublishRequestCount = 10000000, | ||||
|  | ||||
|             }, | ||||
|  | ||||
|             SecurityConfiguration = new SecurityConfiguration | ||||
| @@ -133,8 +140,6 @@ public class OPCUAClient : IDisposable | ||||
|                     StoreType = CertificateStoreType.Directory, | ||||
|                     StorePath = AppContext.BaseDirectory + @"OPCUAClientCertificate\pki\trustedUser", | ||||
|                 } | ||||
|  | ||||
|  | ||||
|             }, | ||||
|  | ||||
|             TransportQuotas = new TransportQuotas | ||||
| @@ -160,8 +165,6 @@ public class OPCUAClient : IDisposable | ||||
|  | ||||
|         m_configuration.Validate(ApplicationType.Client); | ||||
|         m_application.ApplicationConfiguration = m_configuration; | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -188,6 +191,52 @@ public class OPCUAClient : IDisposable | ||||
|     /// SessionReconnectHandler | ||||
|     /// </summary> | ||||
|     public SessionReconnectHandler ReConnectHandler => m_reConnectHandler; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Raised when a good keep alive from the server arrives. | ||||
|     /// </summary> | ||||
|     public event EventHandler<KeepAliveEventArgs> KeepAliveComplete | ||||
|     { | ||||
|         add { m_KeepAliveComplete += value; } | ||||
|         remove { m_KeepAliveComplete -= value; } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Raised when a reconnect operation starts. | ||||
|     /// </summary> | ||||
|     public event EventHandler ReconnectStarting | ||||
|     { | ||||
|         add { m_ReconnectStarting += value; } | ||||
|         remove { m_ReconnectStarting -= value; } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Raised when a reconnect operation completes. | ||||
|     /// </summary> | ||||
|     public event EventHandler ReconnectComplete | ||||
|     { | ||||
|         add { m_ReconnectComplete += value; } | ||||
|         remove { m_ReconnectComplete -= value; } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Raised after successfully connecting to or disconnecing from a server. | ||||
|     /// </summary> | ||||
|     public event EventHandler<bool> ConnectComplete | ||||
|     { | ||||
|         add { m_ConnectComplete += value; } | ||||
|         remove { m_ConnectComplete -= value; } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Raised after the client status change | ||||
|     /// </summary> | ||||
|     public event EventHandler<OpcUaStatusEventArgs> OpcStatusChange | ||||
|     { | ||||
|         add { m_OpcStatusChange += value; } | ||||
|         remove { m_OpcStatusChange -= value; } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 当前活动会话。 | ||||
|     /// </summary> | ||||
| @@ -231,7 +280,7 @@ public class OPCUAClient : IDisposable | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 _logAction?.Invoke(3, this, $"初始化{items[i]}变量订阅失败", ex); | ||||
|                 UpdateStatus(3, DateTime.Now, $"初始化{items[i]}变量订阅失败,错误原因:{ex}"); | ||||
|             } | ||||
|         } | ||||
|         m_subscription.AddItems(monitoredItems); | ||||
| @@ -247,9 +296,9 @@ public class OPCUAClient : IDisposable | ||||
|         var isError = m_subscription.MonitoredItems.Any(a => a.Status.Error != null && StatusCode.IsBad(a.Status.Error.StatusCode)); | ||||
|         if (isError) | ||||
|         { | ||||
|             _logAction?.Invoke(3, this, $"创建以下变量订阅失败:{Environment.NewLine}{m_subscription.MonitoredItems.Where( | ||||
|             UpdateStatus(3, DateTime.Now, $"创建以下变量订阅失败:{Environment.NewLine}{m_subscription.MonitoredItems.Where( | ||||
|                 a => a.Status.Error != null && StatusCode.IsBad(a.Status.Error.StatusCode)) | ||||
|                 .Select(a => a.StartNodeId.ToString() + ":" + a.Status.Error.ToString()).ToJsonString()}", null); | ||||
|                 .Select(a => $"{a.StartNodeId.ToString()}:{a.Status.Error.ToString()}").ToJsonString()}"); | ||||
|         } | ||||
|  | ||||
|         lock (dic_subscriptions) | ||||
| @@ -281,7 +330,6 @@ public class OPCUAClient : IDisposable | ||||
|                 item.Value.Delete(true); | ||||
|                 m_session.RemoveSubscription(item.Value); | ||||
|                 try { item.Value.Dispose(); } catch { } | ||||
|  | ||||
|             } | ||||
|             dic_subscriptions.Clear(); | ||||
|         } | ||||
| @@ -304,12 +352,13 @@ public class OPCUAClient : IDisposable | ||||
|                 dic_subscriptions.RemoveWhere(a => a.Key == subscriptionName); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private void Callback(MonitoredItem monitoreditem, MonitoredItemNotificationEventArgs monitoredItemNotificationEventArgs) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (m_session != null) | ||||
|             { | ||||
|                 var variableNode = ReadNode(monitoreditem.StartNodeId.ToString(), false); | ||||
|                 foreach (var value in monitoreditem.DequeueValues()) | ||||
| @@ -319,7 +368,7 @@ public class OPCUAClient : IDisposable | ||||
|                         var data = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value); | ||||
|                         if (data == null && value.Value != null) | ||||
|                         { | ||||
|                         _logAction?.Invoke(3, this, $"{monitoreditem.StartNodeId}转换出错,原始值String为{value.Value}", null); | ||||
|                             UpdateStatus(3, DateTime.Now, $"{monitoreditem.StartNodeId}转换出错,原始值String为{value.Value}"); | ||||
|                             var data1 = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value); | ||||
|                         } | ||||
|                         DataChangedHandler?.Invoke((variableNode, value, data)); | ||||
| @@ -329,14 +378,14 @@ public class OPCUAClient : IDisposable | ||||
|                         var data = JValue.CreateNull(); | ||||
|                         DataChangedHandler?.Invoke((variableNode, value, data)); | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             _logAction?.Invoke(3, this, $"{monitoreditem.StartNodeId}订阅处理错误", ex); | ||||
|             UpdateStatus(3, DateTime.Now, $"{monitoreditem.StartNodeId}订阅处理错误,错误原因:" + ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     #endregion | ||||
| @@ -462,8 +511,8 @@ public class OPCUAClient : IDisposable | ||||
|  | ||||
|     #endregion | ||||
|  | ||||
|  | ||||
|     #region 连接 | ||||
|  | ||||
|     private ComplexTypeSystem typeSystem; | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -472,7 +521,6 @@ public class OPCUAClient : IDisposable | ||||
|     public async Task ConnectAsync() | ||||
|     { | ||||
|         await ConnectAsync(OPCNode.OPCUrl); | ||||
|         _logAction?.Invoke(1, this, $"连接成功", null); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -484,10 +532,10 @@ public class OPCUAClient : IDisposable | ||||
|         // disconnect any existing session. | ||||
|         if (m_session != null) | ||||
|         { | ||||
|             _logAction?.Invoke(1, this, $"主动断开连接", null); | ||||
|             m_session = null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Creates a new session. | ||||
|     /// </summary> | ||||
| @@ -524,12 +572,16 @@ public class OPCUAClient : IDisposable | ||||
|         60000, | ||||
|         userIdentity, | ||||
|         Array.Empty<string>()); | ||||
|  | ||||
|         typeSystem = new ComplexTypeSystem(m_session); | ||||
|  | ||||
|         m_session.KeepAliveInterval = OPCNode.KeepAliveInterval == 0 ? 60000 : OPCNode.KeepAliveInterval; | ||||
|         m_session.KeepAlive += new KeepAliveEventHandler(Session_KeepAlive); | ||||
|  | ||||
|         // raise an event. | ||||
|         DoConnectComplete(true); | ||||
|  | ||||
|         UpdateStatus(2, DateTime.UtcNow, "Connected"); | ||||
|  | ||||
|         //如果是订阅模式,连接时添加订阅组 | ||||
|         if (OPCNode.ActiveSubscribe) | ||||
|             await AddSubscriptionAsync(Guid.NewGuid().ToString(), Variables.ToArray(), OPCNode.LoadType); | ||||
| @@ -538,6 +590,9 @@ public class OPCUAClient : IDisposable | ||||
|  | ||||
|     private void PrivateDisconnect() | ||||
|     { | ||||
|         bool state = m_session?.Connected == true; | ||||
|  | ||||
|  | ||||
|         if (m_reConnectHandler != null) | ||||
|         { | ||||
|             try { m_reConnectHandler.Dispose(); } catch { } | ||||
| @@ -549,8 +604,14 @@ public class OPCUAClient : IDisposable | ||||
|             m_session.Close(10000); | ||||
|         } | ||||
|  | ||||
|         if (state) | ||||
|         { | ||||
|             UpdateStatus(2, DateTime.UtcNow, "Disconnected"); | ||||
|             DoConnectComplete(false); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     #endregion | ||||
|  | ||||
|     #region 读取/写入 | ||||
| @@ -593,7 +654,6 @@ public class OPCUAClient : IDisposable | ||||
|                 valuesToWrite.Add(valueToWrite); | ||||
|             } | ||||
|  | ||||
|  | ||||
|             var result = await m_session.WriteAsync( | ||||
|      requestHeader: null, | ||||
|      nodesToWrite: valuesToWrite, cancellationToken); | ||||
| @@ -601,7 +661,6 @@ public class OPCUAClient : IDisposable | ||||
|             ClientBase.ValidateResponse(result.Results, valuesToWrite); | ||||
|             ClientBase.ValidateDiagnosticInfos(result.DiagnosticInfos, valuesToWrite); | ||||
|  | ||||
|  | ||||
|             var keys = writeInfoLists.Keys.ToList(); | ||||
|             for (int i = 0; i < keys.Count; i++) | ||||
|             { | ||||
| @@ -624,7 +683,6 @@ public class OPCUAClient : IDisposable | ||||
|             } | ||||
|             return results; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -706,7 +764,6 @@ public class OPCUAClient : IDisposable | ||||
|         return node; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 从服务器读取节点 | ||||
|     /// </summary> | ||||
| @@ -729,18 +786,14 @@ public class OPCUAClient : IDisposable | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 _logAction?.Invoke(3, this, $"获取服务器节点信息失败{nodes.Item2[i]}", null); | ||||
|                 UpdateStatus(3, DateTime.Now, $"获取服务器节点信息失败{nodes.Item2[i]}"); | ||||
|             } | ||||
|         } | ||||
|         return nodes.Item1.ToList(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     #endregion | ||||
|  | ||||
|  | ||||
|  | ||||
|     #region 特性 | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -805,7 +858,6 @@ public class OPCUAClient : IDisposable | ||||
|                 ResultMask = (uint)BrowseResultMask.All | ||||
|             }; | ||||
|             nodesToBrowse.Add(nodeToBrowse); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return await ReadNoteAttributeAsync(nodesToBrowse, nodesToRead, cancellationToken); | ||||
| @@ -968,11 +1020,9 @@ public class OPCUAClient : IDisposable | ||||
|  | ||||
|         return nodeAttribute.ToArray(); | ||||
|     } | ||||
|  | ||||
|     #endregion | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public void Dispose() | ||||
|     { | ||||
| @@ -991,7 +1041,6 @@ public class OPCUAClient : IDisposable | ||||
|             throw new Exception(string.Format("验证证书失败,错误代码:{0}: {1}", eventArgs.Error.Code, eventArgs.Error.AdditionalInfo)); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private async Task<Dictionary<string, List<OPCNodeAttribute>>> ReadNoteAttributeAsync(BrowseDescriptionCollection nodesToBrowse, ReadValueIdCollection nodesToRead, CancellationToken cancellationToken) | ||||
|     { | ||||
|         int startOfProperties = nodesToRead.Count; | ||||
| @@ -1068,7 +1117,6 @@ public class OPCUAClient : IDisposable | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|  | ||||
|             if (nodeAttributes.ContainsKey(nodeToRead.NodeId.ToString())) | ||||
|             { | ||||
|                 nodeAttributes[nodeToRead.NodeId.ToString()].Add(item); | ||||
| @@ -1085,6 +1133,8 @@ public class OPCUAClient : IDisposable | ||||
|     /// 连接处理器连接事件处理完成。 | ||||
|     /// </summary> | ||||
|     private void Server_ReconnectComplete(object sender, EventArgs e) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (!Object.ReferenceEquals(sender, m_reConnectHandler)) | ||||
|             { | ||||
| @@ -1092,15 +1142,39 @@ public class OPCUAClient : IDisposable | ||||
|             } | ||||
|  | ||||
|             m_session = m_reConnectHandler.Session; | ||||
|             m_reConnectHandler.Dispose(); | ||||
|             m_reConnectHandler = null; | ||||
|  | ||||
|             // raise any additional notifications. | ||||
|             m_ReconnectComplete?.Invoke(this, e); | ||||
|         } | ||||
|         catch (Exception) | ||||
|         { | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Report the client status | ||||
|     /// </summary> | ||||
|     /// <param name="logLevel">Whether the status represents an error. </param> | ||||
|     /// <param name="time">The time associated with the status.</param> | ||||
|     /// <param name="status">The status message.</param> | ||||
|     /// <param name="args">Arguments used to format the status message.</param> | ||||
|     private void UpdateStatus(int logLevel, DateTime time, string status, params object[] args) | ||||
|     { | ||||
|         m_OpcStatusChange?.Invoke(this, new OpcUaStatusEventArgs() | ||||
|         { | ||||
|             LogLevel = logLevel, | ||||
|             Time = time.ToLocalTime(), | ||||
|             Text = String.Format(status, args), | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private void Session_KeepAlive(ISession session, KeepAliveEventArgs e) | ||||
|     { | ||||
|         lock (checkLock) | ||||
|         { | ||||
|  | ||||
|             if (!Object.ReferenceEquals(session, m_session)) | ||||
|             { | ||||
|                 return; | ||||
| @@ -1108,22 +1182,39 @@ public class OPCUAClient : IDisposable | ||||
|  | ||||
|             if (ServiceResult.IsBad(e.Status)) | ||||
|             { | ||||
|                 _logAction?.Invoke(3, this, $"心跳检测错误:{e.Status}", null); | ||||
|  | ||||
|                 if (m_reConnectHandler == null) | ||||
|                 if (m_session.KeepAliveInterval <= 0) | ||||
|                 { | ||||
|                     m_reConnectHandler = new SessionReconnectHandler(); | ||||
|                     m_reConnectHandler.BeginReconnect(m_session, m_session.KeepAliveInterval, Server_ReconnectComplete); | ||||
|                 } | ||||
|  | ||||
|                     UpdateStatus(3, e.CurrentTime, "Communication Error ({0})", e.Status); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 UpdateStatus(3, e.CurrentTime, "Reconnecting in {0}s", m_session.KeepAliveInterval / 1000); | ||||
|  | ||||
|                 if (m_reConnectHandler == null) | ||||
|                 { | ||||
|                     m_ReconnectStarting?.Invoke(this, e); | ||||
|  | ||||
|                     m_reConnectHandler = new SessionReconnectHandler(); | ||||
|                     m_reConnectHandler.BeginReconnect(m_session, m_session.KeepAliveInterval, Server_ReconnectComplete); | ||||
|                 } | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             // update status. | ||||
|             UpdateStatus(0, e.CurrentTime, "Session_KeepAlive Connected [{0}]", session.Endpoint.EndpointUrl); | ||||
|  | ||||
|             // raise any additional notifications. | ||||
|             m_KeepAliveComplete?.Invoke(this, e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Raises the connect complete event on the main GUI thread. | ||||
|     /// </summary> | ||||
|     private void DoConnectComplete(bool state) | ||||
|     { | ||||
|         m_ConnectComplete?.Invoke(this, state); | ||||
|     } | ||||
|  | ||||
|     #endregion | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| #region copyright | ||||
| #region copyright | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| @@ -8,11 +9,39 @@ | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| #endregion | ||||
|  | ||||
| using Opc.Ua; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.OPCUA; | ||||
|  | ||||
| /// <summary> | ||||
| /// OPC UA的状态更新消息 | ||||
| /// </summary> | ||||
| public class OpcUaStatusEventArgs | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 日志等级,<br></br> | ||||
|     /// 更为详细的步骤型日志输出 Trace = 0,<br></br> | ||||
|     /// 调试信息日志Debug = 1,<br></br> | ||||
|     /// 消息类日志输出 Info = 2,<br></br> | ||||
|     /// 警告类日志输出 Warning = 3,<br></br> | ||||
|     /// 错误类日志输出 Error = 4,<br></br> | ||||
|     /// 不可控中断类日输出Critical = 5, | ||||
|     /// </summary> | ||||
|     public int LogLevel { get; set; } | ||||
|     /// <summary> | ||||
|     /// 时间 | ||||
|     /// </summary> | ||||
|     public DateTime Time { get; set; } | ||||
|     /// <summary> | ||||
|     /// 文本 | ||||
|     /// </summary> | ||||
|     public string Text { get; set; } | ||||
|  | ||||
| } | ||||
|  | ||||
| /// <summary> | ||||
| /// 读取属性过程中用于描述的 | ||||
| /// </summary> | ||||
| @@ -22,6 +51,7 @@ public class OPCNodeAttribute | ||||
|     /// 属性的名称 | ||||
|     /// </summary> | ||||
|     public string Name { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 操作结果状态描述 | ||||
|     /// </summary> | ||||
| @@ -31,10 +61,9 @@ public class OPCNodeAttribute | ||||
|     /// 属性的类型描述 | ||||
|     /// </summary> | ||||
|     public string Type { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 属性的值,如果读取错误,返回文本描述 | ||||
|     /// </summary> | ||||
|     public object Value { get; set; } | ||||
|  | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -11,5 +11,4 @@ | ||||
| 		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.4.372.56" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
| </Project> | ||||
|   | ||||
| @@ -250,44 +250,44 @@ public class SiemensAddress : DeviceAddressBase | ||||
|     { | ||||
|         if (DataCode == (byte)S7Area.TM) | ||||
|         { | ||||
|             return "T" + Address.ToString() + (IsWString ? ";W=true;" : ";W=false;"); | ||||
|             return $"T{Address.ToString()}{(IsWString ? ";W=true;" : ";W=false;")}"; | ||||
|         } | ||||
|         if (DataCode == (byte)S7Area.CT) | ||||
|         { | ||||
|             return "C" + Address.ToString() + (IsWString ? ";W=true;" : ";W=false;"); | ||||
|             return $"C{Address.ToString()}{(IsWString ? ";W=true;" : ";W=false;")}"; | ||||
|         } | ||||
|  | ||||
|         if (DataCode == (byte)S7Area.AI) | ||||
|         { | ||||
|             return "AI" + GetStringAddress(AddressStart) + (IsWString ? ";W=true;" : ";W=false;"); | ||||
|             return $"AI{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}"; | ||||
|         } | ||||
|  | ||||
|         if (DataCode == (byte)S7Area.AQ) | ||||
|         { | ||||
|             return "AQ" + GetStringAddress(AddressStart) + (IsWString ? ";W=true;" : ";W=false;"); | ||||
|             return $"AQ{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}"; | ||||
|         } | ||||
|  | ||||
|         if (DataCode == (byte)S7Area.PE) | ||||
|         { | ||||
|             return "I" + GetStringAddress(AddressStart) + (IsWString ? ";W=true;" : ";W=false;"); | ||||
|             return $"I{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}"; | ||||
|         } | ||||
|  | ||||
|         if (DataCode == (byte)S7Area.PA) | ||||
|         { | ||||
|             return "Q" + GetStringAddress(AddressStart) + (IsWString ? ";W=true;" : ";W=false;"); | ||||
|             return $"Q{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}"; | ||||
|         } | ||||
|  | ||||
|         if (DataCode == (byte)S7Area.MK) | ||||
|         { | ||||
|             return "M" + GetStringAddress(AddressStart) + (IsWString ? ";W=true;" : ";W=false;"); | ||||
|             return $"M{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}"; | ||||
|         } | ||||
|  | ||||
|         return DataCode == (byte)S7Area.DB ? "DB" + DbBlock.ToString() + "." + GetStringAddress(AddressStart) + (IsWString ? ";W=true;" : ";W=false;") : Address.ToString() + (IsWString ? ";W=true;" : ";W=false;"); | ||||
|         return DataCode == (byte)S7Area.DB ? $"DB{DbBlock.ToString()}.{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}" : Address.ToString() + (IsWString ? ";W=true;" : ";W=false;"); | ||||
|     } | ||||
|  | ||||
|     private static string GetStringAddress(int addressStart) | ||||
|     { | ||||
|         return addressStart % 8 == 0 ? (addressStart / 8).ToString() : string.Format("{0}.{1}", addressStart / 8, addressStart % 8); | ||||
|         return addressStart % 8 == 0 ? (addressStart / 8).ToString() : $"{addressStart / 8}.{addressStart % 8}"; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -24,7 +24,7 @@ public abstract class ReadWriteDevicesSerialSessionBase : ReadWriteDevicesBase | ||||
|     public ReadWriteDevicesSerialSessionBase(SerialSession serialSession) | ||||
|     { | ||||
|         SerialSession = serialSession; | ||||
|         WaitingClientEx = SerialSession.GetWaitingClient(new() { ThrowBreakException = true }); | ||||
|         WaitingClientEx = SerialSession.CreateWaitingClient(new() { ThrowBreakException = true }); | ||||
|         SerialSession.Received -= Received; | ||||
|         SerialSession.Connecting -= Connecting; | ||||
|         SerialSession.Connected -= Connected; | ||||
| @@ -97,7 +97,7 @@ public abstract class ReadWriteDevicesSerialSessionBase : ReadWriteDevicesBase | ||||
|         try | ||||
|         { | ||||
|             waitingOptions ??= new WaitingOptions { ThrowBreakException = true }; | ||||
|             ResponsedData result = SerialSession.GetWaitingClient(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken); | ||||
|             ResponsedData result = SerialSession.CreateWaitingClient(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken); | ||||
|             return OperResult.CreateSuccessResult(result.Data); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
| @@ -112,7 +112,7 @@ public abstract class ReadWriteDevicesSerialSessionBase : ReadWriteDevicesBase | ||||
|         try | ||||
|         { | ||||
|             waitingOptions ??= new WaitingOptions { ThrowBreakException = true }; | ||||
|             ResponsedData result = await SerialSession.GetWaitingClient(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken); | ||||
|             ResponsedData result = await SerialSession.CreateWaitingClient(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken); | ||||
|             return OperResult.CreateSuccessResult(result.Data); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|   | ||||
| @@ -23,7 +23,7 @@ public abstract class ReadWriteDevicesTcpClientBase : ReadWriteDevicesBase | ||||
|     public ReadWriteDevicesTcpClientBase(TcpClient tcpClient) | ||||
|     { | ||||
|         TcpClient = tcpClient; | ||||
|         WaitingClientEx = TcpClient.GetWaitingClient(new() { ThrowBreakException = true }); | ||||
|         WaitingClientEx = TcpClient.CreateWaitingClient(new() { ThrowBreakException = true }); | ||||
|         TcpClient.Connecting -= Connecting; | ||||
|         TcpClient.Connected -= Connected; | ||||
|         TcpClient.Disconnecting -= Disconnecting; | ||||
| @@ -87,7 +87,7 @@ public abstract class ReadWriteDevicesTcpClientBase : ReadWriteDevicesBase | ||||
|         try | ||||
|         { | ||||
|             waitingOptions ??= new WaitingOptions { ThrowBreakException = true, }; | ||||
|             ResponsedData result = TcpClient.GetWaitingClient(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken); | ||||
|             ResponsedData result = TcpClient.CreateWaitingClient(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken); | ||||
|             return OperResult.CreateSuccessResult(result.Data); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
| @@ -102,7 +102,7 @@ public abstract class ReadWriteDevicesTcpClientBase : ReadWriteDevicesBase | ||||
|         try | ||||
|         { | ||||
|             waitingOptions ??= new WaitingOptions { ThrowBreakException = true }; | ||||
|             ResponsedData result = await TcpClient.GetWaitingClient(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken); | ||||
|             ResponsedData result = await TcpClient.CreateWaitingClient(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken); | ||||
|             return OperResult.CreateSuccessResult(result.Data); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
| @@ -137,13 +137,13 @@ public abstract class ReadWriteDevicesTcpClientBase : ReadWriteDevicesBase | ||||
|  | ||||
|     private Task Disconnected(ITcpClientBase client, DisconnectEventArgs e) | ||||
|     { | ||||
|         Logger?.Debug(client.IP + ":" + client.Port + "断开连接-" + e.Message); | ||||
|         Logger?.Debug($"{client.IP}:{client.Port}断开连接-{e.Message}"); | ||||
|         return EasyTask.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     private Task Disconnecting(ITcpClientBase client, DisconnectEventArgs e) | ||||
|     { | ||||
|         Logger?.Debug(client.IP + ":" + client.Port + "正在主动断开连接-" + e.Message); | ||||
|         Logger?.Debug($"{client.IP}:{client.Port}正在主动断开连接-{e.Message}"); | ||||
|         return EasyTask.CompletedTask; | ||||
|     } | ||||
| } | ||||
| @@ -101,14 +101,14 @@ public abstract class ReadWriteDevicesTcpServerBase : ReadWriteDevicesBase | ||||
|     /// <param name="e"></param> | ||||
|     protected virtual Task Connected(SocketClient client, ConnectedEventArgs e) | ||||
|     { | ||||
|         Logger?.Debug(client.IP + ":" + client.Port + "连接成功"); | ||||
|         Logger?.Debug($"{client.IP}:{client.Port}连接成功"); | ||||
|         return EasyTask.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected virtual Task Connecting(SocketClient client, ConnectingEventArgs e) | ||||
|     { | ||||
|         Logger?.Debug(client.IP + ":" + client.Port + "正在连接"); | ||||
|         Logger?.Debug($"{client.IP}:{client.Port}正在连接"); | ||||
|         SetDataAdapter(client); | ||||
|         return EasyTask.CompletedTask; | ||||
|     } | ||||
| @@ -116,14 +116,14 @@ public abstract class ReadWriteDevicesTcpServerBase : ReadWriteDevicesBase | ||||
|     /// <inheritdoc/> | ||||
|     protected virtual Task Disconnected(ITcpClientBase client, DisconnectEventArgs e) | ||||
|     { | ||||
|         Logger?.Debug(client.IP + ":" + client.Port + "断开连接-" + e.Message); | ||||
|         Logger?.Debug($"{client.IP}:{client.Port}断开连接-{e.Message}"); | ||||
|         return EasyTask.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected virtual Task Disconnecting(ITcpClientBase client, DisconnectEventArgs e) | ||||
|     { | ||||
|         Logger?.Debug(client.IP + ":" + client.Port + "正在主动断开连接-" + e.Message); | ||||
|         Logger?.Debug($"{client.IP}:{client.Port}正在主动断开连接-{e.Message}"); | ||||
|         return EasyTask.CompletedTask; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -22,7 +22,7 @@ public abstract class ReadWriteDevicesUdpSessionBase : ReadWriteDevicesBase | ||||
|     { | ||||
|         UdpSession = udpSession; | ||||
|         SetDataAdapter(); | ||||
|         WaitingClientEx = UdpSession.GetWaitingClient(new() { ThrowBreakException = true }); | ||||
|         WaitingClientEx = UdpSession.CreateWaitingClient(new() { ThrowBreakException = true }); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -65,7 +65,7 @@ public abstract class ReadWriteDevicesUdpSessionBase : ReadWriteDevicesBase | ||||
|         try | ||||
|         { | ||||
|             waitingOptions ??= new WaitingOptions { ThrowBreakException = true }; | ||||
|             ResponsedData result = UdpSession.GetWaitingClient(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken); | ||||
|             ResponsedData result = UdpSession.CreateWaitingClient(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken); | ||||
|             return OperResult.CreateSuccessResult(result.Data); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
| @@ -80,7 +80,7 @@ public abstract class ReadWriteDevicesUdpSessionBase : ReadWriteDevicesBase | ||||
|         try | ||||
|         { | ||||
|             waitingOptions ??= new WaitingOptions { ThrowBreakException = true }; | ||||
|             ResponsedData result = await UdpSession.GetWaitingClient(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken); | ||||
|             ResponsedData result = await UdpSession.CreateWaitingClient(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken); | ||||
|             return OperResult.CreateSuccessResult(result.Data); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|   | ||||
| @@ -20,6 +20,12 @@ public sealed class EasyLock | ||||
|     private static long lockWaitCount; | ||||
|     private readonly SemaphoreSlim m_waiterLock = new SemaphoreSlim(1); | ||||
|     /// <inheritdoc/> | ||||
|     public EasyLock(bool initialState = true) | ||||
|     { | ||||
|         if (!initialState) | ||||
|             m_waiterLock.Wait(); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     ~EasyLock() | ||||
|     { | ||||
|         m_waiterLock.SafeDispose(); | ||||
| @@ -54,11 +60,12 @@ public sealed class EasyLock | ||||
|     /// <summary> | ||||
|     /// 进入锁 | ||||
|     /// </summary> | ||||
|     public void Wait(TimeSpan timeSpan, CancellationToken cancellationToken) | ||||
|     public bool Wait(TimeSpan timeSpan, CancellationToken cancellationToken) | ||||
|     { | ||||
|         Interlocked.Increment(ref lockWaitCount); | ||||
|         m_waiterLock.Wait(timeSpan, cancellationToken); | ||||
|         var data = m_waiterLock.Wait(timeSpan, cancellationToken); | ||||
|         Interlocked.Decrement(ref lockWaitCount); | ||||
|         return data; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -73,11 +80,12 @@ public sealed class EasyLock | ||||
|     /// <summary> | ||||
|     /// 进入锁 | ||||
|     /// </summary> | ||||
|     public async Task WaitAsync(TimeSpan timeSpan, CancellationToken cancellationToken) | ||||
|     public async Task<bool> WaitAsync(TimeSpan timeSpan, CancellationToken cancellationToken) | ||||
|     { | ||||
|         Interlocked.Increment(ref lockWaitCount); | ||||
|         await m_waiterLock.WaitAsync(timeSpan, cancellationToken); | ||||
|         var data = await m_waiterLock.WaitAsync(timeSpan, cancellationToken); | ||||
|         Interlocked.Decrement(ref lockWaitCount); | ||||
|         return data; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -28,7 +28,7 @@ public class ByteTransformUtil | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<TResult>(string.Format("{0} {1} : Length({2}) {3}", "转换失败", result.Content.ToHexString(), result.Content.Length, ex.Message)); | ||||
|             return new OperResult<TResult>(string.Format("{0} {1} : Length({2}) {3}", "转换失败", result.Content.ToHexString(), result.Content.Length, ex)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -32,7 +32,7 @@ public static class JTokenUtil | ||||
|         } | ||||
|         catch (Exception) | ||||
|         { | ||||
|             tagValue = JToken.Parse("\"" + item + "\""); | ||||
|             tagValue = JToken.Parse($"\"{item}\""); | ||||
|         } | ||||
|  | ||||
|         return tagValue; | ||||
|   | ||||
| @@ -27,8 +27,7 @@ using System.Runtime.CompilerServices; | ||||
| namespace ThingsGateway.Foundation.Core | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 具有释放的对象。 | ||||
|     /// 并未实现析构函数相关。 | ||||
|     /// 具有释放的对象。内部实现了GC.SuppressFinalize,但不包括析构函数相关。 | ||||
|     /// </summary> | ||||
|     public partial class DisposableObject : IDisposable | ||||
|     { | ||||
| @@ -65,11 +64,12 @@ namespace ThingsGateway.Foundation.Core | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 释放资源。 | ||||
|         /// 释放资源。内部已经处理了<see cref="GC.SuppressFinalize(object)"/> | ||||
|         /// </summary> | ||||
|         public void Dispose() | ||||
|         { | ||||
|             this.Dispose(disposing: true); | ||||
|             GC.SuppressFinalize(this); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -72,7 +72,7 @@ namespace ThingsGateway.Foundation.Core | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         this.OnError(ex.Message, false, true); | ||||
|                         this.OnError(ex.ToString(), false, true); | ||||
|                     } | ||||
|                 } | ||||
|                 return FilterResult.GoOn; | ||||
| @@ -121,7 +121,7 @@ namespace ThingsGateway.Foundation.Core | ||||
|                         } | ||||
|                         catch (Exception ex) | ||||
|                         { | ||||
|                             this.OnError(ex.Message, false, true); | ||||
|                             this.OnError(ex.ToString(), false, true); | ||||
|                         } | ||||
|                     } | ||||
|                     return FilterResult.GoOn; | ||||
|   | ||||
| @@ -87,7 +87,7 @@ namespace ThingsGateway.Foundation.Core | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 this.OnError(ex.Message); | ||||
|                 this.OnError(ex.ToString()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -182,7 +182,7 @@ namespace ThingsGateway.Foundation.Core | ||||
|             } | ||||
|             if (!flag && !converter.CanConvertTo(destinationType)) | ||||
|             { | ||||
|                 throw new InvalidOperationException("无法转换成类型:" + value.ToString() + "==>" + destinationType); | ||||
|                 throw new InvalidOperationException($"无法转换成类型:{value.ToString()}==>{destinationType}"); | ||||
|             } | ||||
|             try | ||||
|             { | ||||
| @@ -190,7 +190,7 @@ namespace ThingsGateway.Foundation.Core | ||||
|             } | ||||
|             catch (Exception e) | ||||
|             { | ||||
|                 throw new InvalidOperationException(" 类型转换出错:" + value.ToString() + "==>" + destinationType, e); | ||||
|                 throw new InvalidOperationException($" 类型转换出错:{value.ToString()}==>{destinationType}", e); | ||||
|             } | ||||
|             return returnValue; | ||||
|         } | ||||
|   | ||||
| @@ -70,6 +70,15 @@ namespace ThingsGateway.Foundation.Core | ||||
|             return isPeriod; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 累计增加一个计数 | ||||
|         /// </summary> | ||||
|         /// <returns></returns> | ||||
|         public bool Increment() | ||||
|         { | ||||
|             return this.Increment(1); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 重置<see cref="Count"/>和<see cref="LastIncrement"/> | ||||
|         /// </summary> | ||||
|   | ||||
| @@ -47,14 +47,7 @@ namespace ThingsGateway.Foundation.Core | ||||
|         /// </summary> | ||||
|         public TimeSpan Period { get; set; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 重置<see cref="Count"/>和<see cref="LastIncrement"/> | ||||
|         /// </summary> | ||||
|         public void Reset() | ||||
|         { | ||||
|             this.m_count = 0; | ||||
|             this.m_lastIncrement = default; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 累计增加计数 | ||||
| @@ -78,5 +71,26 @@ namespace ThingsGateway.Foundation.Core | ||||
|             Interlocked.Add(ref this.m_count, value); | ||||
|             return isPeriod; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 累计增加一个计数 | ||||
|         /// </summary> | ||||
|         /// <returns></returns> | ||||
|         public bool Increment() | ||||
|         { | ||||
|             return this.Increment(1); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 重置<see cref="Count"/>和<see cref="LastIncrement"/> | ||||
|         /// </summary> | ||||
|         public void Reset() | ||||
|         { | ||||
|             this.m_count = 0; | ||||
|             this.m_lastIncrement = default; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -133,7 +133,7 @@ namespace ThingsGateway.Foundation.Core | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 fileStorage = null; | ||||
|                 msg = ex.Message; | ||||
|                 msg = ex.ToString(); | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -134,7 +134,7 @@ namespace ThingsGateway.Foundation.Core | ||||
|                         string path = null; | ||||
|                         while (true) | ||||
|                         { | ||||
|                             path = Path.Combine(dir, $"{count:0000}" + ".log"); | ||||
|                             path = Path.Combine(dir, $"{count:0000}.log"); | ||||
|                             if (!File.Exists(path)) | ||||
|                             { | ||||
|                                 this.m_writer = FilePool.GetWriter(path); | ||||
|   | ||||
| @@ -292,7 +292,13 @@ public static class LoggerExtensions | ||||
|     { | ||||
|         logger.Log(ThingsGateway.Foundation.Core.LogLevel.Error, null, msg, ex); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 输出错误日志 | ||||
|     /// </summary> | ||||
|     public static void LogError(this ILog logger, Exception ex) | ||||
|     { | ||||
|         logger.Log(ThingsGateway.Foundation.Core.LogLevel.Error, null, ex.Message, ex); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 输出警示日志 | ||||
| @@ -301,7 +307,13 @@ public static class LoggerExtensions | ||||
|     { | ||||
|         logger.Log(ThingsGateway.Foundation.Core.LogLevel.Warning, null, msg, ex); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 输出警示日志 | ||||
|     /// </summary> | ||||
|     public static void LogWarning(this ILog logger, Exception ex) | ||||
|     { | ||||
|         logger.Log(ThingsGateway.Foundation.Core.LogLevel.Warning, null, ex.Message, ex); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 输出警示日志 | ||||
|   | ||||
| @@ -50,13 +50,16 @@ namespace ThingsGateway.Foundation.Dmtp | ||||
|  | ||||
|         #region 字段 | ||||
|  | ||||
|         private ClientWebSocket m_client; | ||||
|         private Func<string, IDmtpActor> m_findDmtpActor; | ||||
|         private ValueCounter m_receiveCounter; | ||||
|         private ValueCounter m_sendCounter; | ||||
|         private SealedDmtpActor m_dmtpActor; | ||||
|         private TcpDmtpAdapter m_smtpAdapter; | ||||
|         private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1); | ||||
|         private ClientWebSocket m_client; | ||||
|         private SealedDmtpActor m_dmtpActor; | ||||
|         private Func<string, IDmtpActor> m_findDmtpActor; | ||||
|         private int m_receiveBufferSize = 1024 * 10; | ||||
|         private ValueCounter m_receiveCounter; | ||||
|         private int m_sendBufferSize = 1024 * 10; | ||||
|         private ValueCounter m_sendCounter; | ||||
|         private TcpDmtpAdapter m_smtpAdapter; | ||||
|  | ||||
|         #endregion 字段 | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
| @@ -90,25 +93,21 @@ namespace ThingsGateway.Foundation.Dmtp | ||||
|         /// <inheritdoc/> | ||||
|         public DateTime LastSendTime => this.m_sendCounter.LastIncrement; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 未实现 | ||||
|         /// </summary> | ||||
|         public Func<ByteBlock, bool> OnHandleRawBuffer { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 未实现 | ||||
|         /// </summary> | ||||
|         public Func<ByteBlock, IRequestInfo, bool> OnHandleReceivedData { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public IPluginsManager PluginsManager { get; private set; } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public Protocol Protocol { get; set; } = DmtpUtility.DmtpProtocol; | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public override int ReceiveBufferSize => this.m_receiveBufferSize; | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public IPHost RemoteIPHost { get; private set; } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public override int SendBufferSize => this.m_sendBufferSize; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送<see cref="IDmtpActor"/>关闭消息。 | ||||
|         /// </summary> | ||||
| @@ -388,12 +387,12 @@ namespace ThingsGateway.Foundation.Dmtp | ||||
|  | ||||
|         private void OnReceivePeriod(long value) | ||||
|         { | ||||
|             this.ReceiveBufferSize = TouchSocketUtility.HitBufferLength(value); | ||||
|             this.m_receiveBufferSize = TouchSocketUtility.HitBufferLength(value); | ||||
|         } | ||||
|  | ||||
|         private void OnSendPeriod(long value) | ||||
|         { | ||||
|             this.SendBufferSize = TouchSocketUtility.HitBufferLength(value); | ||||
|             this.m_sendBufferSize = TouchSocketUtility.HitBufferLength(value); | ||||
|         } | ||||
|  | ||||
|         private void PrivateClose(string msg) | ||||
| @@ -473,8 +472,7 @@ namespace ThingsGateway.Foundation.Dmtp | ||||
|                 { | ||||
|                     task = this.m_client.SendAsync(transferBytes[i], WebSocketMessageType.Binary, false, CancellationToken.None); | ||||
|                 } | ||||
|                 task.ConfigureAwait(false); | ||||
|                 task.GetAwaiter().GetResult(); | ||||
|                 task.GetFalseAwaitResult(); | ||||
|                 this.m_sendCounter.Increment(transferBytes[i].Count); | ||||
|             } | ||||
|         } | ||||
| @@ -522,9 +520,8 @@ namespace ThingsGateway.Foundation.Dmtp | ||||
|         /// <summary> | ||||
|         /// 不支持该功能 | ||||
|         /// </summary> | ||||
|         /// <returns></returns> | ||||
|         /// <exception cref="NotSupportedException"></exception> | ||||
|         public IReceiver CreateReceiver() | ||||
|         public void ClearReceiver() | ||||
|         { | ||||
|             throw new NotSupportedException("不支持该功能"); | ||||
|         } | ||||
| @@ -532,12 +529,13 @@ namespace ThingsGateway.Foundation.Dmtp | ||||
|         /// <summary> | ||||
|         /// 不支持该功能 | ||||
|         /// </summary> | ||||
|         /// <returns></returns> | ||||
|         /// <exception cref="NotSupportedException"></exception> | ||||
|         public void ClearReceiver() | ||||
|         public IReceiver CreateReceiver() | ||||
|         { | ||||
|             throw new NotSupportedException("不支持该功能"); | ||||
|         } | ||||
|  | ||||
|         #endregion | ||||
|         #endregion Receiver | ||||
|     } | ||||
| } | ||||
| @@ -374,7 +374,7 @@ namespace ThingsGateway.Foundation.Http | ||||
|         /// <returns></returns> | ||||
|         public static TResponse SetContentTypeFromFileName<TResponse>(this TResponse response, string fileName) where TResponse : HttpResponse | ||||
|         { | ||||
|             var contentDisposition = "attachment;" + "filename=" + System.Web.HttpUtility.UrlEncode(fileName); | ||||
|             var contentDisposition = $"attachment;filename={System.Web.HttpUtility.UrlEncode(fileName)}"; | ||||
|             response.Headers.Add(HttpHeaders.ContentDisposition, contentDisposition); | ||||
|             return response; | ||||
|         } | ||||
| @@ -429,7 +429,7 @@ namespace ThingsGateway.Foundation.Http | ||||
|             using (var reader = FilePool.GetReader(filePath)) | ||||
|             { | ||||
|                 response.SetContentTypeByExtension(Path.GetExtension(filePath)); | ||||
|                 var contentDisposition = "attachment;" + "filename=" + System.Web.HttpUtility.UrlEncode(fileName ?? Path.GetFileName(filePath)); | ||||
|                 var contentDisposition = $"attachment;filename={System.Web.HttpUtility.UrlEncode(fileName ?? Path.GetFileName(filePath))}"; | ||||
|                 response.Headers.Add(HttpHeaders.ContentDisposition, contentDisposition); | ||||
|                 response.Headers.Add(HttpHeaders.AcceptRanges, "bytes"); | ||||
|  | ||||
| @@ -526,7 +526,7 @@ namespace ThingsGateway.Foundation.Http | ||||
|             using (var reader = FilePool.GetReader(filePath)) | ||||
|             { | ||||
|                 context.Response.SetContentTypeByExtension(Path.GetExtension(filePath)); | ||||
|                 var contentDisposition = "attachment;" + "filename=" + System.Web.HttpUtility.UrlEncode(fileName ?? Path.GetFileName(filePath)); | ||||
|                 var contentDisposition = $"attachment;filename={System.Web.HttpUtility.UrlEncode(fileName ?? Path.GetFileName(filePath))}"; | ||||
|                 context.Response.Headers.Add(HttpHeaders.ContentDisposition, contentDisposition); | ||||
|                 context.Response.Headers.Add(HttpHeaders.AcceptRanges, "bytes"); | ||||
|  | ||||
|   | ||||
| @@ -66,7 +66,7 @@ namespace ThingsGateway.Foundation.Http.WebSockets | ||||
|                             args.Context.Response.Build(byteBlock); | ||||
|                             await client.DefaultSendAsync(byteBlock).ConfigureAwait(false); | ||||
|                         } | ||||
|                         await client.PluginsManager.RaiseAsync(nameof(IWebSocketHandshakedPlugin.OnWebSocketHandshaked), client, new HttpContextEventArgs(httpContext)) | ||||
|                         _ = client.PluginsManager.RaiseAsync(nameof(IWebSocketHandshakedPlugin.OnWebSocketHandshaked), client, new HttpContextEventArgs(httpContext)) | ||||
|                             .ConfigureAwait(false); | ||||
|                         return true; | ||||
|                     } | ||||
|   | ||||
| @@ -101,7 +101,7 @@ namespace ThingsGateway.Foundation.Http.WebSockets | ||||
|                 if (await this.VerifyConnection.Invoke(client, e.Context)) | ||||
|                 { | ||||
|                     e.Handled = true; | ||||
|                     _ = client.SwitchProtocolToWebSocket(e.Context); | ||||
|                     await client.SwitchProtocolToWebSocket(e.Context); | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -130,7 +130,7 @@ namespace ThingsGateway.Foundation.Rpc | ||||
|             catch (TargetInvocationException ex) | ||||
|             { | ||||
|                 invokeResult.Status = InvokeStatus.InvocationException; | ||||
|                 invokeResult.Message = ex.InnerException != null ? "函数内部发生异常,信息:" + ex.InnerException.Message : "函数内部发生异常,信息:未知"; | ||||
|                 invokeResult.Message = ex.InnerException != null ? $"函数内部发生异常,信息:{ex.InnerException.Message}" : "函数内部发生异常,信息:未知"; | ||||
|                 if (callContext.MethodInstance.Filters != null) | ||||
|                 { | ||||
|                     for (var i = 0; i < callContext.MethodInstance.Filters.Length; i++) | ||||
| @@ -223,7 +223,7 @@ namespace ThingsGateway.Foundation.Rpc | ||||
|             catch (TargetInvocationException ex) | ||||
|             { | ||||
|                 invokeResult.Status = InvokeStatus.InvocationException; | ||||
|                 invokeResult.Message = ex.InnerException != null ? "函数内部发生异常,信息:" + ex.InnerException.Message : "函数内部发生异常,信息:未知"; | ||||
|                 invokeResult.Message = ex.InnerException != null ? $"函数内部发生异常,信息:{ex.InnerException.Message}" : "函数内部发生异常,信息:未知"; | ||||
|                 if (callContext.MethodInstance.Filters != null) | ||||
|                 { | ||||
|                     for (var i = 0; i < callContext.MethodInstance.Filters.Length; i++) | ||||
|   | ||||
| @@ -21,22 +21,12 @@ public abstract class BaseSerial : DependencyObject, ISerial | ||||
|     /// 同步根。 | ||||
|     /// </summary> | ||||
|     protected readonly object SyncRoot = new object(); | ||||
|     private int m_receiveBufferSize = 1024 * 10; | ||||
|     private int m_sendBufferSize = 1024 * 10; | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public virtual int SendBufferSize | ||||
|     { | ||||
|         get => this.m_sendBufferSize; | ||||
|         set => this.m_sendBufferSize = value < 1024 ? 1024 : value; | ||||
|     } | ||||
|     public abstract int SendBufferSize { get; } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public virtual int ReceiveBufferSize | ||||
|     { | ||||
|         get => this.m_receiveBufferSize; | ||||
|         set => this.m_receiveBufferSize = value < 1024 ? 1024 : value; | ||||
|     } | ||||
|     public abstract int ReceiveBufferSize { get; } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public ILog Logger { get; set; } | ||||
| @@ -24,9 +24,14 @@ internal sealed class InternalSerialCore : SerialCore | ||||
| public class SerialCore : IDisposable, ISender | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 初始缓存大小 | ||||
|     /// 最小缓存尺寸 | ||||
|     /// </summary> | ||||
|     public const int BufferSize = 1024 * 10; | ||||
|     public int MinBufferSize { get; set; } = 1024 * 10; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 最大缓存尺寸 | ||||
|     /// </summary> | ||||
|     public int MaxBufferSize { get; set; } = 1024 * 1024 * 10; | ||||
|     #region 字段 | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -35,14 +40,15 @@ public class SerialCore : IDisposable, ISender | ||||
|     public readonly object SyncRoot = new object(); | ||||
|  | ||||
|     private long m_bufferRate; | ||||
|     private bool m_disposedValue; | ||||
|     private SpinLock m_lock; | ||||
|     private bool m_online => MainSerialPort?.IsOpen == true; | ||||
|     private int m_receiveBufferSize = BufferSize; | ||||
|     private bool m_online => m_serialPort?.IsOpen == true; | ||||
|     private int m_receiveBufferSize = 1024 * 10; | ||||
|     private ValueCounter m_receiveCounter; | ||||
|     private int m_sendBufferSize = BufferSize; | ||||
|     private int m_sendBufferSize = 1024 * 10; | ||||
|     private ValueCounter m_sendCounter; | ||||
|     private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1); | ||||
|     private readonly EasyLock m_semaphore = new EasyLock(); | ||||
|     private SerialPort m_serialPort; | ||||
|  | ||||
|     #endregion 字段 | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -69,7 +75,7 @@ public class SerialCore : IDisposable, ISender | ||||
|     /// </summary> | ||||
|     ~SerialCore() | ||||
|     { | ||||
|         this.Dispose(disposing: false); | ||||
|         this.SafeDispose(); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
| @@ -100,19 +106,11 @@ public class SerialCore : IDisposable, ISender | ||||
|     public Action<SerialCore, ByteBlock> OnReceived { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 接收缓存池(可以设定初始值,运行时的值会根据流速自动调整) | ||||
|     /// 接收缓存池,运行时的值会根据流速自动调整 | ||||
|     /// </summary> | ||||
|     public int ReceiveBufferSize | ||||
|     { | ||||
|         get => this.m_receiveBufferSize; | ||||
|         set | ||||
|         { | ||||
|             this.m_receiveBufferSize = value; | ||||
|             if (this.MainSerialPort != null && !MainSerialPort.IsOpen) | ||||
|             { | ||||
|                 this.MainSerialPort.ReadBufferSize = value; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -121,19 +119,11 @@ public class SerialCore : IDisposable, ISender | ||||
|     public ValueCounter ReceiveCounter { get => this.m_receiveCounter; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 发送缓存池(可以设定初始值,运行时的值会根据流速自动调整) | ||||
|     /// 发送缓存池,运行时的值会根据流速自动调整 | ||||
|     /// </summary> | ||||
|     public int SendBufferSize | ||||
|     { | ||||
|         get => this.m_sendBufferSize; | ||||
|         set | ||||
|         { | ||||
|             this.m_sendBufferSize = value; | ||||
|             if (this.MainSerialPort != null && !MainSerialPort.IsOpen) | ||||
|             { | ||||
|                 this.MainSerialPort.WriteBufferSize = value; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -144,7 +134,7 @@ public class SerialCore : IDisposable, ISender | ||||
|     /// <summary> | ||||
|     /// SerialPort | ||||
|     /// </summary> | ||||
|     public SerialPort MainSerialPort { get; private set; } | ||||
|     public SerialPort MainSerialPort { get => this.m_serialPort; } | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -155,11 +145,12 @@ public class SerialCore : IDisposable, ISender | ||||
|         var byteBlock = BytePool.Default.GetByteBlock(this.ReceiveBufferSize); | ||||
|         this.UserToken = byteBlock; | ||||
|         byteBlock.SetLength(0); | ||||
|         if (this.MainSerialPort.BytesToRead > 0) | ||||
|         if (this.m_serialPort.BytesToRead > 0) | ||||
|         { | ||||
|             this.m_bufferRate += 2; | ||||
|             this.ProcessReceived(); | ||||
|         } | ||||
|         MainSerialPort.DataReceived += MainSerialPort_DataReceived; | ||||
|         m_serialPort.DataReceived += MainSerialPort_DataReceived; | ||||
|     } | ||||
|  | ||||
|     private void MainSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) | ||||
| @@ -171,7 +162,7 @@ public class SerialCore : IDisposable, ISender | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             this.PrivateBreakOut(false, ex.Message); | ||||
|             this.PrivateBreakOut(false, ex.ToString()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -189,12 +180,12 @@ public class SerialCore : IDisposable, ISender | ||||
|     /// </summary> | ||||
|     public void Dispose() | ||||
|     { | ||||
|         this.Dispose(disposing: true); | ||||
|         GC.SuppressFinalize(this); | ||||
|         UserToken.SafeDispose(); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 重置环境,并设置新的<see cref="MainSerialPort"/>。 | ||||
|     /// 重置环境,并设置新的<see cref="m_serialPort"/>。 | ||||
|     /// </summary> | ||||
|     /// <param name="socket"></param> | ||||
|     public virtual void Reset(SerialPort socket) | ||||
| @@ -209,7 +200,7 @@ public class SerialCore : IDisposable, ISender | ||||
|             throw new Exception("新的SerialPort必须在连接状态。"); | ||||
|         } | ||||
|         this.Reset(); | ||||
|         this.MainSerialPort = socket; | ||||
|         this.m_serialPort = socket; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -219,14 +210,14 @@ public class SerialCore : IDisposable, ISender | ||||
|     { | ||||
|         this.m_receiveCounter.Reset(); | ||||
|         this.m_sendCounter.Reset(); | ||||
|         this.MainSerialPort = null; | ||||
|         this.m_serialPort = null; | ||||
|         this.OnReceived = null; | ||||
|         this.OnBreakOut = null; | ||||
|         this.UserToken = null; | ||||
|         this.m_bufferRate = 1; | ||||
|         this.m_lock = new SpinLock(); | ||||
|         this.m_receiveBufferSize = BufferSize; | ||||
|         this.m_sendBufferSize = BufferSize; | ||||
|         this.m_receiveBufferSize = this.MinBufferSize; | ||||
|         this.m_sendBufferSize = this.MinBufferSize; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -244,7 +235,7 @@ public class SerialCore : IDisposable, ISender | ||||
|         try | ||||
|         { | ||||
|             this.m_lock.Enter(ref lockTaken); | ||||
|             this.MainSerialPort.Write(buffer, offset, length); | ||||
|             this.m_serialPort.Write(buffer, offset, length); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
| @@ -267,7 +258,7 @@ public class SerialCore : IDisposable, ISender | ||||
|         { | ||||
|             await this.m_semaphore.WaitAsync(); | ||||
|  | ||||
|             this.MainSerialPort.Write(buffer, offset, length); | ||||
|             this.m_serialPort.Write(buffer, offset, length); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
| @@ -287,22 +278,7 @@ public class SerialCore : IDisposable, ISender | ||||
|         this.OnBreakOut?.Invoke(this, manual, msg); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 释放对象 | ||||
|     /// </summary> | ||||
|     /// <param name="disposing"></param> | ||||
|     protected virtual void Dispose(bool disposing) | ||||
|     { | ||||
|         if (!this.m_disposedValue) | ||||
|         { | ||||
|             if (disposing) | ||||
|             { | ||||
|             } | ||||
|  | ||||
|             this.m_disposedValue = true; | ||||
|         } | ||||
|         UserToken.SafeDispose(); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 当发生异常的时候 | ||||
| @@ -341,12 +317,20 @@ public class SerialCore : IDisposable, ISender | ||||
|  | ||||
|     private void OnReceivePeriod(long value) | ||||
|     { | ||||
|         this.ReceiveBufferSize = TouchSocketUtility.HitBufferLength(value); | ||||
|         this.m_receiveBufferSize = Math.Max(TouchSocketUtility.HitBufferLength(value), this.MinBufferSize); | ||||
|         if (this.MainSerialPort != null && !MainSerialPort.IsOpen) | ||||
|         { | ||||
|             this.MainSerialPort.ReadBufferSize = this.m_receiveBufferSize; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void OnSendPeriod(long value) | ||||
|     { | ||||
|         this.SendBufferSize = TouchSocketUtility.HitBufferLength(value); | ||||
|         this.m_sendBufferSize = Math.Max(TouchSocketUtility.HitBufferLength(value), this.MinBufferSize); | ||||
|         if (this.MainSerialPort != null && !MainSerialPort.IsOpen) | ||||
|         { | ||||
|             this.MainSerialPort.WriteBufferSize = this.m_sendBufferSize; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void PrivateBreakOut(bool manual, string msg) | ||||
| @@ -367,21 +351,21 @@ public class SerialCore : IDisposable, ISender | ||||
|             UserToken?.SafeDispose(); | ||||
|             return; | ||||
|         } | ||||
|         if (MainSerialPort.BytesToRead > 0) | ||||
|         if (m_serialPort.BytesToRead > 0) | ||||
|         { | ||||
|             var byteBlock = UserToken; | ||||
|             byte[] buffer = BytePool.Default.Rent(MainSerialPort.BytesToRead); | ||||
|             int num = MainSerialPort.Read(buffer, 0, MainSerialPort.BytesToRead); | ||||
|             byte[] buffer = BytePool.Default.Rent(m_serialPort.BytesToRead); | ||||
|             int num = m_serialPort.Read(buffer, 0, m_serialPort.BytesToRead); | ||||
|             byteBlock.Write(buffer, 0, num); | ||||
|             byteBlock.SetLength(num); | ||||
|             this.HandleBuffer(byteBlock); | ||||
|             try | ||||
|             { | ||||
|                 var newByteBlock = BytePool.Default.GetByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, TouchSocketUtility.MaxBufferLength)); | ||||
|                 var newByteBlock = BytePool.Default.GetByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, this.MaxBufferSize)); | ||||
|                 newByteBlock.SetLength(0); | ||||
|                 UserToken = newByteBlock; | ||||
|  | ||||
|                 if (MainSerialPort.BytesToRead > 0) | ||||
|                 if (m_serialPort.BytesToRead > 0) | ||||
|                 { | ||||
|                     this.m_bufferRate += 2; | ||||
|                     this.ProcessReceived(); | ||||
| @@ -389,7 +373,7 @@ public class SerialCore : IDisposable, ISender | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 this.PrivateBreakOut(false, ex.Message); | ||||
|                 this.PrivateBreakOut(false, ex.ToString()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -57,7 +57,7 @@ public class SerialSessionBase : BaseSerial, ISerialSession | ||||
|  | ||||
|     private DelaySender m_delaySender; | ||||
|     private bool m_online => MainSerialPort?.IsOpen == true; | ||||
|     private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1); | ||||
|     private readonly EasyLock m_semaphore = new(); | ||||
|     private readonly InternalSerialCore m_serialCore; | ||||
|     #endregion 变量 | ||||
|  | ||||
| @@ -204,10 +204,10 @@ public class SerialSessionBase : BaseSerial, ISerialSession | ||||
|     #region 属性 | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public DateTime LastReceivedTime => this.GetTcpCore().ReceiveCounter.LastIncrement; | ||||
|     public DateTime LastReceivedTime => this.GetSerialCore().ReceiveCounter.LastIncrement; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public DateTime LastSendTime => this.GetTcpCore().SendCounter.LastIncrement; | ||||
|     public DateTime LastSendTime => this.GetSerialCore().SendCounter.LastIncrement; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public IContainer Container { get; private set; } | ||||
| @@ -321,7 +321,7 @@ public class SerialSessionBase : BaseSerial, ISerialSession | ||||
|  | ||||
|     private void BeginReceive() | ||||
|     { | ||||
|         this.GetTcpCore().BeginIocpReceive(); | ||||
|         this.GetSerialCore().BeginIocpReceive(); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -375,32 +375,22 @@ public class SerialSessionBase : BaseSerial, ISerialSession | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private SerialCore GetTcpCore() | ||||
|     private SerialCore GetSerialCore() | ||||
|     { | ||||
|         this.ThrowIfDisposed(); | ||||
|         return this.m_serialCore ?? throw new ObjectDisposedException(this.GetType().Name); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override int ReceiveBufferSize | ||||
|     { | ||||
|         get => this.GetTcpCore().ReceiveBufferSize; | ||||
|         set | ||||
|         { | ||||
|             this.GetTcpCore().ReceiveBufferSize = value; | ||||
|         } | ||||
|         get => this.GetSerialCore().ReceiveBufferSize; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override int SendBufferSize | ||||
|     { | ||||
|         get => this.GetTcpCore().SendBufferSize; | ||||
|         set | ||||
|         { | ||||
|             this.GetTcpCore().SendBufferSize = value; | ||||
|         } | ||||
|         get => this.GetSerialCore().SendBufferSize; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
| @@ -728,7 +718,7 @@ public class SerialSessionBase : BaseSerial, ISerialSession | ||||
|     { | ||||
|         if (this.SendingData(buffer, offset, length).GetFalseAwaitResult()) | ||||
|         { | ||||
|             this.GetTcpCore().Send(buffer, offset, length); | ||||
|             this.GetSerialCore().Send(buffer, offset, length); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -737,7 +727,7 @@ public class SerialSessionBase : BaseSerial, ISerialSession | ||||
|     { | ||||
|         if (await this.SendingData(buffer, offset, length)) | ||||
|         { | ||||
|             await this.GetTcpCore().SendAsync(buffer, offset, length); | ||||
|             await this.GetSerialCore().SendAsync(buffer, offset, length); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -770,7 +760,15 @@ public class SerialSessionBase : BaseSerial, ISerialSession | ||||
|         this.m_serialCore.Reset(serialPort); | ||||
|         this.m_serialCore.OnReceived = this.HandleReceived; | ||||
|         this.m_serialCore.OnBreakOut = this.BreakOut; | ||||
|         if (this.Config.GetValue(TouchSocketConfigExtension.MinBufferSizeProperty) is int minValue) | ||||
|         { | ||||
|             this.m_serialCore.MinBufferSize = minValue; | ||||
|         } | ||||
|  | ||||
|         if (this.Config.GetValue(TouchSocketConfigExtension.MaxBufferSizeProperty) is int maxValue) | ||||
|         { | ||||
|             this.m_serialCore.MaxBufferSize = maxValue; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void HandleReceived(SerialCore core, ByteBlock byteBlock) | ||||
|   | ||||
| @@ -35,22 +35,11 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|         /// </summary> | ||||
|         protected readonly object SyncRoot = new object(); | ||||
|  | ||||
|         private int m_receiveBufferSize = 1024 * 10; | ||||
|         private int m_sendBufferSize = 1024 * 10; | ||||
|         /// <inheritdoc/> | ||||
|         public abstract int SendBufferSize { get; } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public virtual int SendBufferSize | ||||
|         { | ||||
|             get => this.m_sendBufferSize; | ||||
|             set => this.m_sendBufferSize = value < 1024 ? 1024 : value; | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public virtual int ReceiveBufferSize | ||||
|         { | ||||
|             get => this.m_receiveBufferSize; | ||||
|             set => this.m_receiveBufferSize = value < 1024 ? 1024 : value; | ||||
|         } | ||||
|         public abstract int ReceiveBufferSize { get; } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public ILog Logger { get; set; } | ||||
|   | ||||
| @@ -108,10 +108,6 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 最大BufferLength | ||||
|         /// </summary> | ||||
|         public static int MaxBufferLength { get; set; } = 1024 * 1024 * 10; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 命中BufferLength | ||||
|   | ||||
| @@ -24,9 +24,15 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|     private const string m_msg1 = "远程终端主动关闭"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 初始缓存大小 | ||||
|     /// 最小缓存尺寸 | ||||
|     /// </summary> | ||||
|     public const int BufferSize = 1024 * 10; | ||||
|     public int MinBufferSize { get; set; } = 1024 * 10; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 最大缓存尺寸 | ||||
|     /// </summary> | ||||
|     public int MaxBufferSize { get; set; } = 1024 * 1024 * 10; | ||||
|  | ||||
|     #region 字段 | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -35,14 +41,14 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|     public readonly object SyncRoot = new object(); | ||||
|  | ||||
|     private long m_bufferRate; | ||||
|     private bool m_disposedValue; | ||||
|     private SpinLock m_lock; | ||||
|     private volatile bool m_online; | ||||
|     private int m_receiveBufferSize = BufferSize; | ||||
|     private int m_receiveBufferSize = 1024 * 10; | ||||
|     private ValueCounter m_receiveCounter; | ||||
|     private int m_sendBufferSize = BufferSize; | ||||
|     private int m_sendBufferSize = 1024 * 10; | ||||
|     private ValueCounter m_sendCounter; | ||||
|     private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1); | ||||
|     private Socket m_socket; | ||||
|     private readonly EasyLock m_semaphore = new(); | ||||
|     #endregion 字段 | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -64,14 +70,6 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 析构函数 | ||||
|     /// </summary> | ||||
|     ~TcpCore() | ||||
|     { | ||||
|         this.Dispose(disposing: false); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public bool CanSend => this.m_online; | ||||
|  | ||||
| @@ -96,16 +94,11 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|     public Action<TcpCore, ByteBlock> OnReceived { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 接收缓存池(可以设定初始值,运行时的值会根据流速自动调整) | ||||
|     /// 接收缓存池,运行时的值会根据流速自动调整 | ||||
|     /// </summary> | ||||
|     public int ReceiveBufferSize | ||||
|     { | ||||
|         get => this.m_receiveBufferSize; | ||||
|         set | ||||
|         { | ||||
|             this.m_receiveBufferSize = value; | ||||
|             this.Socket.ReceiveBufferSize = value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -114,16 +107,11 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|     public ValueCounter ReceiveCounter { get => this.m_receiveCounter; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 发送缓存池(可以设定初始值,运行时的值会根据流速自动调整) | ||||
|     /// 发送缓存池,运行时的值会根据流速自动调整 | ||||
|     /// </summary> | ||||
|     public int SendBufferSize | ||||
|     { | ||||
|         get => this.m_sendBufferSize; | ||||
|         set | ||||
|         { | ||||
|             this.m_sendBufferSize = value; | ||||
|             this.Socket.SendBufferSize = value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -134,7 +122,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|     /// <summary> | ||||
|     /// Socket | ||||
|     /// </summary> | ||||
|     public Socket Socket { get; private set; } | ||||
|     public Socket Socket { get => this.m_socket; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 提供一个用于客户端-服务器通信的流,该流使用安全套接字层 (SSL) 安全协议对服务器和(可选)客户端进行身份验证。 | ||||
| @@ -152,7 +140,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|     /// <param name="sslOption"></param> | ||||
|     public virtual void Authenticate(ServiceSslOption sslOption) | ||||
|     { | ||||
|         var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false); | ||||
|         var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.m_socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.m_socket, false), false); | ||||
|         sslStream.AuthenticateAsServer(sslOption.Certificate); | ||||
|  | ||||
|         this.SslStream = sslStream; | ||||
| @@ -165,7 +153,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|     /// <param name="sslOption"></param> | ||||
|     public virtual void Authenticate(ClientSslOption sslOption) | ||||
|     { | ||||
|         var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false); | ||||
|         var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.m_socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.m_socket, false), false); | ||||
|         if (sslOption.ClientCertificates == null) | ||||
|         { | ||||
|             sslStream.AuthenticateAsClient(sslOption.TargetHost); | ||||
| @@ -185,7 +173,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|     /// <returns></returns> | ||||
|     public virtual async Task AuthenticateAsync(ServiceSslOption sslOption) | ||||
|     { | ||||
|         var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false); | ||||
|         var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.m_socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.m_socket, false), false); | ||||
|         await sslStream.AuthenticateAsServerAsync(sslOption.Certificate); | ||||
|  | ||||
|         this.SslStream = sslStream; | ||||
| @@ -199,7 +187,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|     /// <returns></returns> | ||||
|     public virtual async Task AuthenticateAsync(ClientSslOption sslOption) | ||||
|     { | ||||
|         var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false); | ||||
|         var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.m_socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.m_socket, false), false); | ||||
|         if (sslOption.ClientCertificates == null) | ||||
|         { | ||||
|             await sslStream.AuthenticateAsClientAsync(sslOption.TargetHost); | ||||
| @@ -220,8 +208,9 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|         var byteBlock = BytePool.Default.GetByteBlock(this.ReceiveBufferSize); | ||||
|         this.UserToken = byteBlock; | ||||
|         this.SetBuffer(byteBlock.Buffer, 0, byteBlock.Capacity); | ||||
|         if (!this.Socket.ReceiveAsync(this)) | ||||
|         if (!this.m_socket.ReceiveAsync(this)) | ||||
|         { | ||||
|             this.m_bufferRate += 2; | ||||
|             this.ProcessReceived(this); | ||||
|         } | ||||
|     } | ||||
| @@ -257,7 +246,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 byteBlock.Dispose(); | ||||
|                 this.PrivateBreakOut(false, ex.Message); | ||||
|                 this.PrivateBreakOut(false, ex.ToString()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -271,15 +260,6 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|         this.PrivateBreakOut(true, msg); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 释放对象 | ||||
|     /// </summary> | ||||
|     public new void Dispose() | ||||
|     { | ||||
|         // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 | ||||
|         this.Dispose(disposing: true); | ||||
|         GC.SuppressFinalize(this); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 重置环境,并设置新的<see cref="Socket"/>。 | ||||
| @@ -298,7 +278,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|         } | ||||
|         this.Reset(); | ||||
|         this.m_online = true; | ||||
|         this.Socket = socket; | ||||
|         this.m_socket = socket; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -310,14 +290,14 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|         this.m_sendCounter.Reset(); | ||||
|         this.SslStream?.Dispose(); | ||||
|         this.SslStream = null; | ||||
|         this.Socket = null; | ||||
|         this.m_socket = null; | ||||
|         this.OnReceived = null; | ||||
|         this.OnBreakOut = null; | ||||
|         this.UserToken = null; | ||||
|         this.m_bufferRate = 1; | ||||
|         this.m_lock = new SpinLock(); | ||||
|         this.m_receiveBufferSize = BufferSize; | ||||
|         this.m_sendBufferSize = BufferSize; | ||||
|         this.m_receiveBufferSize = this.MinBufferSize; | ||||
|         this.m_sendBufferSize = this.MinBufferSize; | ||||
|         this.m_online = false; | ||||
|     } | ||||
|  | ||||
| @@ -344,7 +324,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|                 this.m_lock.Enter(ref lockTaken); | ||||
|                 while (length > 0) | ||||
|                 { | ||||
|                     var r = this.Socket.Send(buffer, offset, length, SocketFlags.None); | ||||
|                     var r = this.m_socket.Send(buffer, offset, length, SocketFlags.None); | ||||
|                     if (r == 0 && length > 0) | ||||
|                     { | ||||
|                         throw new Exception("发送数据不完全"); | ||||
| @@ -387,7 +367,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|  | ||||
|                 while (length > 0) | ||||
|                 { | ||||
|                     var r = await this.Socket.SendAsync(new ArraySegment<byte>(buffer, offset, length), SocketFlags.None, CancellationToken.None); | ||||
|                     var r = await this.m_socket.SendAsync(new ArraySegment<byte>(buffer, offset, length), SocketFlags.None, CancellationToken.None); | ||||
|                     if (r == 0 && length > 0) | ||||
|                     { | ||||
|                         throw new Exception("发送数据不完全"); | ||||
| @@ -414,7 +394,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|  | ||||
|                 while (length > 0) | ||||
|                 { | ||||
|                     var r = this.Socket.Send(buffer, offset, length, SocketFlags.None); | ||||
|                     var r = this.m_socket.Send(buffer, offset, length, SocketFlags.None); | ||||
|                     if (r == 0 && length > 0) | ||||
|                     { | ||||
|                         throw new Exception("发送数据不完全"); | ||||
| @@ -443,23 +423,6 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|         this.OnBreakOut?.Invoke(this, manual, msg); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 释放对象 | ||||
|     /// </summary> | ||||
|     /// <param name="disposing"></param> | ||||
|     protected virtual void Dispose(bool disposing) | ||||
|     { | ||||
|         if (!this.m_disposedValue) | ||||
|         { | ||||
|             if (disposing) | ||||
|             { | ||||
|             } | ||||
|  | ||||
|             this.m_disposedValue = true; | ||||
|         } | ||||
|         base.Dispose(); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 当发生异常的时候 | ||||
|     /// </summary> | ||||
| @@ -481,7 +444,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 this.PrivateBreakOut(false, ex.Message); | ||||
|                 this.PrivateBreakOut(false, ex.ToString()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -514,12 +477,20 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|  | ||||
|     private void OnReceivePeriod(long value) | ||||
|     { | ||||
|         this.ReceiveBufferSize = TouchSocketUtility.HitBufferLength(value); | ||||
|         this.m_receiveBufferSize = Math.Max(TouchSocketUtility.HitBufferLength(value), this.MinBufferSize); | ||||
|         if (this.m_socket != null) | ||||
|         { | ||||
|             this.m_socket.ReceiveBufferSize = this.m_receiveBufferSize; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void OnSendPeriod(long value) | ||||
|     { | ||||
|         this.SendBufferSize = TouchSocketUtility.HitBufferLength(value); | ||||
|         this.m_sendBufferSize = Math.Max(TouchSocketUtility.HitBufferLength(value), this.MinBufferSize); | ||||
|         if (this.m_socket != null) | ||||
|         { | ||||
|             this.m_socket.SendBufferSize = this.m_sendBufferSize; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void PrivateBreakOut(bool manual, string msg) | ||||
| @@ -548,11 +519,11 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|             this.HandleBuffer(byteBlock); | ||||
|             try | ||||
|             { | ||||
|                 var newByteBlock = BytePool.Default.GetByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, TouchSocketUtility.MaxBufferLength)); | ||||
|                 var newByteBlock = BytePool.Default.GetByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, this.MaxBufferSize)); | ||||
|                 e.UserToken = newByteBlock; | ||||
|                 e.SetBuffer(newByteBlock.Buffer, 0, newByteBlock.Capacity); | ||||
|  | ||||
|                 if (!this.Socket.ReceiveAsync(e)) | ||||
|                 if (!this.m_socket.ReceiveAsync(e)) | ||||
|                 { | ||||
|                     this.m_bufferRate += 2; | ||||
|                     this.ProcessReceived(e); | ||||
| @@ -560,7 +531,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 this.PrivateBreakOut(false, ex.Message); | ||||
|                 this.PrivateBreakOut(false, ex.ToString()); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|   | ||||
| @@ -149,7 +149,7 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 this.BreakOut(default, false, ex.Message); | ||||
|                 this.BreakOut(default, false, ex.ToString()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -227,6 +227,15 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|             tcpCore.Reset(socket); | ||||
|             tcpCore.OnReceived = this.HandleReceived; | ||||
|             tcpCore.OnBreakOut = this.BreakOut; | ||||
|             if (this.Config.GetValue(TouchSocketConfigExtension.MinBufferSizeProperty) is int minValue) | ||||
|             { | ||||
|                 tcpCore.MinBufferSize = minValue; | ||||
|             } | ||||
|  | ||||
|             if (this.Config.GetValue(TouchSocketConfigExtension.MaxBufferSizeProperty) is int maxValue) | ||||
|             { | ||||
|                 tcpCore.MaxBufferSize = maxValue; | ||||
|             } | ||||
|             this.m_tcpCore = tcpCore; | ||||
|         } | ||||
|  | ||||
| @@ -398,20 +407,12 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|         public override int ReceiveBufferSize | ||||
|         { | ||||
|             get => this.GetTcpCore().ReceiveBufferSize; | ||||
|             set | ||||
|             { | ||||
|                 this.GetTcpCore().ReceiveBufferSize = value; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public override int SendBufferSize | ||||
|         { | ||||
|             get => this.GetTcpCore().SendBufferSize; | ||||
|             set | ||||
|             { | ||||
|                 this.GetTcpCore().SendBufferSize = value; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|   | ||||
| @@ -71,7 +71,7 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|  | ||||
|         private DelaySender m_delaySender; | ||||
|         private volatile bool m_online; | ||||
|         private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1); | ||||
|         private readonly EasyLock m_semaphore = new(); | ||||
|         private readonly InternalTcpCore m_tcpCore; | ||||
|         #endregion 变量 | ||||
|  | ||||
| @@ -334,10 +334,7 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|                 var iPHost = this.Config.GetValue(TouchSocketConfigExtension.RemoteIPHostProperty) ?? throw new ArgumentNullException(nameof(IPHost), "iPHost不能为空。"); | ||||
|                 this.MainSocket.SafeDispose(); | ||||
|                 var socket = this.CreateSocket(iPHost); | ||||
|                 this.PrivateOnConnecting(new ConnectingEventArgs(socket)) | ||||
|                     .ConfigureAwait(false) | ||||
|                     .GetAwaiter() | ||||
|                     .GetResult(); | ||||
|                 this.PrivateOnConnecting(new ConnectingEventArgs(socket)).GetFalseAwaitResult(); | ||||
|                 if (timeout == 5000) | ||||
|                 { | ||||
|                     socket.Connect(iPHost.Host, iPHost.Port); | ||||
| @@ -500,6 +497,11 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|  | ||||
|         #endregion | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public override string ToString() | ||||
|         { | ||||
|             return this.GetIPPort(); | ||||
|         } | ||||
|         private void BreakOut(TcpCore core, bool manual, string msg) | ||||
|         { | ||||
|             lock (this.SyncRoot) | ||||
| @@ -526,20 +528,12 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|         public override int ReceiveBufferSize | ||||
|         { | ||||
|             get => this.GetTcpCore().ReceiveBufferSize; | ||||
|             set | ||||
|             { | ||||
|                 this.GetTcpCore().ReceiveBufferSize = value; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public override int SendBufferSize | ||||
|         { | ||||
|             get => this.GetTcpCore().SendBufferSize; | ||||
|             set | ||||
|             { | ||||
|                 this.GetTcpCore().SendBufferSize = value; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
| @@ -942,7 +936,15 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|             this.m_tcpCore.Reset(socket); | ||||
|             this.m_tcpCore.OnReceived = this.HandleReceived; | ||||
|             this.m_tcpCore.OnBreakOut = this.BreakOut; | ||||
|             if (this.Config.GetValue(TouchSocketConfigExtension.MinBufferSizeProperty) is int minValue) | ||||
|             { | ||||
|                 this.m_tcpCore.MinBufferSize = minValue; | ||||
|             } | ||||
|  | ||||
|             if (this.Config.GetValue(TouchSocketConfigExtension.MaxBufferSizeProperty) is int maxValue) | ||||
|             { | ||||
|                 this.m_tcpCore.MaxBufferSize = maxValue; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private void HandleReceived(TcpCore core, ByteBlock byteBlock) | ||||
|   | ||||
| @@ -31,6 +31,12 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|     /// </summary> | ||||
|     public class TcpService<TClient> : TcpServiceBase, ITcpService<TClient> where TClient : SocketClient, new() | ||||
|     { | ||||
|         /// <inheritdoc/> | ||||
|         public override int SendBufferSize => 1024 * 1024 * 10; | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public override int ReceiveBufferSize => 1024 * 1024 * 10; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 构造函数 | ||||
|         /// </summary> | ||||
| @@ -203,7 +209,7 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|         /// <param name="e"></param> | ||||
|         protected virtual Task OnDisconnecting(TClient socketClient, DisconnectEventArgs e) | ||||
|         { | ||||
|             if (this.Disconnected != null) | ||||
|             if (this.Disconnecting != null) | ||||
|             { | ||||
|                 return this.Disconnecting.Invoke(socketClient, e); | ||||
|             } | ||||
| @@ -477,7 +483,7 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|             { | ||||
|                 this.m_serverState = ServerState.Exception; | ||||
|  | ||||
|                 this.m_pluginsManager.Raise(nameof(IServerStartedPlugin.OnServerStarted), this, new ServiceStateEventArgs(this.m_serverState, ex) { Message = ex.Message }); | ||||
|                 this.m_pluginsManager.Raise(nameof(IServerStartedPlugin.OnServerStarted), this, new ServiceStateEventArgs(this.m_serverState, ex) { Message = ex.ToString() }); | ||||
|                 throw; | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -155,6 +155,11 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|         /// <param name="tcpCore"></param> | ||||
|         public void ReturnTcpCore(TcpCore tcpCore) | ||||
|         { | ||||
|             if (this.DisposedValue) | ||||
|             { | ||||
|                 tcpCore.SafeDispose(); | ||||
|                 return; | ||||
|             } | ||||
|             this.m_tcpCores.Push(tcpCore); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -69,10 +69,14 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|             this.Protocol = Protocol.Udp; | ||||
|             var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); | ||||
|             this.Monitor = new UdpNetworkMonitor(null, socket); | ||||
|             this.ReceiveBufferSize = 1024 * 64; | ||||
|             this.SendBufferSize = 1024 * 64; | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public override int ReceiveBufferSize => 64 * 1024; | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public override int SendBufferSize => 64 * 1024; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// <inheritdoc/> | ||||
|         /// </summary> | ||||
| @@ -332,7 +336,7 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 this.ServerState = ServerState.Exception; | ||||
|                 this.PluginsManager.Raise(nameof(IServerStartedPlugin.OnServerStarted), this, new ServiceStateEventArgs(this.ServerState, ex) { Message = ex.Message }); | ||||
|                 this.PluginsManager.Raise(nameof(IServerStartedPlugin.OnServerStarted), this, new ServiceStateEventArgs(this.ServerState, ex) { Message = ex.ToString() }); | ||||
|                 throw; | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -55,7 +55,7 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 this.OnError(ex.Message); | ||||
|                 this.OnError(ex.ToString()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -173,7 +173,7 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new Result(ResultCode.Exception, ex.Message); | ||||
|                 return new Result(ResultCode.Exception, ex.ToString()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -193,7 +193,7 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new Result(ResultCode.Exception, ex.Message); | ||||
|                 return new Result(ResultCode.Exception, ex.ToString()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -24,7 +24,6 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| using System.Net; | ||||
| using System.Text; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Sockets | ||||
| { | ||||
|     /// <summary> | ||||
| @@ -358,5 +357,145 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|         } | ||||
|  | ||||
|         #endregion IUdpClientSender | ||||
|  | ||||
|         #region IWaitSender | ||||
|         /// <summary> | ||||
|         /// 发送字节流 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="msg"></param> | ||||
|         /// <param name="timeout"></param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static byte[] SendThenReturn(this IWaitSender client, string msg, int timeout = 5000) | ||||
|         { | ||||
|             using (var tokenSource = new CancellationTokenSource(timeout)) | ||||
|             { | ||||
|                 return client.SendThenReturn(Encoding.UTF8.GetBytes(msg), tokenSource.Token); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送字节流 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="buffer">数据缓存区</param> | ||||
|         /// <param name="timeout"></param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static byte[] SendThenReturn(this IWaitSender client, byte[] buffer, int timeout = 5000) | ||||
|         { | ||||
|             using (var tokenSource = new CancellationTokenSource(timeout)) | ||||
|             { | ||||
|                 return client.SendThenReturn(buffer, 0, buffer.Length, tokenSource.Token); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送流中的有效数据 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="byteBlock">数据块载体</param> | ||||
|         /// <param name="timeout"></param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static byte[] SendThenReturn(this IWaitSender client, ByteBlock byteBlock, int timeout = 5000) | ||||
|         { | ||||
|             using (var tokenSource = new CancellationTokenSource(timeout)) | ||||
|             { | ||||
|                 return client.SendThenReturn(byteBlock.Buffer, 0, byteBlock.Len, tokenSource.Token); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 异步发送 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="buffer">数据缓存区</param> | ||||
|         /// <param name="timeout"></param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static Task<byte[]> SendThenReturnAsync(this IWaitSender client, byte[] buffer, int timeout = 5000) | ||||
|         { | ||||
|             using (var tokenSource = new CancellationTokenSource(timeout)) | ||||
|             { | ||||
|                 return client.SendThenReturnAsync(buffer, 0, buffer.Length, tokenSource.Token); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 异步发送 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="msg"></param> | ||||
|         /// <param name="timeout"></param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static Task<byte[]> SendThenReturnAsync(this IWaitSender client, string msg, int timeout = 5000) | ||||
|         { | ||||
|             using (var tokenSource = new CancellationTokenSource(timeout)) | ||||
|             { | ||||
|                 return client.SendThenReturnAsync(Encoding.UTF8.GetBytes(msg), tokenSource.Token); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送字节流 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="buffer">数据缓存区</param> | ||||
|         /// <param name="token">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static byte[] SendThenReturn(this IWaitSender client, byte[] buffer, CancellationToken token) | ||||
|         { | ||||
|             return client.SendThenReturn(buffer, 0, buffer.Length, token); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送流中的有效数据 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="byteBlock">数据块载体</param> | ||||
|         /// <param name="token">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static byte[] SendThenReturn(this IWaitSender client, ByteBlock byteBlock, CancellationToken token) | ||||
|         { | ||||
|             return client.SendThenReturn(byteBlock.Buffer, 0, byteBlock.Len, token); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 异步发送 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="buffer">数据缓存区</param> | ||||
|         /// <param name="token">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static Task<byte[]> SendThenReturnAsync(this IWaitSender client, byte[] buffer, CancellationToken token) | ||||
|         { | ||||
|             return client.SendThenReturnAsync(buffer, 0, buffer.Length, token); | ||||
|         } | ||||
|         #endregion | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -51,6 +51,54 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|         public static readonly DependencyProperty<Func<UdpDataHandlingAdapter>> UdpDataHandlingAdapterProperty = DependencyProperty<Func<UdpDataHandlingAdapter>>.Register("UdpDataHandlingAdapter", () => { return new NormalUdpDataHandlingAdapter(); }); | ||||
|  | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 最小缓存池尺寸 | ||||
|         /// 所需类型<see cref="int"/> | ||||
|         /// </summary> | ||||
|         public static readonly DependencyProperty<int?> MinBufferSizeProperty = DependencyProperty<int?>.Register("MinBufferSize", default); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 最大缓存池尺寸 | ||||
|         /// 所需类型<see cref="int"/> | ||||
|         /// </summary> | ||||
|         public static readonly DependencyProperty<int?> MaxBufferSizeProperty = DependencyProperty<int?>.Register("MinBufferSize", default); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 最小缓存容量,默认缺省。 | ||||
|         /// <list type="number"> | ||||
|         /// </list> | ||||
|         /// </summary> | ||||
|         /// <param name="config"></param> | ||||
|         /// <param name="value"></param> | ||||
|         /// <returns></returns> | ||||
|         public static TouchSocketConfig MinBufferSize(this TouchSocketConfig config, int value) | ||||
|         { | ||||
|             if (value < 1024) | ||||
|             { | ||||
|                 throw new ArgumentOutOfRangeException(nameof(value), value, "数值不能小于1024"); | ||||
|             } | ||||
|             config.SetValue(MinBufferSizeProperty, value); | ||||
|             return config; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 最大缓存容量,默认缺省。 | ||||
|         /// <list type="number"> | ||||
|         /// </list> | ||||
|         /// </summary> | ||||
|         /// <param name="config"></param> | ||||
|         /// <param name="value"></param> | ||||
|         /// <returns></returns> | ||||
|         public static TouchSocketConfig MaxBufferSize(this TouchSocketConfig config, int value) | ||||
|         { | ||||
|             if (value < 1024) | ||||
|             { | ||||
|                 throw new ArgumentOutOfRangeException(nameof(value), value, "数值不能小于1024"); | ||||
|             } | ||||
|             config.SetValue(MaxBufferSizeProperty, value); | ||||
|             return config; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送超时设定,单位毫秒,默认为0。意为禁用该配置。 | ||||
|         /// </summary> | ||||
|   | ||||
| @@ -36,37 +36,14 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|         /// <param name="buffer">数据缓存区</param> | ||||
|         /// <param name="offset">偏移</param> | ||||
|         /// <param name="length">长度</param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <param name="cancellationToken">取消令箭</param> | ||||
|         /// <param name="token">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         byte[] SendThenReturn(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default); | ||||
|         byte[] SendThenReturn(byte[] buffer, int offset, int length, CancellationToken token); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送字节流 | ||||
|         /// </summary> | ||||
|         /// <param name="buffer">数据缓存区</param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <param name="cancellationToken">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         byte[] SendThenReturn(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送流中的有效数据 | ||||
|         /// </summary> | ||||
|         /// <param name="byteBlock">数据块载体</param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <param name="cancellationToken">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         byte[] SendThenReturn(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 异步发送 | ||||
| @@ -74,36 +51,13 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|         /// <param name="buffer">数据缓存区</param> | ||||
|         /// <param name="offset">偏移</param> | ||||
|         /// <param name="length">长度</param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <param name="cancellationToken">取消令箭</param> | ||||
|         /// <param name="token">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         Task<byte[]> SendThenReturnAsync(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default); | ||||
|         Task<byte[]> SendThenReturnAsync(byte[] buffer, int offset, int length, CancellationToken token); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 异步发送 | ||||
|         /// </summary> | ||||
|         /// <param name="buffer">数据缓存区</param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <param name="cancellationToken">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         Task<byte[]> SendThenReturnAsync(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 异步发送 | ||||
|         /// </summary> | ||||
|         /// <param name="byteBlock">数据块载体</param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <param name="cancellationToken">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         Task<byte[]> SendThenReturnAsync(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default); | ||||
|     } | ||||
| } | ||||
| @@ -33,12 +33,12 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|         /// <summary> | ||||
|         /// 发送缓存区大小。最小值=1024。 | ||||
|         /// </summary> | ||||
|         int SendBufferSize { get; set; } | ||||
|         int SendBufferSize { get; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 接收缓存区大小。最小值=1024。 | ||||
|         /// </summary> | ||||
|         int ReceiveBufferSize { get; set; } | ||||
|         int ReceiveBufferSize { get; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 日志记录器 | ||||
|   | ||||
| @@ -31,6 +31,24 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|     [PluginOption(Singleton = true, NotRegister = true)] | ||||
|     public sealed class ReconnectionPlugin<TClient> : PluginBase where TClient : class, ITcpClient | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// 重连插件 | ||||
|         /// </summary> | ||||
|         public ReconnectionPlugin() | ||||
|         { | ||||
|             this.ActionForConnect = async (c) => | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     await c.ConnectAsync(); | ||||
|                     return true; | ||||
|                 } | ||||
|                 catch | ||||
|                 { | ||||
|                     return false; | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|         /// <inheritdoc/> | ||||
|         protected override void Loaded(IPluginsManager pluginsManager) | ||||
|         { | ||||
| @@ -134,6 +152,21 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 每个周期可执行的委托。返回值为True标识客户端存活。返回False,表示失活,立即重连。返回null时,表示跳过此次检验。 | ||||
|         /// </summary> | ||||
|         /// <param name="actionForCheck"></param> | ||||
|         /// <returns></returns> | ||||
|         public ReconnectionPlugin<TClient> SetActionForCheck(Func<TClient, int, bool?> actionForCheck) | ||||
|         { | ||||
|             this.ActionForCheck = async (c, i) => | ||||
|             { | ||||
|                 await EasyTask.CompletedTask; | ||||
|                 return actionForCheck.Invoke(c, i); | ||||
|             }; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 设置连接动作 | ||||
|         /// </summary> | ||||
| @@ -145,6 +178,21 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 设置连接动作 | ||||
|         /// </summary> | ||||
|         /// <param name="tryConnect"></param> | ||||
|         /// <returns>无论如何,只要返回True,则结束本轮尝试</returns> | ||||
|         public ReconnectionPlugin<TClient> SetConnectAction(Func<TClient, bool> tryConnect) | ||||
|         { | ||||
|             this.ActionForConnect = async (c) => | ||||
|             { | ||||
|                 await EasyTask.CompletedTask; | ||||
|                 return tryConnect.Invoke(c); | ||||
|             }; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 检验时间间隔 | ||||
|         /// </summary> | ||||
|   | ||||
| @@ -121,7 +121,7 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|                     { | ||||
|                         if (this.ReturnException) | ||||
|                         { | ||||
|                             client.Send(ex.Message); | ||||
|                             client.Send(ex.ToString()); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|   | ||||
| @@ -18,11 +18,17 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|     public class Receiver : DisposableObject, IReceiver | ||||
|     { | ||||
|         private readonly IClient m_client; | ||||
|         private readonly AutoResetEvent m_resetEventForComplateRead = new AutoResetEvent(false); | ||||
|         private readonly EasyLock m_resetEventForComplateRead = new EasyLock(false); | ||||
|         private readonly AsyncAutoResetEvent m_resetEventForRead = new AsyncAutoResetEvent(false); | ||||
|         private ByteBlock m_byteBlock; | ||||
|         private IRequestInfo m_requestInfo; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Receiver | ||||
|         /// </summary> | ||||
|         ~Receiver() | ||||
|         { | ||||
|             this.Dispose(false); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Receiver | ||||
|         /// </summary> | ||||
| @@ -64,7 +70,7 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|             { | ||||
|                 return true; | ||||
|             } | ||||
|             if (this.m_resetEventForComplateRead.WaitOne(TimeSpan.FromSeconds(10))) | ||||
|             if (this.m_resetEventForComplateRead.Wait(TimeSpan.FromSeconds(10), CancellationToken.None)) | ||||
|             { | ||||
|                 return true; | ||||
|             } | ||||
| @@ -73,10 +79,16 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         protected override void Dispose(bool disposing) | ||||
|         { | ||||
|             if (disposing) | ||||
|             { | ||||
|                 this.m_client.ClearReceiver(); | ||||
|             this.m_resetEventForComplateRead.SafeDispose(); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 this.m_resetEventForRead.SafeDispose(); | ||||
|             } | ||||
|  | ||||
|             this.m_byteBlock = null; | ||||
|             base.Dispose(disposing); | ||||
|         } | ||||
| @@ -85,7 +97,7 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|         { | ||||
|             this.m_byteBlock = default; | ||||
|             this.m_requestInfo = default; | ||||
|             this.m_resetEventForComplateRead.Set(); | ||||
|             this.m_resetEventForComplateRead.Release(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -46,37 +46,13 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|         /// <param name="buffer">数据缓存区</param> | ||||
|         /// <param name="offset">偏移</param> | ||||
|         /// <param name="length">长度</param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <param name="cancellationToken">取消令箭</param> | ||||
|         /// <param name="token">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         ResponsedData SendThenResponse(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default); | ||||
|         ResponsedData SendThenResponse(byte[] buffer, int offset, int length, CancellationToken token); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送字节流 | ||||
|         /// </summary> | ||||
|         /// <param name="buffer">数据缓存区</param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <param name="cancellationToken">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         ResponsedData SendThenResponse(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送流中的有效数据 | ||||
|         /// </summary> | ||||
|         /// <param name="byteBlock">数据块载体</param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <param name="cancellationToken">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         ResponsedData SendThenResponse(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 异步发送 | ||||
| @@ -84,36 +60,12 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|         /// <param name="buffer">数据缓存区</param> | ||||
|         /// <param name="offset">偏移</param> | ||||
|         /// <param name="length">长度</param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <param name="cancellationToken">取消令箭</param> | ||||
|         /// <param name="token">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         Task<ResponsedData> SendThenResponseAsync(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 异步发送 | ||||
|         /// </summary> | ||||
|         /// <param name="buffer">数据缓存区</param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <param name="cancellationToken">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         Task<ResponsedData> SendThenResponseAsync(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 异步发送 | ||||
|         /// </summary> | ||||
|         /// <param name="byteBlock">数据块载体</param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <param name="cancellationToken">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         Task<ResponsedData> SendThenResponseAsync(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default); | ||||
|         Task<ResponsedData> SendThenResponseAsync(byte[] buffer, int offset, int length, CancellationToken token); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -27,17 +27,11 @@ namespace ThingsGateway.Foundation.Sockets | ||||
| { | ||||
|     internal class WaitingClient<TClient> : DisposableObject, IWaitingClient<TClient> where TClient : IClient, ISender | ||||
|     { | ||||
|         private readonly Func<ResponsedData, bool> m_func; | ||||
|         private readonly SemaphoreSlim m_semaphoreSlim = new SemaphoreSlim(1, 1); | ||||
|         private readonly EasyLock m_semaphoreSlim = new(); | ||||
|         private volatile bool m_breaked; | ||||
|         private CancellationTokenSource m_cancellation; | ||||
|         private CancellationTokenSource m_cancellationTokenSource; | ||||
|  | ||||
|  | ||||
|         public WaitingClient(TClient client, WaitingOptions waitingOptions, Func<ResponsedData, bool> func) | ||||
|         { | ||||
|             this.Client = client ?? throw new ArgumentNullException(nameof(client)); | ||||
|             this.WaitingOptions = waitingOptions; | ||||
|             this.m_func = func; | ||||
|         } | ||||
|  | ||||
|         public WaitingClient(TClient client, WaitingOptions waitingOptions) | ||||
|         { | ||||
| @@ -59,19 +53,25 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|  | ||||
|         protected override void Dispose(bool disposing) | ||||
|         { | ||||
|             this.Client = default; | ||||
|             this.Cancel(); | ||||
|             this.Client = default; | ||||
|             base.Dispose(disposing); | ||||
|         } | ||||
|  | ||||
|         private void Cancel() | ||||
|         { | ||||
|             this.m_cancellation.Cancel(); | ||||
|             try | ||||
|             { | ||||
|                 this.m_cancellationTokenSource?.Cancel(); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         #region 同步Response | ||||
|  | ||||
|         public ResponsedData SendThenResponse(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken token = default) | ||||
|         public ResponsedData SendThenResponse(byte[] buffer, int offset, int length, CancellationToken token = default) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
| @@ -79,14 +79,13 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|                 this.m_breaked = false; | ||||
|                 if (token.CanBeCanceled) | ||||
|                 { | ||||
|                     using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(timeout); | ||||
|                     m_cancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, token); | ||||
|                     this.m_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     m_cancellation = new CancellationTokenSource(timeout); | ||||
|                     this.m_cancellationTokenSource = new CancellationTokenSource(5000); | ||||
|                 } | ||||
|                 using (m_cancellation) | ||||
|                 using (m_cancellationTokenSource) | ||||
|                 { | ||||
|                     if (this.WaitingOptions.RemoteIPHost != null && this.Client is IUdpSession session) | ||||
|                     { | ||||
| @@ -96,7 +95,7 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|  | ||||
|                             while (true) | ||||
|                             { | ||||
|                                 using (var receiverResult = receiver.ReadAsync(m_cancellation.Token).GetFalseAwaitResult()) | ||||
|                                 using (var receiverResult = receiver.ReadAsync(this.m_cancellationTokenSource.Token).GetFalseAwaitResult()) | ||||
|                                 { | ||||
|                                     var response = new ResponsedData(receiverResult.ByteBlock?.ToArray(), receiverResult.RequestInfo); | ||||
|                                 } | ||||
| @@ -110,7 +109,7 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|                             this.Client.Send(buffer, offset, length); | ||||
|                             while (true) | ||||
|                             { | ||||
|                                 using (var receiverResult = receiver.ReadAsync(m_cancellation.Token).GetFalseAwaitResult()) | ||||
|                                 using (var receiverResult = receiver.ReadAsync(this.m_cancellationTokenSource.Token).GetFalseAwaitResult()) | ||||
|                                 { | ||||
|                                     if (receiverResult.IsClosed) | ||||
|                                     { | ||||
| @@ -119,13 +118,13 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|                                     } | ||||
|                                     var response = new ResponsedData(receiverResult.ByteBlock?.ToArray(), receiverResult.RequestInfo); | ||||
|  | ||||
|                                     if (this.m_func == null) | ||||
|                                     if (this.WaitingOptions.FilterFunc == null) | ||||
|                                     { | ||||
|                                         return response; | ||||
|                                     } | ||||
|                                     else | ||||
|                                     { | ||||
|                                         if (this.m_func.Invoke(response)) | ||||
|                                         if (this.WaitingOptions.FilterFunc.Invoke(response)) | ||||
|                                         { | ||||
|                                             return response; | ||||
|                                         } | ||||
| @@ -142,25 +141,18 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 this.m_cancellationTokenSource = null; | ||||
|                 this.m_semaphoreSlim.Release(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public ResponsedData SendThenResponse(byte[] buffer, int timeout = 1000 * 5, CancellationToken token = default) | ||||
|         { | ||||
|             return this.SendThenResponse(buffer, 0, buffer.Length, timeout, token); | ||||
|         } | ||||
|  | ||||
|         public ResponsedData SendThenResponse(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken token = default) | ||||
|         { | ||||
|             return this.SendThenResponse(byteBlock.Buffer, 0, byteBlock.Len, timeout, token); | ||||
|         } | ||||
|  | ||||
|         #endregion 同步Response | ||||
|  | ||||
|         #region Response异步 | ||||
|  | ||||
|         public async Task<ResponsedData> SendThenResponseAsync(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken token = default) | ||||
|         public async Task<ResponsedData> SendThenResponseAsync(byte[] buffer, int offset, int length, CancellationToken token = default) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
| @@ -168,14 +160,13 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|                 this.m_breaked = false; | ||||
|                 if (token.CanBeCanceled) | ||||
|                 { | ||||
|                     using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(timeout); | ||||
|                     m_cancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, token); | ||||
|                     this.m_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     m_cancellation = new CancellationTokenSource(timeout); | ||||
|                     this.m_cancellationTokenSource = new CancellationTokenSource(5000); | ||||
|                 } | ||||
|                 using (m_cancellation) | ||||
|                 using (m_cancellationTokenSource) | ||||
|                 { | ||||
|                     if (this.WaitingOptions.RemoteIPHost != null && this.Client is IUdpSession session) | ||||
|                     { | ||||
| @@ -185,7 +176,7 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|  | ||||
|                             while (true) | ||||
|                             { | ||||
|                                 using (var receiverResult = await receiver.ReadAsync(m_cancellation.Token)) | ||||
|                                 using (var receiverResult = await receiver.ReadAsync(m_cancellationTokenSource.Token)) | ||||
|                                 { | ||||
|                                     var response = new ResponsedData(receiverResult.ByteBlock?.ToArray(), receiverResult.RequestInfo); | ||||
|                                 } | ||||
| @@ -199,7 +190,7 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|                             await this.Client.SendAsync(buffer, offset, length); | ||||
|                             while (true) | ||||
|                             { | ||||
|                                 using (var receiverResult = await receiver.ReadAsync(m_cancellation.Token)) | ||||
|                                 using (var receiverResult = await receiver.ReadAsync(this.m_cancellationTokenSource.Token)) | ||||
|                                 { | ||||
|                                     if (receiverResult.IsClosed) | ||||
|                                     { | ||||
| @@ -208,13 +199,13 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|                                     } | ||||
|                                     var response = new ResponsedData(receiverResult.ByteBlock?.ToArray(), receiverResult.RequestInfo); | ||||
|  | ||||
|                                     if (this.m_func == null) | ||||
|                                     if (this.WaitingOptions.FilterFunc == null) | ||||
|                                     { | ||||
|                                         return response; | ||||
|                                     } | ||||
|                                     else | ||||
|                                     { | ||||
|                                         if (this.m_func.Invoke(response)) | ||||
|                                         if (this.WaitingOptions.FilterFunc.Invoke(response)) | ||||
|                                         { | ||||
|                                             return response; | ||||
|                                         } | ||||
| @@ -231,58 +222,27 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 this.m_cancellationTokenSource = null; | ||||
|                 this.m_semaphoreSlim.Release(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public Task<ResponsedData> SendThenResponseAsync(byte[] buffer, int timeout = 1000 * 5, CancellationToken token = default) | ||||
|         { | ||||
|             return this.SendThenResponseAsync(buffer, 0, buffer.Length, timeout, token); | ||||
|         } | ||||
|  | ||||
|         public Task<ResponsedData> SendThenResponseAsync(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken token = default) | ||||
|         { | ||||
|             return this.SendThenResponseAsync(byteBlock.Buffer, 0, byteBlock.Len, timeout, token); | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         #endregion Response异步 | ||||
|  | ||||
|         #region 字节同步 | ||||
|  | ||||
|         public byte[] SendThenReturn(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken token = default) | ||||
|         public byte[] SendThenReturn(byte[] buffer, int offset, int length, CancellationToken token = default) | ||||
|         { | ||||
|             return this.SendThenResponse(buffer, offset, length, timeout, token).Data; | ||||
|             return this.SendThenResponse(buffer, offset, length, token).Data; | ||||
|         } | ||||
|  | ||||
|         public byte[] SendThenReturn(byte[] buffer, int timeout = 1000 * 5, CancellationToken token = default) | ||||
|         public async Task<byte[]> SendThenReturnAsync(byte[] buffer, int offset, int length, CancellationToken token = default) | ||||
|         { | ||||
|             return this.SendThenReturn(buffer, 0, buffer.Length, timeout, token); | ||||
|             return (await this.SendThenResponseAsync(buffer, offset, length, token)).Data; | ||||
|         } | ||||
|  | ||||
|         public byte[] SendThenReturn(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken token = default) | ||||
|         { | ||||
|             return this.SendThenReturn(byteBlock.Buffer, 0, byteBlock.Len, timeout, token); | ||||
|         } | ||||
|  | ||||
|         #endregion 字节同步 | ||||
|  | ||||
|         #region 字节异步 | ||||
|  | ||||
|         public async Task<byte[]> SendThenReturnAsync(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken token = default) | ||||
|         { | ||||
|             return (await this.SendThenResponseAsync(buffer, offset, length, timeout, token)).Data; | ||||
|         } | ||||
|  | ||||
|         public async Task<byte[]> SendThenReturnAsync(byte[] buffer, int timeout = 1000 * 5, CancellationToken token = default) | ||||
|         { | ||||
|             return (await this.SendThenResponseAsync(buffer, 0, buffer.Length, timeout, token)).Data; | ||||
|         } | ||||
|  | ||||
|         public async Task<byte[]> SendThenReturnAsync(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken token = default) | ||||
|         { | ||||
|             return (await this.SendThenResponseAsync(byteBlock.Buffer, 0, byteBlock.Len, timeout, token)).Data; | ||||
|         } | ||||
|  | ||||
|         #endregion 字节异步 | ||||
|     } | ||||
| } | ||||
| @@ -23,6 +23,8 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using System.Text; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Sockets | ||||
| { | ||||
|     /// <summary> | ||||
| @@ -31,28 +33,224 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|     public static class WaitingClientExtension | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// 获取筛选条件的可等待的客户端。 | ||||
|         /// </summary> | ||||
|         /// <typeparam name="TClient"></typeparam> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="waitingOptions"></param> | ||||
|         /// <param name="func">当条件成立时返回</param> | ||||
|         /// <returns></returns> | ||||
|         public static IWaitingClient<TClient> GetWaitingClient<TClient>(this TClient client, WaitingOptions waitingOptions, Func<ResponsedData, bool> func) where TClient : IClient, ISender | ||||
|         { | ||||
|             return new WaitingClient<TClient>(client, waitingOptions, func); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 获取可等待的客户端。 | ||||
|         /// 创建可等待的客户端。 | ||||
|         /// </summary> | ||||
|         /// <typeparam name="TClient"></typeparam> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="waitingOptions"></param> | ||||
|         /// <returns></returns> | ||||
|         public static IWaitingClient<TClient> GetWaitingClient<TClient>(this TClient client, WaitingOptions waitingOptions) where TClient : IClient, ISender | ||||
|         public static IWaitingClient<TClient> CreateWaitingClient<TClient>(this TClient client, WaitingOptions waitingOptions) where TClient : IClient, ISender | ||||
|         { | ||||
|             return new WaitingClient<TClient>(client, waitingOptions); | ||||
|         } | ||||
|  | ||||
|         #region 发送 | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送数据并等待 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="buffer">数据缓存区</param> | ||||
|         /// <param name="token">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static ResponsedData SendThenResponse<TClient>(this IWaitingClient<TClient> client, byte[] buffer, CancellationToken token) | ||||
|             where TClient : IClient, ISender | ||||
|         { | ||||
|             return client.SendThenResponse(buffer, 0, buffer.Length, token); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送数据并等待 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="msg"></param> | ||||
|         /// <param name="token">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static ResponsedData SendThenResponse<TClient>(this IWaitingClient<TClient> client, string msg, CancellationToken token) | ||||
|             where TClient : IClient, ISender | ||||
|         { | ||||
|             return client.SendThenResponse(Encoding.UTF8.GetBytes(msg), token); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// 发送数据并等待 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="buffer">数据缓存区</param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <param name="token">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static ResponsedData SendThenResponse<TClient>(this IWaitingClient<TClient> client, byte[] buffer, int timeout, CancellationToken token) | ||||
|             where TClient : IClient, ISender | ||||
|         { | ||||
|             using CancellationTokenSource cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(new CancellationTokenSource(timeout).Token, token); | ||||
|             return client.SendThenResponse(buffer, 0, buffer.Length, cancellationTokenSource.Token); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// 发送数据并等待 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="buffer">数据缓存区</param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <param name="token">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static Task<ResponsedData> SendThenResponseAsync<TClient>(this IWaitingClient<TClient> client, byte[] buffer, int timeout, CancellationToken token) | ||||
|             where TClient : IClient, ISender | ||||
|         { | ||||
|             using CancellationTokenSource cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(new CancellationTokenSource(timeout).Token, token); | ||||
|             return client.SendThenResponseAsync(buffer, 0, buffer.Length, cancellationTokenSource.Token); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送数据并等待 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="msg"></param> | ||||
|         /// <param name="timeout"></param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static ResponsedData SendThenResponse<TClient>(this IWaitingClient<TClient> client, string msg, int timeout = 5000) | ||||
|             where TClient : IClient, ISender | ||||
|         { | ||||
|             return client.SendThenResponse(Encoding.UTF8.GetBytes(msg), timeout); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送数据并等待 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="byteBlock">数据块载体</param> | ||||
|         /// <param name="token">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static ResponsedData SendThenResponse<TClient>(this IWaitingClient<TClient> client, ByteBlock byteBlock, CancellationToken token) | ||||
|             where TClient : IClient, ISender | ||||
|         { | ||||
|             return client.SendThenResponse(byteBlock.Buffer, 0, byteBlock.Len, token); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送数据并等待 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="buffer">数据缓存区</param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static ResponsedData SendThenResponse<TClient>(this IWaitingClient<TClient> client, byte[] buffer, int timeout = 5000) | ||||
|             where TClient : IClient, ISender | ||||
|         { | ||||
|             using (var tokenSource = new CancellationTokenSource(timeout)) | ||||
|             { | ||||
|                 return client.SendThenResponse(buffer, 0, buffer.Length, tokenSource.Token); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送数据并等待 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="byteBlock">数据块载体</param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static ResponsedData SendThenResponse<TClient>(this IWaitingClient<TClient> client, ByteBlock byteBlock, int timeout = 5000) | ||||
|             where TClient : IClient, ISender | ||||
|         { | ||||
|             using (var tokenSource = new CancellationTokenSource(timeout)) | ||||
|             { | ||||
|                 return client.SendThenResponse(byteBlock.Buffer, 0, byteBlock.Len, tokenSource.Token); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送数据并等待 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="buffer"></param> | ||||
|         /// <param name="token">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static Task<ResponsedData> SendThenResponseAsync<TClient>(this IWaitingClient<TClient> client, byte[] buffer, CancellationToken token) | ||||
|             where TClient : IClient, ISender | ||||
|         { | ||||
|             return client.SendThenResponseAsync(buffer, 0, buffer.Length, token); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送数据并等待 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="msg"></param> | ||||
|         /// <param name="token">取消令箭</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static Task<ResponsedData> SendThenResponseAsync<TClient>(this IWaitingClient<TClient> client, string msg, CancellationToken token) | ||||
|             where TClient : IClient, ISender | ||||
|         { | ||||
|             return client.SendThenResponseAsync(Encoding.UTF8.GetBytes(msg), token); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送数据并等待 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="buffer"></param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static Task<ResponsedData> SendThenResponseAsync<TClient>(this IWaitingClient<TClient> client, byte[] buffer, int timeout = 5000) | ||||
|             where TClient : IClient, ISender | ||||
|         { | ||||
|             using (var tokenSource = new CancellationTokenSource(timeout)) | ||||
|             { | ||||
|                 return client.SendThenResponseAsync(buffer, 0, buffer.Length, tokenSource.Token); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 发送数据并等待 | ||||
|         /// </summary> | ||||
|         /// <param name="client"></param> | ||||
|         /// <param name="msg"></param> | ||||
|         /// <param name="timeout">超时时间</param> | ||||
|         /// <exception cref="NotConnectedException">客户端没有连接</exception> | ||||
|         /// <exception cref="OverlengthException">发送数据超长</exception> | ||||
|         /// <exception cref="Exception">其他异常</exception> | ||||
|         /// <returns>返回的数据</returns> | ||||
|         public static Task<ResponsedData> SendThenResponseAsync<TClient>(this IWaitingClient<TClient> client, string msg, int timeout = 5000) | ||||
|             where TClient : IClient, ISender | ||||
|         { | ||||
|             using (var tokenSource = new CancellationTokenSource(timeout)) | ||||
|             { | ||||
|                 return client.SendThenResponseAsync(Encoding.UTF8.GetBytes(msg), tokenSource.Token); | ||||
|             } | ||||
|         } | ||||
|         #endregion 发送 | ||||
|     } | ||||
| } | ||||
| @@ -45,5 +45,12 @@ namespace ThingsGateway.Foundation.Sockets | ||||
|         /// 当Client为Tcp系时。是否在断开连接时以异常返回结果。 | ||||
|         /// </summary> | ||||
|         public bool ThrowBreakException { get; set; } = true; | ||||
|  | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 筛选函数 | ||||
|         /// </summary> | ||||
|         public Func<ResponsedData, bool> FilterFunc { get; set; } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -142,7 +142,7 @@ namespace ThingsGateway.Foundation.WebApi | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         invokeResult.Status = InvokeStatus.Exception; | ||||
|                         invokeResult.Message = ex.Message; | ||||
|                         invokeResult.Message = ex.ToString(); | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
| @@ -273,7 +273,7 @@ namespace ThingsGateway.Foundation.WebApi | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         invokeResult.Status = InvokeStatus.Exception; | ||||
|                         invokeResult.Message = ex.Message; | ||||
|                         invokeResult.Message = ex.ToString(); | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| <Project> | ||||
| 	<PropertyGroup> | ||||
| 		<Version>3.0.0.12</Version> | ||||
| 		<Version>3.0.0.22</Version> | ||||
| 		<LangVersion>latest</LangVersion> | ||||
| 		<ImplicitUsings>enable</ImplicitUsings> | ||||
| 		<TargetFrameworks>net6.0;net7.0</TargetFrameworks> | ||||
|   | ||||
| @@ -98,7 +98,7 @@ public class KafkaProducer : UpLoadBase | ||||
|                         } | ||||
|                         catch (Exception ex) | ||||
|                         { | ||||
|                             LogMessage.LogWarning(ex, ToString()); | ||||
|                             LogMessage.LogWarning(ex); | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
| @@ -133,7 +133,7 @@ public class KafkaProducer : UpLoadBase | ||||
|                                 } | ||||
|                                 catch (Exception ex) | ||||
|                                 { | ||||
|                                     LogMessage.LogWarning(ex, ToString()); | ||||
|                                     LogMessage.LogWarning(ex); | ||||
|                                 } | ||||
|  | ||||
|                             } | ||||
| @@ -144,7 +144,7 @@ public class KafkaProducer : UpLoadBase | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         LogMessage?.LogWarning(ex, ToString()); | ||||
|                         LogMessage?.LogWarning(ex); | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
| @@ -153,7 +153,7 @@ public class KafkaProducer : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|         try | ||||
|         { | ||||
| @@ -180,7 +180,7 @@ public class KafkaProducer : UpLoadBase | ||||
|                         } | ||||
|                         catch (Exception ex) | ||||
|                         { | ||||
|                             LogMessage?.LogWarning(ex, ToString()); | ||||
|                             LogMessage?.LogWarning(ex); | ||||
|                         } | ||||
|                     } | ||||
|                     if (isSuccess) | ||||
| @@ -212,7 +212,7 @@ public class KafkaProducer : UpLoadBase | ||||
|                             } | ||||
|                             catch (Exception ex) | ||||
|                             { | ||||
|                                 LogMessage?.LogWarning(ex, ToString()); | ||||
|                                 LogMessage?.LogWarning(ex); | ||||
|                             } | ||||
|                         } | ||||
|                         if (isSuccess) | ||||
| @@ -226,7 +226,7 @@ public class KafkaProducer : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|  | ||||
|         if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50) | ||||
| @@ -401,7 +401,7 @@ public class KafkaProducer : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|             await CacheDb.AddCacheData(topic, payLoad, driverPropertys.CacheMaxCount); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -162,7 +162,7 @@ public class ModbusSerialServer : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|             tags.ForEach(a => | ||||
|             { | ||||
|                 a.VariableValueChange -= VariableValueChange; | ||||
| @@ -234,7 +234,7 @@ public class ModbusSerialServer : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex.Message); | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|   | ||||
| @@ -158,7 +158,7 @@ public class ModbusTcpServer : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|             tags.ForEach(a => | ||||
|             { | ||||
|                 a.VariableValueChange -= VariableValueChange; | ||||
| @@ -201,7 +201,7 @@ public class ModbusTcpServer : UpLoadBase | ||||
|             var addressStr = GetPropertyValue(tag.Value, nameof(ModbusTcpServerVariableProperty.ServiceAddress)); | ||||
|             if (Enum.TryParse<DataTypeEnum>(type, out DataTypeEnum result)) | ||||
|             { | ||||
|                 var result1 = await RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusTcpServer)}-{CurrentDevice.Name}-{client.IP + ":" + client.Port}", | ||||
|                 var result1 = await RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusTcpServer)}-{CurrentDevice.Name}-{$"{client.IP}:{client.Port}"}", | ||||
|                new Dictionary<string, string> | ||||
| { | ||||
|     { | ||||
| @@ -216,7 +216,7 @@ public class ModbusTcpServer : UpLoadBase | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var result1 = await RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusTcpServer)}-{CurrentDevice.Name}-{client.IP + ":" + client.Port}", | ||||
|                 var result1 = await RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusTcpServer)}-{CurrentDevice.Name}-{$"{client.IP}:{client.Port}"}", | ||||
|                new Dictionary<string, string> | ||||
| { | ||||
|     { | ||||
| @@ -230,7 +230,7 @@ public class ModbusTcpServer : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex.Message); | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|   | ||||
| @@ -124,7 +124,7 @@ public class IotSharpClient : UpLoadBase | ||||
|                         } | ||||
|                         catch (Exception ex) | ||||
|                         { | ||||
|                             LogMessage?.LogWarning(ex, ToString()); | ||||
|                             LogMessage?.LogWarning(ex); | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
| @@ -163,7 +163,7 @@ public class IotSharpClient : UpLoadBase | ||||
|                                 } | ||||
|                                 catch (Exception ex) | ||||
|                                 { | ||||
|                                     LogMessage?.LogWarning(ex, ToString()); | ||||
|                                     LogMessage?.LogWarning(ex); | ||||
|                                 } | ||||
|  | ||||
|                             } | ||||
| @@ -171,7 +171,7 @@ public class IotSharpClient : UpLoadBase | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         LogMessage?.LogWarning(ex, ToString()); | ||||
|                         LogMessage?.LogWarning(ex); | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
| @@ -180,7 +180,7 @@ public class IotSharpClient : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|         try | ||||
|         { | ||||
| @@ -207,7 +207,7 @@ public class IotSharpClient : UpLoadBase | ||||
|                         } | ||||
|                         catch (Exception ex) | ||||
|                         { | ||||
|                             LogMessage?.LogWarning(ex, ToString()); | ||||
|                             LogMessage?.LogWarning(ex); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
| @@ -237,7 +237,7 @@ public class IotSharpClient : UpLoadBase | ||||
|                             } | ||||
|                             catch (Exception ex) | ||||
|                             { | ||||
|                                 LogMessage?.LogWarning(ex, ToString()); | ||||
|                                 LogMessage?.LogWarning(ex); | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
| @@ -250,7 +250,7 @@ public class IotSharpClient : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|  | ||||
|         if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50) | ||||
| @@ -301,7 +301,7 @@ public class IotSharpClient : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogError(ex, ToString()); | ||||
|             LogMessage?.LogError(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|   | ||||
| @@ -114,7 +114,7 @@ public class MqttClient : UpLoadBase | ||||
|                         } | ||||
|                         catch (Exception ex) | ||||
|                         { | ||||
|                             LogMessage?.LogWarning(ex, ToString()); | ||||
|                             LogMessage?.LogWarning(ex); | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
| @@ -149,7 +149,7 @@ public class MqttClient : UpLoadBase | ||||
|                                 } | ||||
|                                 catch (Exception ex) | ||||
|                                 { | ||||
|                                     LogMessage?.LogWarning(ex, ToString()); | ||||
|                                     LogMessage?.LogWarning(ex); | ||||
|                                 } | ||||
|  | ||||
|                             } | ||||
| @@ -157,7 +157,7 @@ public class MqttClient : UpLoadBase | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         LogMessage?.LogWarning(ex, ToString()); | ||||
|                         LogMessage?.LogWarning(ex); | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
| @@ -167,7 +167,7 @@ public class MqttClient : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|         try | ||||
|         { | ||||
| @@ -194,7 +194,7 @@ public class MqttClient : UpLoadBase | ||||
|                         } | ||||
|                         catch (Exception ex) | ||||
|                         { | ||||
|                             LogMessage?.LogWarning(ex, ToString()); | ||||
|                             LogMessage?.LogWarning(ex); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
| @@ -226,7 +226,7 @@ public class MqttClient : UpLoadBase | ||||
|                             } | ||||
|                             catch (Exception ex) | ||||
|                             { | ||||
|                                 LogMessage?.LogWarning(ex, ToString()); | ||||
|                                 LogMessage?.LogWarning(ex); | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
| @@ -239,7 +239,7 @@ public class MqttClient : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|  | ||||
|         if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50) | ||||
| @@ -293,7 +293,7 @@ public class MqttClient : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogError(ex, ToString()); | ||||
|             LogMessage?.LogError(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -438,7 +438,7 @@ public class MqttClient : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|         try | ||||
|         { | ||||
|   | ||||
| @@ -108,7 +108,7 @@ public class MqttServer : UpLoadBase | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         LogMessage?.LogWarning(ex, ToString()); | ||||
|                         LogMessage?.LogWarning(ex); | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
| @@ -117,7 +117,7 @@ public class MqttServer : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|  | ||||
|         try | ||||
| @@ -147,7 +147,7 @@ public class MqttServer : UpLoadBase | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         LogMessage?.LogWarning(ex, ToString()); | ||||
|                         LogMessage?.LogWarning(ex); | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
| @@ -157,7 +157,7 @@ public class MqttServer : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|  | ||||
|         try | ||||
| @@ -170,7 +170,7 @@ public class MqttServer : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|         if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50) | ||||
|         { | ||||
| @@ -302,7 +302,7 @@ public class MqttServer : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|         try | ||||
|         { | ||||
|   | ||||
| @@ -88,12 +88,12 @@ f => | ||||
| .Build(); | ||||
|  | ||||
|             await mqttClientPage.MqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + $"订阅{driverDebugUIPage.Address}成功")); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - 订阅{driverDebugUIPage.Address}成功")); | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + ex.Message)); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - {ex}")); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| @@ -106,12 +106,12 @@ f => | ||||
| .Build(); | ||||
|  | ||||
|             await mqttClientPage.MqttClient.UnsubscribeAsync(mqttSubscribeOptions, CancellationToken.None); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + $"取消订阅{driverDebugUIPage.Address}成功")); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - 取消订阅{driverDebugUIPage.Address}成功")); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|  | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + ex.Message)); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - {ex}")); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| @@ -125,11 +125,11 @@ f => | ||||
| .WithTopic($"{PublishTopic}") | ||||
| .WithPayload(PublishValue).Build(); | ||||
|             await mqttClientPage.MqttClient.PublishAsync(devMessage, CancellationToken.None); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + $"发布{PublishTopic}成功")); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - 发布{PublishTopic}成功")); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + ex.Message)); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - {ex}")); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| @@ -141,11 +141,11 @@ f => | ||||
|             using MqttRpcClient mqttRpcClient = new(mqttClientPage.MqttClient); | ||||
|             var data = await mqttRpcClient.ExecuteAsync(MqttRpcTopicPair, driverDebugUIPage.WriteValue, MQTTnet.Protocol.MqttQualityOfServiceLevel.AtMostOnce, TimeSpan.FromSeconds(10)); | ||||
|             var str = Encoding.UTF8.GetString(data); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + str)); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - {str}")); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + ex.Message)); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - {ex}")); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|   | ||||
| @@ -230,7 +230,7 @@ public class OPCDAClient : CollectBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -86,7 +86,7 @@ public partial class OPCDAClientDebugPage : IDisposable | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + ex.Message)); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {ex}")); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -182,7 +182,7 @@ public partial class OPCDAClientDebugPage : IDisposable | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + ex.Message)); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {ex}")); | ||||
|         } | ||||
|  | ||||
|         return Task.CompletedTask; | ||||
| @@ -228,7 +228,7 @@ public partial class OPCDAClientDebugPage : IDisposable | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + "写入失败:" + ex.Message)); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 写入失败:{ex}")); | ||||
|         } | ||||
|  | ||||
|         return Task.CompletedTask; | ||||
|   | ||||
| @@ -177,6 +177,8 @@ public class OPCUAClient : CollectBase | ||||
|         if (_plc != null) | ||||
|         { | ||||
|             _plc.DataChangedHandler -= DataChangedHandler; | ||||
|             _plc.OpcStatusChange -= _plc_OpcStatusChange; | ||||
|  | ||||
|             _plc.Disconnect(); | ||||
|             _plc.SafeDispose(); | ||||
|             _plc = null; | ||||
| @@ -204,13 +206,19 @@ public class OPCUAClient : CollectBase | ||||
|         }; | ||||
|         if (_plc == null) | ||||
|         { | ||||
|             _plc = new((arg1, arg2, arg3, arg4) => Log_Out((LogLevel)arg1, arg2, arg3, arg4)); | ||||
|             _plc = new(); | ||||
|             _plc.OpcStatusChange += _plc_OpcStatusChange; | ||||
|             _plc.DataChangedHandler += DataChangedHandler; | ||||
|         } | ||||
|  | ||||
|         _plc.OPCNode = opcNode; | ||||
|     } | ||||
|  | ||||
|     private void _plc_OpcStatusChange(object sender, OpcUaStatusEventArgs e) | ||||
|     { | ||||
|         Log_Out((LogLevel)e.LogLevel, null, e.Text, null); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken) | ||||
|     { | ||||
| @@ -283,7 +291,7 @@ public class OPCUAClient : CollectBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,39 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Opc.Ua.Configuration; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.OPCUA; | ||||
|  | ||||
|  | ||||
| public partial class OPCUAServer | ||||
| { | ||||
|     public class ApplicationMessageDlg : IApplicationMessageDlg | ||||
|     { | ||||
|         private string message = string.Empty; | ||||
|         private ILog _log; | ||||
|         public ApplicationMessageDlg(ILog log) | ||||
|         { | ||||
|             _log = log; | ||||
|         } | ||||
|         public override void Message(string text, bool ask) | ||||
|         { | ||||
|             message = text; | ||||
|         } | ||||
|  | ||||
|         public override async Task<bool> ShowAsync() | ||||
|         { | ||||
|             _log.Warning(message); | ||||
|             return await Task.FromResult(true); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,59 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.OPCUA; | ||||
|  | ||||
|  | ||||
| public partial class OPCUAServer | ||||
| { | ||||
|     private class OPCUALogger : ILogger | ||||
|     { | ||||
|         private ILog _log; | ||||
|         public OPCUALogger(ILog log) | ||||
|         { | ||||
|             _log = log; | ||||
|  | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Set the log level | ||||
|         /// </summary> | ||||
|         public Microsoft.Extensions.Logging.LogLevel LogLevel { get; set; } = Microsoft.Extensions.Logging.LogLevel.Trace; | ||||
|         /// <inheritdoc/> | ||||
|         public IDisposable BeginScope<TState>(TState state) => default; | ||||
|         /// <inheritdoc/> | ||||
|         public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel) => logLevel >= LogLevel; | ||||
|         /// <inheritdoc/> | ||||
|         public void Log<TState>( | ||||
|             Microsoft.Extensions.Logging.LogLevel logLevel, | ||||
|             EventId eventId, | ||||
|             TState state, | ||||
|             Exception exception, | ||||
|             Func<TState, Exception, string> formatter) | ||||
|         { | ||||
|             if (!IsEnabled(logLevel)) | ||||
|             { | ||||
|                 return; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var message = formatter(state, exception); | ||||
|                 _log.Log((Foundation.Core.LogLevel)(byte)logLevel, state, message, exception); | ||||
|                 if (logLevel > Microsoft.Extensions.Logging.LogLevel.Information) | ||||
|                 { | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -63,7 +63,7 @@ public partial class OPCUAServer : UpLoadBase | ||||
|     public override async Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         // 启动服务器。 | ||||
|         await m_application.CheckApplicationInstanceCertificate(true, 0, 1200); | ||||
|         await m_application.CheckApplicationInstanceCertificate(false, 0, 1200); | ||||
|         await m_application.Start(m_server); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
| @@ -90,7 +90,7 @@ public partial class OPCUAServer : UpLoadBase | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         LogMessage.LogWarning(ex, ToString()); | ||||
|                         LogMessage.LogWarning(ex); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
| @@ -98,7 +98,7 @@ public partial class OPCUAServer : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage.LogWarning(ex, ToString()); | ||||
|             LogMessage.LogWarning(ex); | ||||
|         } | ||||
|         if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50) | ||||
|         { | ||||
| @@ -138,9 +138,13 @@ public partial class OPCUAServer : UpLoadBase | ||||
|         _uploadVariables = null; | ||||
|         CollectVariableRunTimes.Clear(); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Init(UploadDeviceRunTime device) | ||||
|     { | ||||
|         ApplicationInstance.MessageDlg = new ApplicationMessageDlg(LogMessage);//默认返回true | ||||
|  | ||||
|         //Utils.SetLogger(new OPCUALogger(LogMessage)); //调试用途 | ||||
|         m_application = new ApplicationInstance(); | ||||
|         m_configuration = GetDefaultConfiguration(); | ||||
|         m_configuration.Validate(ApplicationType.Server).GetAwaiter().GetResult(); | ||||
| @@ -169,7 +173,7 @@ public partial class OPCUAServer : UpLoadBase | ||||
|     private ApplicationConfiguration GetDefaultConfiguration() | ||||
|     { | ||||
|         ApplicationConfiguration config = new(); | ||||
|         string url = driverPropertys.OpcUaStringUrl; | ||||
|         var urls = driverPropertys.OpcUaStringUrl.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); | ||||
|         // 签名及加密验证 | ||||
|         ServerSecurityPolicyCollection policies = new(); | ||||
|         var userTokens = new UserTokenPolicyCollection(); | ||||
| @@ -242,13 +246,14 @@ public partial class OPCUAServer : UpLoadBase | ||||
|  | ||||
|         config.ApplicationName = "ThingsGateway OPCUAServer"; | ||||
|         config.ApplicationType = ApplicationType.Server; | ||||
|         config.ApplicationUri = Utils.Format(@"urn:{0}:thingsgatewayopcuaserver", System.Net.Dns.GetHostName()); | ||||
|         config.ApplicationUri = driverPropertys.ApplicationUri; | ||||
|  | ||||
|  | ||||
|         config.ServerConfiguration = new ServerConfiguration() | ||||
|         { | ||||
|             // 配置登录的地址 | ||||
|             BaseAddresses = new string[] { url }, | ||||
|             BaseAddresses = urls, | ||||
|  | ||||
|             SecurityPolicies = policies, | ||||
|             UserTokenPolicies = userTokens, | ||||
|             ShutdownDelay = 1, | ||||
| @@ -269,7 +274,7 @@ public partial class OPCUAServer : UpLoadBase | ||||
|             MaxNotificationQueueSize = 100,       // 为每个被监视项目保存在队列中的最大证书数 | ||||
|             MaxNotificationsPerPublish = 1000,    // 每次发布的最大通知数 | ||||
|             MinMetadataSamplingInterval = 1000,   // 元数据的最小采样间隔 | ||||
|             MaxRegistrationInterval = 30000,   // 两次注册尝试之间的最大时间(以毫秒为单位) | ||||
|             MaxRegistrationInterval = -1,   // 两次注册尝试之间的最大时间(以毫秒为单位)//不提供注册 | ||||
|  | ||||
|         }; | ||||
|         config.SecurityConfiguration = new SecurityConfiguration() | ||||
| @@ -283,7 +288,8 @@ public partial class OPCUAServer : UpLoadBase | ||||
|             { | ||||
|                 StoreType = CertificateStoreType.X509Store, | ||||
|                 StorePath = "CurrentUser\\UAServer_ThingsGateway", | ||||
|                 SubjectName = "CN=ThingsGateway OPCUAServer, C=CN, S=GUANGZHOU, O=ThingsGateway, DC=" + System.Net.Dns.GetHostName(), | ||||
|                 SubjectName = driverPropertys.SubjectName, | ||||
|                 //ValidationOptions = CertificateValidationOptions.SuppressHostNameInvalid, | ||||
|             }, | ||||
|  | ||||
|             TrustedPeerCertificates = new CertificateTrustList() | ||||
| @@ -323,7 +329,7 @@ public partial class OPCUAServer : UpLoadBase | ||||
|  | ||||
|  | ||||
|         config.CertificateValidator = new CertificateValidator(); | ||||
|         config.CertificateValidator.Update(config); | ||||
|         config.CertificateValidator.Update(config).GetAwaiter().GetResult(); | ||||
|         config.Extensions = new XmlElementCollection(); | ||||
|  | ||||
|         return config; | ||||
|   | ||||
| @@ -10,6 +10,8 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Opc.Ua; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.OPCUA; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| @@ -18,8 +20,21 @@ public class OPCUAServerProperty : UpDriverPropertyBase | ||||
|     /// <summary> | ||||
|     /// 服务地址 | ||||
|     /// </summary> | ||||
|     [DeviceProperty("服务地址", "")] | ||||
|     [DeviceProperty("服务地址", "分号分割数组,可设置多个url")] | ||||
|     public string OpcUaStringUrl { get; set; } = "opc.tcp://127.0.0.1:49321"; | ||||
|     /// <summary> | ||||
|     /// SubjectName | ||||
|     /// </summary> | ||||
|     [DeviceProperty("SubjectName", "")] | ||||
|     public string SubjectName { get; set; } = "CN=ThingsGateway OPCUAServer, C=CN, S=GUANGZHOU, O=ThingsGateway, DC=" + System.Net.Dns.GetHostName(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// ApplicationUri | ||||
|     /// </summary> | ||||
|     [DeviceProperty("ApplicationUri", "")] | ||||
|     public string ApplicationUri { get; set; } = Utils.Format(@"urn:{0}:thingsgatewayopcuaserver", System.Net.Dns.GetHostName()); | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 安全策略 | ||||
|     /// </summary> | ||||
|   | ||||
| @@ -53,16 +53,23 @@ | ||||
|                  写入 | ||||
|              </MButton> | ||||
|  | ||||
|              <MButton Class="mx-1 my-3" Color="accent" OnClick="()=>IsShowImportVariableList=!IsShowImportVariableList"> | ||||
|                  <span>查看OPC节点空间</span> | ||||
|              </MButton> | ||||
|  | ||||
|  | ||||
|          </div> | ||||
|         <div class="my-1 ml-2"> | ||||
|  | ||||
|              <MRow  Align="AlignTypes.Center" > | ||||
|  | ||||
|              <MCheckbox HideDetails="@("auto")"  @bind-Value=IsShowSubvariable Dense Label="是否显示子变量"/> | ||||
|               | ||||
|              <MButton Class="mx-1" Color="accent" OnClick="()=>IsShowImportVariableList=!IsShowImportVariableList"> | ||||
|                  <span>查看OPC节点空间</span> | ||||
|              </MButton> | ||||
|             </MRow> | ||||
|          </div> | ||||
|  | ||||
|          <MCol Class="my-1 py-1"> | ||||
|              <MRow NoGutters> | ||||
|              <MRow  Align="AlignTypes.Center"> | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -115,7 +122,7 @@ | ||||
|     <ChildContent> | ||||
|         @if (IsShowImportVariableList) | ||||
|         { | ||||
|             <OPCUAImportVariable @ref=ImportVariable PLC="_plc"></OPCUAImportVariable> | ||||
|             <OPCUAImportVariable @ref=ImportVariable PLC="_plc" IsShowSubvariable=IsShowSubvariable></OPCUAImportVariable> | ||||
|         } | ||||
|     </ChildContent> | ||||
|  | ||||
| @@ -127,5 +134,5 @@ | ||||
|  | ||||
|     @code { | ||||
|     private readonly List<(string Code, string Language)> _sections = new(); | ||||
|  | ||||
|     private bool IsShowSubvariable; | ||||
| } | ||||
|   | ||||
| @@ -80,7 +80,7 @@ public partial class OPCUAClientDebugPage | ||||
|             await _plc.AddSubscriptionAsync(Guid.NewGuid().ToString(), new[] { driverDebugUIPage.Address }); | ||||
|         else | ||||
|         { | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Debug, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + "未连接")); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Debug, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 未连接")); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -186,7 +186,7 @@ public partial class OPCUAClientDebugPage | ||||
|             { | ||||
|  | ||||
|                 driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, | ||||
|                         $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)}  - {ex.Message}")); | ||||
|                         $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)}  - {ex}")); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
| @@ -234,7 +234,7 @@ public partial class OPCUAClientDebugPage | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, | ||||
|                         $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)}  - {ex.Message}")); | ||||
|                         $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)}  - {ex}")); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -37,7 +37,7 @@ | ||||
|          <MTextField Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.UserName) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.UserName /> | ||||
|          <MTextField Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.Password) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.Password /> | ||||
|  | ||||
|          <MCheckbox Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.ActiveSubscribe) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.ActiveSubscribe /> | ||||
|          <MCheckbox Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.ActiveSubscribe) Dense  HideDetails="@("auto")" @bind-Value=@node.ActiveSubscribe /> | ||||
|          <MCheckbox Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.IsUseSecurity) Dense HideDetails="@("auto")" @bind-Value=@node.IsUseSecurity /> | ||||
|          <MCheckbox Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.CheckDomain) Dense HideDetails="@("auto")" @bind-Value=@node.CheckDomain /> | ||||
|          <MCheckbox Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.LoadType) Dense HideDetails="@("auto")" @bind-Value=@node.LoadType /> | ||||
|   | ||||
| @@ -36,16 +36,24 @@ public partial class OPCUAClientPage | ||||
|     /// </summary> | ||||
|     public void Dispose() | ||||
|     { | ||||
|         OPC.OpcStatusChange -= OPC_OpcStatusChange; | ||||
|         OPC.SafeDispose(); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void OnInitialized() | ||||
|     { | ||||
|         OPC = new ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient(LogOut); | ||||
|         OPC = new ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient(); | ||||
|         OPC.OpcStatusChange += OPC_OpcStatusChange; | ||||
|         base.OnInitialized(); | ||||
|     } | ||||
|  | ||||
|     private void OPC_OpcStatusChange(object sender, OpcUaStatusEventArgs e) | ||||
|     { | ||||
|         if (e.LogLevel > 0) | ||||
|             LogAction?.Invoke((LogLevel)e.LogLevel, null, e.Text, null); | ||||
|     } | ||||
|  | ||||
|     private async Task ConnectAsync() | ||||
|     { | ||||
|         try | ||||
| @@ -75,6 +83,4 @@ public partial class OPCUAClientPage | ||||
|         return OPC; | ||||
|     } | ||||
|  | ||||
|     private void LogOut(byte logLevel, object source, string message, Exception exception) => LogAction?.Invoke((LogLevel)logLevel, source, message, exception); | ||||
|  | ||||
| } | ||||
| @@ -43,6 +43,11 @@ public partial class OPCUAImportVariable | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient PLC { get; set; } | ||||
|     /// <summary> | ||||
|     /// 是否显示子变量 | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public bool IsShowSubvariable { get; set; } | ||||
|  | ||||
|     private List<ReferenceDescription> Actived | ||||
|     { | ||||
| @@ -73,7 +78,7 @@ public partial class OPCUAImportVariable | ||||
|         //动态加载子项时,导出内容需要添加手动加载代码 | ||||
|         foreach (var node in Selected.ToList()) | ||||
|         { | ||||
|             List<OPCUATagModel> nodes = await PopulateBranchAsync((NodeId)node.NodeId, true); | ||||
|             List<OPCUATagModel> nodes = await PopulateBranchAsync((NodeId)node.NodeId, true, IsShowSubvariable); | ||||
|             if (nodes.Count > 0) | ||||
|             { | ||||
|                 Selected.AddRange(nodes.SelectMany(a => a.GetAllTags()).Select(a => a.Tag).Where(a => a != null).ToList()); | ||||
| @@ -165,7 +170,7 @@ public partial class OPCUAImportVariable | ||||
|     { | ||||
|         Task.Run(async () => | ||||
|         { | ||||
|             Nodes = await PopulateBranchAsync(ObjectIds.ObjectsFolder); | ||||
|             Nodes = await PopulateBranchAsync(ObjectIds.ObjectsFolder, isShowSubvariable: IsShowSubvariable); | ||||
|             overlay = false; | ||||
|             await InvokeAsync(StateHasChanged); | ||||
|         }); | ||||
| @@ -206,10 +211,10 @@ public partial class OPCUAImportVariable | ||||
|     private async Task PopulateBranchAsync(OPCUATagModel model) | ||||
|     { | ||||
|         var sourceId = (NodeId)model.Tag.NodeId; | ||||
|         model.Nodes = await PopulateBranchAsync(sourceId); | ||||
|         model.Nodes = await PopulateBranchAsync(sourceId, isShowSubvariable: IsShowSubvariable); | ||||
|     } | ||||
|  | ||||
|     private async Task<List<OPCUATagModel>> PopulateBranchAsync(NodeId sourceId, bool isAll = false) | ||||
|     private async Task<List<OPCUATagModel>> PopulateBranchAsync(NodeId sourceId, bool isAll = false, bool isShowSubvariable = false) | ||||
|     { | ||||
|         if (!PLC.Connected) | ||||
|         { | ||||
| @@ -232,13 +237,13 @@ public partial class OPCUAImportVariable | ||||
|                     Name = Utils.Format("{0}", target), | ||||
|                     Tag = target | ||||
|                 }; | ||||
|                 //if (target.NodeClass != NodeClass.Variable) | ||||
|                 if (isShowSubvariable || target.NodeClass != NodeClass.Variable) | ||||
|                 { | ||||
|                     var data = await GetReferenceDescriptionCollectionAsync((NodeId)target.NodeId); | ||||
|                     if (data != null && data.Count > 0) | ||||
|                     { | ||||
|                         if (isAll) | ||||
|                             child.Nodes = await PopulateBranchAsync((NodeId)target.NodeId); | ||||
|                             child.Nodes = await PopulateBranchAsync((NodeId)target.NodeId, isShowSubvariable: IsShowSubvariable); | ||||
|                         else | ||||
|                             child.Nodes = new(); | ||||
|                     } | ||||
| @@ -247,10 +252,32 @@ public partial class OPCUAImportVariable | ||||
|                         child.Nodes = null; | ||||
|                     } | ||||
|                 } | ||||
|                 //else | ||||
|                 else | ||||
|                 { | ||||
|                     child.Nodes = null; | ||||
|                 } | ||||
|  | ||||
|  | ||||
|  | ||||
|                 ////if (target.NodeClass != NodeClass.Variable)  //这个判断注释后会让子节点也是变量的情况下无法加载 | ||||
|                 //{ | ||||
|                 //    var data = await GetReferenceDescriptionCollectionAsync((NodeId)target.NodeId); | ||||
|                 //    if (data != null && data.Count > 0) | ||||
|                 //    { | ||||
|                 //        if (isAll) | ||||
|                 //            child.Nodes = await PopulateBranchAsync((NodeId)target.NodeId); | ||||
|                 //        else | ||||
|                 //            child.Nodes = new(); | ||||
|                 //    } | ||||
|                 //    else | ||||
|                 //    { | ||||
|                 //        child.Nodes = null; | ||||
|                 //    } | ||||
|                 //} | ||||
|                 ////else | ||||
|                 ////{ | ||||
|                 ////    child.Nodes = null; | ||||
|                 ////} | ||||
|                 list.Add(child); | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -0,0 +1,15 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| global using ThingsGateway.Foundation.Core; | ||||
| global using ThingsGateway.Gateway.Application; | ||||
| global using ThingsGateway.Gateway.Core; | ||||
							
								
								
									
										325
									
								
								framework/Plugin/ThingsGateway.Plugin.QuestDB/QuestDB.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										325
									
								
								framework/Plugin/ThingsGateway.Plugin.QuestDB/QuestDB.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,325 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Furion; | ||||
|  | ||||
| using Mapster; | ||||
|  | ||||
| using Microsoft.Extensions.Hosting; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using System.Reflection; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension; | ||||
| using ThingsGateway.Foundation.Extension.ConcurrentQueue; | ||||
| using ThingsGateway.Foundation.Extension.String; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.QuestDB; | ||||
| public class QuestDB : UpLoadBase | ||||
| { | ||||
|     private readonly ConcurrentQueue<QuestDBHistoryValue> DeviceVariableRunTimes = new(); | ||||
|     private readonly QuestDBProperty driverPropertys = new(); | ||||
|     private readonly QuestDBVariableProperty variablePropertys = new(); | ||||
|     private TypeAdapterConfig _config; | ||||
|     private GlobalDeviceData _globalDeviceData; | ||||
|  | ||||
|     private List<DeviceVariableRunTime> _uploadVariables = new(); | ||||
|     private TimerTick exTimerTick; | ||||
|     public QuestDB() | ||||
|     { | ||||
|         _config = new TypeAdapterConfig(); | ||||
|         _config.ForType<DeviceVariableRunTime, HistoryValue>() | ||||
|             .Map(dest => dest.Value, (src) => ValueReturn(src)) | ||||
|             .Map(dest => dest.CollectTime, (src) => src.CollectTime.ToUniversalTime())//注意sqlsugar插入时无时区,直接utc时间 | ||||
|             .Map(dest => dest.CreateTime, (src) => DateTime.UtcNow);//注意sqlsugar插入时无时区,直接utc时间 | ||||
|     } | ||||
|     private static object ValueReturn(DeviceVariableRunTime src) | ||||
|     { | ||||
|         if (src.Value?.ToString()?.IsBoolValue() == true) | ||||
|         { | ||||
|             if (src.Value.ToBoolean()) | ||||
|             { | ||||
|                 return 1; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return 0; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return src.Value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public override Type DriverDebugUIType => null; | ||||
|     public override UpDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|     public override List<DeviceVariableRunTime> UploadVariables => _uploadVariables; | ||||
|  | ||||
|     public override VariablePropertyBase VariablePropertys => variablePropertys; | ||||
|  | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     public override async Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         SqlSugarClient db = GetHisDbAsync(); | ||||
|         db.DbMaintenance.CreateDatabase(); | ||||
|         db.CodeFirst.InitTables(typeof(QuestDBHistoryValue)); | ||||
|         await Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     public override async Task ExecuteAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         var db = GetHisDbAsync(); | ||||
|         { | ||||
|             if (!driverPropertys.IsInterval) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     ////变化推送 | ||||
|                     var varList = DeviceVariableRunTimes.ToListWithDequeue(); | ||||
|                     if (varList?.Count != 0) | ||||
|                     { | ||||
|                         await InserableAsync(db, varList, cancellationToken); | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     LogMessage?.LogWarning(ex); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (exTimerTick.IsTickHappen()) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         var varList = _uploadVariables.ToList().Adapt<List<QuestDBHistoryValue>>(_config); | ||||
|                         if (varList?.Count != 0) | ||||
|                         { | ||||
|                             await InserableAsync(db, varList, cancellationToken); | ||||
|                         } | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         LogMessage?.LogWarning(ex); | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await Task.Delay(driverPropertys.CycleInterval - UploadDeviceThread.CycleInterval, cancellationToken); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public override bool IsConnected() => _uploadVariables?.Count > 0; | ||||
|  | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             _globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange); | ||||
|             _uploadVariables = null; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogError(ex); | ||||
|         } | ||||
|  | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|  | ||||
|     protected override void Init(UploadDeviceRunTime device) | ||||
|     { | ||||
|  | ||||
|         _globalDeviceData = App.GetService<GlobalDeviceData>(); | ||||
|  | ||||
|         var tags = _globalDeviceData.AllVariables.Where(a => a.VariablePropertys.ContainsKey(device.Id)) | ||||
|            .Where(b => GetPropertyValue(b, nameof(variablePropertys.Enable)).ToBoolean()) | ||||
|            .ToList(); | ||||
|  | ||||
|         _uploadVariables = tags; | ||||
|  | ||||
|         if (!driverPropertys.IsInterval) | ||||
|         { | ||||
|             _uploadVariables.ForEach(a => | ||||
|             { | ||||
|                 a.VariableValueChange += VariableValueChange; | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         if (_uploadVariables.Count == 0) | ||||
|         { | ||||
|             LogMessage.LogWarning("插件变量数量为0"); | ||||
|         } | ||||
|         if (driverPropertys.IntervalTime < 1) | ||||
|             driverPropertys.IntervalTime = 10; | ||||
|         exTimerTick = new(driverPropertys.IntervalTime * 1000); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Aop设置 | ||||
|     /// </summary> | ||||
|     /// <param name="db"></param> | ||||
|     private static void AopSetting(SqlSugarClient db) | ||||
|     { | ||||
|         var config = db.CurrentConnectionConfig; | ||||
|  | ||||
|         // 设置超时时间 | ||||
|         db.Ado.CommandTimeOut = 30; | ||||
|  | ||||
|         // 打印SQL语句 | ||||
|         db.Aop.OnLogExecuting = (sql, pars) => | ||||
|         { | ||||
|             //如果不是开发环境就打印sql | ||||
|             if (App.HostEnvironment.IsDevelopment()) | ||||
|             { | ||||
|                 if (sql.StartsWith("SELECT")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Green; | ||||
|                 } | ||||
|                 if (sql.StartsWith("UPDATE")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Yellow; | ||||
|                 } | ||||
|                 if (sql.StartsWith("INSERT")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Blue; | ||||
|                 } | ||||
|                 if (sql.StartsWith("DELETE")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Red; | ||||
|                 } | ||||
|                 WriteSqlLog(UtilMethods.GetSqlString(config.DbType, sql, pars)); | ||||
|                 Console.ForegroundColor = ConsoleColor.White; | ||||
|                 Console.WriteLine(); | ||||
|             } | ||||
|         }; | ||||
|         //异常 | ||||
|         db.Aop.OnError = (ex) => | ||||
|         { | ||||
|             //如果不是开发环境就打印日志 | ||||
|             if (App.WebHostEnvironment.IsDevelopment()) | ||||
|             { | ||||
|                 if (ex.Parametres == null) return; | ||||
|                 Console.ForegroundColor = ConsoleColor.Red; | ||||
|                 var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value)); | ||||
|                 WriteSqlLogError(UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres)); | ||||
|                 Console.ForegroundColor = ConsoleColor.White; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private static void WriteSqlLog(string msg) | ||||
|     { | ||||
|         Console.WriteLine("【Sql执行时间】:" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat()); | ||||
|         Console.WriteLine("【Sql语句】:" + msg + Environment.NewLine); | ||||
|     } | ||||
|  | ||||
|     private static void WriteSqlLogError(string msg) | ||||
|     { | ||||
|         Console.WriteLine("【Sql执行错误时间】:" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat()); | ||||
|         Console.WriteLine("【Sql语句】:" + msg + Environment.NewLine); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取数据库链接 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     private SqlSugarClient GetHisDbAsync() | ||||
|     { | ||||
|         var configureExternalServices = new ConfigureExternalServices | ||||
|         { | ||||
|             EntityService = (type, column) => // 修改列可空-1、带?问号 2、String类型若没有Required | ||||
|             { | ||||
|                 if ((type.PropertyType.IsGenericType && type.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) | ||||
|                     || (type.PropertyType == typeof(string) && type.GetCustomAttribute<RequiredAttribute>() == null)) | ||||
|                     column.IsNullable = true; | ||||
|             }, | ||||
|         }; | ||||
|         var sqlSugarClient = new SqlSugarClient(new ConnectionConfig() | ||||
|         { | ||||
|             ConnectionString = driverPropertys.ConnectStr,//连接字符串 | ||||
|             DbType = DbType.QuestDB,//数据库类型 | ||||
|             IsAutoCloseConnection = true, //不设成true要手动close | ||||
|             ConfigureExternalServices = configureExternalServices, | ||||
|         } | ||||
|         ); | ||||
|         AopSetting(sqlSugarClient);//aop配置 | ||||
|         return sqlSugarClient; | ||||
|     } | ||||
|  | ||||
|     private async Task InserableAsync(SqlSugarClient db, List<QuestDBHistoryValue> dbInserts, CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var result = await db.Insertable(dbInserts).ExecuteCommandAsync(cancellationToken); | ||||
|             if (result > 0) | ||||
|                 LogMessage.Trace(FoundationConst.LogMessageHeader + dbInserts.ToJsonString()); | ||||
|             //连接成功时补发缓存数据 | ||||
|             var cacheData = await CacheDb.GetCacheData(); | ||||
|             foreach (var item in cacheData) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     var data = item.CacheStr.FromJsonString<List<QuestDBHistoryValue>>(); | ||||
|                     var cacheresult = await db.Insertable(data).ExecuteCommandAsync(cancellationToken); | ||||
|                     if (cacheresult > 0) | ||||
|                     { | ||||
|                         await CacheDb.DeleteCacheData(item.Id); | ||||
|                         LogMessage.Trace(FoundationConst.LogMessageHeader + $"主题:{item.Topic}{Environment.NewLine}负载:{item.CacheStr}"); | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                                                 LogMessage.LogWarning(ex); | ||||
|                 } | ||||
|  | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|                                         LogMessage.LogWarning(ex); | ||||
|             await CacheDb.AddCacheData("", dbInserts.ToJsonString(), driverPropertys.CacheMaxCount); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private void VariableValueChange(DeviceVariableRunTime collectVariableRunTime) | ||||
|     { | ||||
|         if (!driverPropertys.IsInterval) | ||||
|             DeviceVariableRunTimes.Enqueue(collectVariableRunTime.Adapt<QuestDBHistoryValue>(_config)); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -0,0 +1,62 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.ComponentModel; | ||||
|  | ||||
| namespace ThingsGateway.Gateway.Core; | ||||
| /// <summary> | ||||
| /// 历史数据表 | ||||
| /// </summary> | ||||
| [SugarTable("historyValue", TableDescription = "历史数据表")] | ||||
| public class QuestDBHistoryValue | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 采集时间 | ||||
|     /// </summary> | ||||
|     [TimeDbSplitField(DateType.Month)] | ||||
|     [Description("采集时间")] | ||||
|     public DateTime CollectTime { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 上传时间 | ||||
|     /// </summary> | ||||
|     [Description("上传时间")] | ||||
|     public DateTime CreateTime { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 设备名称 | ||||
|     /// </summary> | ||||
|     [SugarColumn(ColumnDataType = "symbol")] | ||||
|     [Description("设备名称")] | ||||
|     public string DeviceName { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 变量名称 | ||||
|     /// </summary> | ||||
|     [SugarColumn(ColumnDataType = "symbol")] | ||||
|     [Description("变量名称")] | ||||
|     public string Name { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 是否在线 | ||||
|     /// </summary> | ||||
|     [Description("是否在线")] | ||||
|     public bool IsOnline { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 变量值 | ||||
|     /// </summary> | ||||
|     [Description("变量值")] | ||||
|     public double Value { get; set; } | ||||
| } | ||||
| @@ -0,0 +1,31 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.QuestDB; | ||||
|  | ||||
| public class QuestDBProperty : UpDriverPropertyBase | ||||
| { | ||||
|     [DeviceProperty("链接字符串", "")] public string ConnectStr { get; set; } = "host=localhost;port=8812;username=admin;password=quest;database=qdb;ServerCompatibilityMode=NoTypeLoading;"; | ||||
|     [DeviceProperty("是否间隔插入", "False时将每次变化写入")] public bool IsInterval { get; set; } = true; | ||||
|     [DeviceProperty("间隔时间", "秒,实时表时代表更新间隔,历史表时代表插入间隔")] public int IntervalTime { get; set; } = 10; | ||||
|     [DeviceProperty("缓存最大条数", "默认2千条")] public int CacheMaxCount { get; set; } = 2000; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 线程循环间隔 | ||||
|     /// </summary> | ||||
|     [DeviceProperty("线程循环间隔", "最小10ms")] | ||||
|     public int CycleInterval { get; set; } = 1000; | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -0,0 +1,22 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.QuestDB; | ||||
|  | ||||
| public class QuestDBVariableProperty : VariablePropertyBase | ||||
| { | ||||
|     [VariableProperty("启用", "")] | ||||
|     public bool Enable { get; set; } = true; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -0,0 +1,14 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|  | ||||
| 	  | ||||
| 	 | ||||
| 	<Target Name="PostBuild" AfterTargets="PostBuildEvent"> | ||||
|  | ||||
| 		<Exec Command=" set dir="$(SolutionDir)Web\ThingsGateway.Web.Entry\bin\$(Configuration)\$(TargetFramework)\Plugins\$(AssemblyName)"
 if not exist %25dir%25  md %25dir%25  
copy "$(TargetDir)*QuestDB*.dll"  %25dir%25



" /> | ||||
| 	 | ||||
| 	</Target> | ||||
|  | ||||
|  | ||||
|    | ||||
|  | ||||
| </Project> | ||||
| @@ -93,7 +93,7 @@ public class RabbitMQClient : UpLoadBase | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     LogMessage?.LogWarning(ex, ToString()); | ||||
|                     LogMessage?.LogWarning(ex); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @@ -124,7 +124,7 @@ public class RabbitMQClient : UpLoadBase | ||||
|                             } | ||||
|                             catch (Exception ex) | ||||
|                             { | ||||
|                                 LogMessage?.LogWarning(ex, ToString()); | ||||
|                                 LogMessage?.LogWarning(ex); | ||||
|                             } | ||||
|  | ||||
|                         } | ||||
| @@ -150,7 +150,7 @@ public class RabbitMQClient : UpLoadBase | ||||
|                             } | ||||
|                             catch (Exception ex) | ||||
|                             { | ||||
|                                 LogMessage?.LogWarning(ex, ToString()); | ||||
|                                 LogMessage?.LogWarning(ex); | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
| @@ -188,7 +188,7 @@ public class RabbitMQClient : UpLoadBase | ||||
|                                     } | ||||
|                                     catch (Exception ex) | ||||
|                                     { | ||||
|                                         LogMessage?.LogWarning(ex, ToString()); | ||||
|                                         LogMessage?.LogWarning(ex); | ||||
|                                     } | ||||
|  | ||||
|                                 } | ||||
| @@ -214,7 +214,7 @@ public class RabbitMQClient : UpLoadBase | ||||
|                                     } | ||||
|                                     catch (Exception ex) | ||||
|                                     { | ||||
|                                         LogMessage?.LogWarning(ex, ToString()); | ||||
|                                         LogMessage?.LogWarning(ex); | ||||
|                                     } | ||||
|                                 } | ||||
|  | ||||
| @@ -224,7 +224,7 @@ public class RabbitMQClient : UpLoadBase | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         LogMessage?.LogWarning(ex, ToString()); | ||||
|                         LogMessage?.LogWarning(ex); | ||||
|                     } | ||||
|  | ||||
|  | ||||
| @@ -233,7 +233,7 @@ public class RabbitMQClient : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|         try | ||||
|         { | ||||
| @@ -257,7 +257,7 @@ public class RabbitMQClient : UpLoadBase | ||||
|                             } | ||||
|                             catch (Exception ex) | ||||
|                             { | ||||
|                                 LogMessage?.LogWarning(ex, ToString()); | ||||
|                                 LogMessage?.LogWarning(ex); | ||||
|                             } | ||||
|  | ||||
|                         } | ||||
| @@ -275,7 +275,7 @@ public class RabbitMQClient : UpLoadBase | ||||
|                             } | ||||
|                             catch (Exception ex) | ||||
|                             { | ||||
|                                 LogMessage?.LogWarning(ex, ToString()); | ||||
|                                 LogMessage?.LogWarning(ex); | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
| @@ -305,7 +305,7 @@ public class RabbitMQClient : UpLoadBase | ||||
|                                 } | ||||
|                                 catch (Exception ex) | ||||
|                                 { | ||||
|                                     LogMessage?.LogWarning(ex, ToString()); | ||||
|                                     LogMessage?.LogWarning(ex); | ||||
|                                 } | ||||
|  | ||||
|                             } | ||||
| @@ -323,7 +323,7 @@ public class RabbitMQClient : UpLoadBase | ||||
|                                 } | ||||
|                                 catch (Exception ex) | ||||
|                                 { | ||||
|                                     LogMessage?.LogWarning(ex, ToString()); | ||||
|                                     LogMessage?.LogWarning(ex); | ||||
|                                 } | ||||
|                             } | ||||
|  | ||||
| @@ -338,7 +338,7 @@ public class RabbitMQClient : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|  | ||||
|         if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50) | ||||
| @@ -482,7 +482,7 @@ public class RabbitMQClient : UpLoadBase | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex, ToString()); | ||||
|             LogMessage?.LogWarning(ex); | ||||
|             await CacheDb.AddCacheData(queueName, data, driverPropertys.CacheMaxCount); | ||||
|         } | ||||
|  | ||||
|   | ||||
							
								
								
									
										15
									
								
								framework/Plugin/ThingsGateway.Plugin.SQLDB/GlobalUsings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								framework/Plugin/ThingsGateway.Plugin.SQLDB/GlobalUsings.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| global using ThingsGateway.Foundation.Core; | ||||
| global using ThingsGateway.Gateway.Application; | ||||
| global using ThingsGateway.Gateway.Core; | ||||
							
								
								
									
										334
									
								
								framework/Plugin/ThingsGateway.Plugin.SQLDB/SQLDB.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										334
									
								
								framework/Plugin/ThingsGateway.Plugin.SQLDB/SQLDB.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,334 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Furion; | ||||
|  | ||||
| using Mapster; | ||||
|  | ||||
| using Microsoft.Extensions.Hosting; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using System.Reflection; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension.ConcurrentQueue; | ||||
| using ThingsGateway.Foundation.Extension.String; | ||||
|  | ||||
| using Yitter.IdGenerator; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.SQLDB; | ||||
| public class SQLDB : UpLoadBase | ||||
| { | ||||
|     private readonly ConcurrentQueue<SQLHistoryValue> DeviceVariableRunTimes = new(); | ||||
|     private readonly SQLDBProperty driverPropertys = new(); | ||||
|     private readonly SQLDBVariableProperty variablePropertys = new(); | ||||
|     private TypeAdapterConfig _config; | ||||
|     private GlobalDeviceData _globalDeviceData; | ||||
|  | ||||
|     private List<DeviceVariableRunTime> _uploadVariables = new(); | ||||
|     private TimerTick exTimerTick; | ||||
|     private TimerTick exRealTimerTick; | ||||
|     public SQLDB() | ||||
|     { | ||||
|         _config = new TypeAdapterConfig(); | ||||
|         _config.ForType<DeviceVariableRunTime, SQLHistoryValue>() | ||||
| .Map(dest => dest.Id, (src) => YitIdHelper.NextId()) | ||||
| .Map(dest => dest.CreateTime, (src) => DateTime.Now); | ||||
|     } | ||||
|  | ||||
|     public override Type DriverDebugUIType => null; | ||||
|     public override UpDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|     public override List<DeviceVariableRunTime> UploadVariables => _uploadVariables; | ||||
|  | ||||
|     public override VariablePropertyBase VariablePropertys => variablePropertys; | ||||
|  | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     public override async Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         var db = GetHisDbAsync(); | ||||
|         db.CodeFirst.InitTables(typeof(SQLHistoryValue)); | ||||
|         db.MappingTables.Add("SQLRealValue", driverPropertys.ReadDBTableName); // typeof(类).Name 可以拿到类名 | ||||
|         db.CodeFirst.InitTables(typeof(SQLRealValue)); //生成的表名是 newTableName | ||||
|         await Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     public override async Task ExecuteAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         var db = GetHisDbAsync(); | ||||
|  | ||||
|         if (driverPropertys.IsReadDB) | ||||
|         { | ||||
|             if (exRealTimerTick.IsTickHappen()) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     var varList = _uploadVariables.ToList().Adapt<List<SQLRealValue>>(); | ||||
|                     if (varList?.Count != 0) | ||||
|                     { | ||||
|                         //var result = await db.Storageable(varList).As(driverPropertys.ReadDBTableName).ExecuteCommandAsync(cancellationToken); | ||||
|                         await db.Fastest<SQLRealValue>().AS(driverPropertys.ReadDBTableName).PageSize(100000).BulkUpdateAsync(varList); | ||||
|  | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     LogMessage?.LogWarning(ex); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|  | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|  | ||||
|             if (!driverPropertys.IsInterval) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     ////变化推送 | ||||
|                     var varList = DeviceVariableRunTimes.ToListWithDequeue(); | ||||
|                     if (varList?.Count != 0) | ||||
|                     { | ||||
|                         await InserableAsync(db, varList, cancellationToken); | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     LogMessage?.LogWarning(ex); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (exTimerTick.IsTickHappen()) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         var varList = _uploadVariables.ToList().Adapt<List<SQLHistoryValue>>(_config); | ||||
|                         if (varList?.Count != 0) | ||||
|                         { | ||||
|                             await InserableAsync(db, varList, cancellationToken); | ||||
|                         } | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         LogMessage?.LogWarning(ex); | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await Task.Delay(driverPropertys.CycleInterval - UploadDeviceThread.CycleInterval, cancellationToken); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取数据库链接 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public SqlSugarClient GetHisDbAsync() | ||||
|     { | ||||
|         var configureExternalServices = new ConfigureExternalServices | ||||
|         { | ||||
|             EntityService = (type, column) => // 修改列可空-1、带?问号 2、String类型若没有Required | ||||
|             { | ||||
|                 if ((type.PropertyType.IsGenericType && type.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) | ||||
|                     || (type.PropertyType == typeof(string) && type.GetCustomAttribute<RequiredAttribute>() == null)) | ||||
|                     column.IsNullable = true; | ||||
|             }, | ||||
|         }; | ||||
|         var sqlSugarClient = new SqlSugarClient(new ConnectionConfig() | ||||
|         { | ||||
|             ConnectionString = driverPropertys.ConnectStr,//连接字符串 | ||||
|             DbType = driverPropertys.DbType,//数据库类型 | ||||
|             IsAutoCloseConnection = true, //不设成true要手动close | ||||
|             ConfigureExternalServices = configureExternalServices, | ||||
|         } | ||||
|         ); | ||||
|         AopSetting(sqlSugarClient);//aop配置 | ||||
|         return sqlSugarClient; | ||||
|     } | ||||
|  | ||||
|     public override bool IsConnected() => _uploadVariables?.Count > 0; | ||||
|  | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             _globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange); | ||||
|             _uploadVariables = null; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogError(ex); | ||||
|         } | ||||
|  | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|  | ||||
|     protected override void Init(UploadDeviceRunTime device) | ||||
|     { | ||||
|  | ||||
|         _globalDeviceData = App.GetService<GlobalDeviceData>(); | ||||
|  | ||||
|         var tags = _globalDeviceData.AllVariables.Where(a => a.VariablePropertys.ContainsKey(device.Id)) | ||||
|            .Where(b => GetPropertyValue(b, nameof(variablePropertys.Enable)).ToBoolean()) | ||||
|            .ToList(); | ||||
|  | ||||
|         _uploadVariables = tags; | ||||
|  | ||||
|         if (!driverPropertys.IsReadDB) | ||||
|             if (!driverPropertys.IsInterval) | ||||
|             { | ||||
|                 _uploadVariables.ForEach(a => | ||||
|                 { | ||||
|                     a.VariableValueChange += VariableValueChange; | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|         if (_uploadVariables.Count == 0) | ||||
|         { | ||||
|             LogMessage.LogWarning("插件变量数量为0"); | ||||
|         } | ||||
|         if (driverPropertys.IntervalTime < 1) | ||||
|             driverPropertys.IntervalTime = 10; | ||||
|         exTimerTick = new(driverPropertys.IntervalTime * 1000); | ||||
|         exRealTimerTick = new(driverPropertys.IntervalTime * 1000); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Aop设置 | ||||
|     /// </summary> | ||||
|     /// <param name="db"></param> | ||||
|     private static void AopSetting(SqlSugarClient db) | ||||
|     { | ||||
|         var config = db.CurrentConnectionConfig; | ||||
|  | ||||
|         // 设置超时时间 | ||||
|         db.Ado.CommandTimeOut = 30; | ||||
|  | ||||
|         // 打印SQL语句 | ||||
|         db.Aop.OnLogExecuting = (sql, pars) => | ||||
|         { | ||||
|             //如果不是开发环境就打印sql | ||||
|             if (App.HostEnvironment.IsDevelopment()) | ||||
|             { | ||||
|                 if (sql.StartsWith("SELECT")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Green; | ||||
|                 } | ||||
|                 if (sql.StartsWith("UPDATE")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Yellow; | ||||
|                 } | ||||
|                 if (sql.StartsWith("INSERT")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Blue; | ||||
|                 } | ||||
|                 if (sql.StartsWith("DELETE")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Red; | ||||
|                 } | ||||
|                 WriteSqlLog(UtilMethods.GetSqlString(config.DbType, sql, pars)); | ||||
|                 Console.ForegroundColor = ConsoleColor.White; | ||||
|                 Console.WriteLine(); | ||||
|             } | ||||
|         }; | ||||
|         //异常 | ||||
|         db.Aop.OnError = (ex) => | ||||
|         { | ||||
|             //如果不是开发环境就打印日志 | ||||
|             if (App.WebHostEnvironment.IsDevelopment()) | ||||
|             { | ||||
|                 if (ex.Parametres == null) return; | ||||
|                 Console.ForegroundColor = ConsoleColor.Red; | ||||
|                 var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value)); | ||||
|                 WriteSqlLogError(UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres)); | ||||
|                 Console.ForegroundColor = ConsoleColor.White; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|     } | ||||
|     private static void WriteSqlLog(string msg) | ||||
|     { | ||||
|         Console.WriteLine("【Sql执行时间】:" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat()); | ||||
|         Console.WriteLine("【Sql语句】:" + msg + Environment.NewLine); | ||||
|     } | ||||
|     private static void WriteSqlLogError(string msg) | ||||
|     { | ||||
|         Console.WriteLine("【Sql执行错误时间】:" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat()); | ||||
|         Console.WriteLine("【Sql语句】:" + msg + Environment.NewLine); | ||||
|     } | ||||
|     private async Task InserableAsync(SqlSugarClient db, List<SQLHistoryValue> dbInserts, CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var result = await db.Insertable(dbInserts).SplitTable().ExecuteCommandAsync(); | ||||
|             if (result > 0) | ||||
|                 LogMessage.Trace(FoundationConst.LogMessageHeader + dbInserts.ToJsonString()); | ||||
|             //连接成功时补发缓存数据 | ||||
|             var cacheData = await CacheDb.GetCacheData(); | ||||
|             foreach (var item in cacheData) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     var data = item.CacheStr.FromJsonString<List<SQLHistoryValue>>(); | ||||
|                     var cacheresult = await db.Insertable(data).SplitTable().ExecuteCommandAsync(); | ||||
|                     if (cacheresult > 0) | ||||
|                     { | ||||
|                         await CacheDb.DeleteCacheData(item.Id); | ||||
|                         LogMessage.Trace(FoundationConst.LogMessageHeader + $"主题:{item.Topic}{Environment.NewLine}负载:{item.CacheStr}"); | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                                                 LogMessage.LogWarning(ex); | ||||
|                 } | ||||
|  | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|                                         LogMessage.LogWarning(ex); | ||||
|             await CacheDb.AddCacheData("", dbInserts.ToJsonString(), driverPropertys.CacheMaxCount); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private void VariableValueChange(DeviceVariableRunTime collectVariableRunTime) | ||||
|     { | ||||
|         if (!driverPropertys.IsReadDB) | ||||
|             if (!driverPropertys.IsInterval) | ||||
|                 DeviceVariableRunTimes.Enqueue(collectVariableRunTime.Adapt<SQLHistoryValue>(_config)); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										37
									
								
								framework/Plugin/ThingsGateway.Plugin.SQLDB/SQLDBProperty.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								framework/Plugin/ThingsGateway.Plugin.SQLDB/SQLDBProperty.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.SQLDB; | ||||
|  | ||||
| public class SQLDBProperty : UpDriverPropertyBase | ||||
| { | ||||
|     [DeviceProperty("是否实时表", "true=>实时表更新,false=>历史存储(按月分表)")] public bool IsReadDB { get; set; } = false; | ||||
|     [DeviceProperty("实时表名称", "")] public string ReadDBTableName { get; set; } = "ReadDBTableName"; | ||||
|  | ||||
|     [DeviceProperty("数据库类型", "MySql/SqlServer")] public DbType DbType { get; set; } = DbType.MySql; | ||||
|     [DeviceProperty("链接字符串", "")] public string ConnectStr { get; set; } = "server=localhost;Database=test;Uid=root;Pwd=111111;AllowLoadLocalInfile=true;"; | ||||
|     [DeviceProperty("是否间隔插入", "False时将每次变化写入")] public bool IsInterval { get; set; } = true; | ||||
|     [DeviceProperty("间隔时间", "秒,实时表时代表更新间隔,历史表时代表插入间隔")] public int IntervalTime { get; set; } = 10; | ||||
|     [DeviceProperty("缓存最大条数", "默认2千条")] public int CacheMaxCount { get; set; } = 2000; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 线程循环间隔 | ||||
|     /// </summary> | ||||
|     [DeviceProperty("线程循环间隔", "最小10ms")] | ||||
|     public int CycleInterval { get; set; } = 1000; | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -0,0 +1,22 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.SQLDB; | ||||
|  | ||||
| public class SQLDBVariableProperty : VariablePropertyBase | ||||
| { | ||||
|     [VariableProperty("启用", "")] | ||||
|     public bool Enable { get; set; } = true; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -0,0 +1,56 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.SQLDB; | ||||
|  | ||||
| [SplitTable(SplitType.Month)]//按月分表 (自带分表支持 年、季、月、周、日) | ||||
| [SugarTable("historyValue_{year}{month}{day}", TableDescription = "设备采集历史表")]//3个变量必须要有 | ||||
| [SugarIndex("index_Name", nameof(SQLHistoryValue.Name), OrderByType.Desc)] | ||||
| [SugarIndex("index_DeviceName", nameof(SQLHistoryValue.DeviceName), OrderByType.Desc)] | ||||
| [SugarIndex("index_CollectTime", nameof(SQLHistoryValue.CollectTime), OrderByType.Desc)] | ||||
| public class SQLHistoryValue | ||||
| { | ||||
|     [SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)] | ||||
|     public long Id { get; set; } | ||||
|     /// <summary> | ||||
|     /// 变量名称 | ||||
|     /// </summary> | ||||
|     [SugarColumn(ColumnName = "Name", ColumnDescription = "变量名称")] | ||||
|     public string Name { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 设备名称 | ||||
|     /// </summary> | ||||
|     [SugarColumn(ColumnName = "DeviceName", ColumnDescription = "设备名称")] | ||||
|     public string DeviceName { get; set; } | ||||
|  | ||||
|     ///<summary> | ||||
|     ///实时值 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "Value", ColumnDescription = "实时值")] | ||||
|     public string Value { get; set; } | ||||
|  | ||||
|     ///<summary> | ||||
|     ///是否在线 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "IsOnline", ColumnDescription = "是否在线 True=在线;False=离线")] | ||||
|     public bool IsOnline { get; set; } | ||||
|  | ||||
|  | ||||
|     public DateTime CollectTime { get; set; } | ||||
|  | ||||
|     [SplitField] //分表字段 在插入的时候会根据这个字段插入哪个表,在更新删除的时候用这个字段找出相关表 | ||||
|     public DateTime CreateTime { get; set; } | ||||
| } | ||||
|  | ||||
							
								
								
									
										52
									
								
								framework/Plugin/ThingsGateway.Plugin.SQLDB/SQLRealValue.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								framework/Plugin/ThingsGateway.Plugin.SQLDB/SQLRealValue.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.SQLDB; | ||||
|  | ||||
| [SugarTable(TableDescription = "设备采集实时表")] | ||||
| [SugarIndex("index_Name", nameof(SQLHistoryValue.Name), OrderByType.Desc)] | ||||
| [SugarIndex("index_DeviceName", nameof(SQLHistoryValue.DeviceName), OrderByType.Desc)] | ||||
| [SugarIndex("index_CollectTime", nameof(SQLHistoryValue.CollectTime), OrderByType.Desc)] | ||||
| public class SQLRealValue | ||||
| { | ||||
|     [SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)] | ||||
|     public long Id { get; set; } | ||||
|     /// <summary> | ||||
|     /// 变量名称 | ||||
|     /// </summary> | ||||
|     [SugarColumn(ColumnName = "Name", ColumnDescription = "变量名称")] | ||||
|     public string Name { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 设备名称 | ||||
|     /// </summary> | ||||
|     [SugarColumn(ColumnName = "DeviceName", ColumnDescription = "设备名称")] | ||||
|     public string DeviceName { get; set; } | ||||
|  | ||||
|     ///<summary> | ||||
|     ///实时值 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "Value", ColumnDescription = "实时值")] | ||||
|     public string Value { get; set; } | ||||
|  | ||||
|     ///<summary> | ||||
|     ///是否在线 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "IsOnline", ColumnDescription = "是否在线 True=在线;False=离线")] | ||||
|     public bool IsOnline { get; set; } | ||||
|  | ||||
|     public DateTime CollectTime { get; set; } | ||||
|  | ||||
| } | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user