From 47e442874c49ed0e6d2b43fc0089d15bbb1643a0 Mon Sep 17 00:00:00 2001 From: "2248356998 qq.com" <2248356998@qq.com> Date: Sat, 18 Oct 2025 23:14:55 +0800 Subject: [PATCH] =?UTF-8?q?pref:=20=E5=BC=82=E6=AD=A5=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E6=9C=BA=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/AdminTable.razor.cs | 4 +- .../Collections/ObjectPoolLock.cs | 4 +- .../LinkedCancellationTokenSourceCache.cs | 3 +- .../ReusableCancellationTokenSource.cs | 22 +- .../Common/WaitLock.cs | 69 +- .../ManualResetValueTaskSourceCore.cs | 3 +- .../ExpressionsToSql/Common/SugarParameter.cs | 1 - src/Directory.Build.props | 8 +- .../VariableObject.cs | 38 +- .../Channel/DDP/DDPTcpSessionClientChannel.cs | 116 +-- .../Channel/DDP/DDPUdpSessionChannel.cs | 97 +- .../Channel/Plugin/DtuPlugin.cs | 87 +- .../Plugin/HeartbeatAndReceivePlugin.cs | 9 +- .../Device/DeviceBase.cs | 191 ++-- .../Common/AsyncReadWriteLock.cs | 42 +- .../Common/Tasks/DoTask.cs | 146 --- .../Business/Cache/BusinessBaseWithCache.cs | 831 ++++++++++-------- .../Driver/Collect/CollectBase.cs | 406 +++++---- .../Driver/Collect/CollectFoundationBase.cs | 168 ++-- .../GlobalData/GlobalData.cs | 67 +- .../Model/VariableMethod.cs | 65 +- .../Model/VariableRunTime.cs | 15 +- .../Services/RulesEngineHostedService.cs | 96 +- .../ChannelDeviceTree.razor.cs | 29 +- .../ChannelDeviceTreeItem.cs | 73 +- .../Benchmark/BenchmarkAsyncWaitData.cs | 426 +++++++++ .../Program.cs | 23 +- .../Slave/ModbusSlave.cs | 275 +++--- .../OpcUaMaster/OpcUaMaster.cs | 9 +- .../S7/SiemensS7Master.cs | 280 +++--- .../QuestDB/QuestDBProducer.other.cs | 150 ++-- .../SqlDB/SqlDBProducer.cs | 51 +- .../SqlDB/SqlDbProducer.other.cs | 207 +++-- .../TDengineDB/TDengineDBProducer.other.cs | 125 +-- .../Webhook/Webhook.other.cs | 88 +- .../Kafka/KafkaProducer.other.cs | 117 +-- .../ModbusSlave/ModbusSlave.cs | 149 ++-- .../MqttClient/MqttClient.cs | 48 +- .../MqttClient/MqttClient.other.cs | 213 +++-- .../MqttCollect/MqttCollect.cs | 33 +- .../MqttCollect/MqttCollect.other.cs | 33 +- .../MqttServer/MqttServer.other.cs | 255 +++--- .../OpcDaMaster/OpcDaMaster.cs | 1 - .../OpcUaMaster/OpcUaMaster.cs | 129 +-- .../OpcUaServer/OpcUaServer.cs | 83 +- .../RabbitMQ/RabbitMQProducer.cs | 55 +- .../RabbitMQ/RabbitMQProducer.other.cs | 75 +- .../SiemensS7Master/SiemensS7Master.cs | 113 +-- .../Test/TestCollectPlugin.cs | 2 +- 49 files changed, 3222 insertions(+), 2308 deletions(-) rename src/{Foundation/ThingsGateway.Foundation/Utils => Admin/ThingsGateway.NewLife.X/Common}/LinkedCancellationTokenSourceCache.cs (97%) rename src/{Foundation/ThingsGateway.Foundation/Utils => Admin/ThingsGateway.NewLife.X/Common}/ReusableCancellationTokenSource.cs (77%) delete mode 100644 src/Gateway/ThingsGateway.Gateway.Application/Common/Tasks/DoTask.cs create mode 100644 src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/BenchmarkAsyncWaitData.cs diff --git a/src/Admin/ThingsGateway.Admin.Razor/Components/AdminTable.razor.cs b/src/Admin/ThingsGateway.Admin.Razor/Components/AdminTable.razor.cs index b54699684..cde69af96 100644 --- a/src/Admin/ThingsGateway.Admin.Razor/Components/AdminTable.razor.cs +++ b/src/Admin/ThingsGateway.Admin.Razor/Components/AdminTable.razor.cs @@ -15,11 +15,11 @@ public partial class AdminTable where TItem : class, new() { /// [Parameter] - public Func OnColumnVisibleChanged { get; set; } + public Func OnColumnVisibleChanged { get; set; } /// [Parameter] - public Func,Task> OnColumnCreating { get; set; } + public Func, Task> OnColumnCreating { get; set; } /// [Parameter] public TableRenderMode RenderMode { get; set; } diff --git a/src/Admin/ThingsGateway.NewLife.X/Collections/ObjectPoolLock.cs b/src/Admin/ThingsGateway.NewLife.X/Collections/ObjectPoolLock.cs index d05be2027..31dccc7f2 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Collections/ObjectPoolLock.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Collections/ObjectPoolLock.cs @@ -118,7 +118,7 @@ public class ObjectPoolLock : DisposeBase, IPool where T : class if (count == 0) Init(); #if DEBUG - WriteLog("Acquire Create Free={0} Busy={1}", FreeCount, count + 1); + WriteLog("Acquire Create Free={0} Busy={1}", FreeCount, count + 1); #endif } } @@ -156,7 +156,7 @@ public class ObjectPoolLock : DisposeBase, IPool where T : class if (!_busy.Remove(value)) { #if DEBUG - WriteLog("Return Error"); + WriteLog("Return Error"); #endif return false; diff --git a/src/Foundation/ThingsGateway.Foundation/Utils/LinkedCancellationTokenSourceCache.cs b/src/Admin/ThingsGateway.NewLife.X/Common/LinkedCancellationTokenSourceCache.cs similarity index 97% rename from src/Foundation/ThingsGateway.Foundation/Utils/LinkedCancellationTokenSourceCache.cs rename to src/Admin/ThingsGateway.NewLife.X/Common/LinkedCancellationTokenSourceCache.cs index 803edb095..bcb17f854 100644 --- a/src/Foundation/ThingsGateway.Foundation/Utils/LinkedCancellationTokenSourceCache.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Common/LinkedCancellationTokenSourceCache.cs @@ -8,7 +8,7 @@ // QQ群:605534569 //------------------------------------------------------------------------------ -namespace ThingsGateway.Foundation; +namespace ThingsGateway.NewLife; public class LinkedCancellationTokenSourceCache : IDisposable { @@ -63,6 +63,7 @@ public class LinkedCancellationTokenSourceCache : IDisposable _cachedCts?.Dispose(); _cachedCts = null!; } + GC.SuppressFinalize(this); } } diff --git a/src/Foundation/ThingsGateway.Foundation/Utils/ReusableCancellationTokenSource.cs b/src/Admin/ThingsGateway.NewLife.X/Common/ReusableCancellationTokenSource.cs similarity index 77% rename from src/Foundation/ThingsGateway.Foundation/Utils/ReusableCancellationTokenSource.cs rename to src/Admin/ThingsGateway.NewLife.X/Common/ReusableCancellationTokenSource.cs index 20a61d46c..3a5d9c463 100644 --- a/src/Foundation/ThingsGateway.Foundation/Utils/ReusableCancellationTokenSource.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Common/ReusableCancellationTokenSource.cs @@ -10,13 +10,18 @@ // 感谢您的下载和使用 //------------------------------------------------------------------------------ -namespace ThingsGateway.Foundation; +namespace ThingsGateway.NewLife; using System; using System.Threading; public sealed class ReusableCancellationTokenSource : IDisposable { + ~ReusableCancellationTokenSource() + { + Dispose(); + } + private readonly Timer _timer; private CancellationTokenSource? _cts; @@ -47,7 +52,7 @@ public sealed class ReusableCancellationTokenSource : IDisposable /// /// 获取一个 CTS,并启动超时 /// - public CancellationTokenSource GetTokenSource(long timeout, CancellationToken external1 = default, CancellationToken external2 = default, CancellationToken external3 = default) + public CancellationToken GetTokenSource(long timeout, CancellationToken external1 = default, CancellationToken external2 = default, CancellationToken external3 = default) { TimeoutStatus = false; @@ -57,7 +62,7 @@ public sealed class ReusableCancellationTokenSource : IDisposable // 启动 Timer _timer.Change(timeout, Timeout.Infinite); - return _cts; + return _cts.Token; } @@ -71,15 +76,16 @@ public sealed class ReusableCancellationTokenSource : IDisposable /// public void Cancel() { - _cts?.SafeCancel(); + try { _cts?.Cancel(); } catch { } } public void Dispose() { - _cts?.SafeCancel(); - _cts?.SafeDispose(); - _linkedCtsCache.SafeDispose(); - _timer.SafeDispose(); + try { _cts?.Cancel(); } catch { } + try { _cts?.Dispose(); } catch { } + try { _linkedCtsCache?.Dispose(); } catch { } + try { _timer?.Dispose(); } catch { } + GC.SuppressFinalize(this); } } diff --git a/src/Admin/ThingsGateway.NewLife.X/Common/WaitLock.cs b/src/Admin/ThingsGateway.NewLife.X/Common/WaitLock.cs index 2fb531a75..b17a0c5a2 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Common/WaitLock.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Common/WaitLock.cs @@ -8,6 +8,8 @@ // QQ群:605534569 //------------------------------------------------------------------------------ +using ThingsGateway.NewLife.Collections; + namespace ThingsGateway.NewLife; /// @@ -93,15 +95,20 @@ public sealed class WaitLock : IDisposable #if NET6_0_OR_GREATER if (cancellationToken.CanBeCanceled) return WaitUntilCountOrTimeoutAsync(_waiterLock.WaitAsync(Timeout.Infinite, CancellationToken.None), Timeout.Infinite, cancellationToken); + //return WaitUntilAsync2(_waiterLock.WaitAsync(Timeout.Infinite, CancellationToken.None), Timeout.Infinite, cancellationToken); else return _waiterLock.WaitAsync(Timeout.Infinite, CancellationToken.None); #else - return _waiterLock.WaitAsync(Timeout.Infinite,cancellationToken); + return _waiterLock.WaitAsync(Timeout.Infinite, cancellationToken); #endif } #if NET6_0_OR_GREATER + + + //private ObjectPoolLock _reusableTimeouts = new(); + /// Performs the asynchronous wait. /// The asynchronous waiter. /// The timeout. @@ -118,6 +125,61 @@ public sealed class WaitLock : IDisposable return (asyncWaiter.WaitAsync(TimeSpan.FromMilliseconds(millisecondsTimeout), cancellationToken)); } } + + //private Task WaitUntilAsync2(Task task, int timeoutMs, CancellationToken token) + //{ + // if (task.IsCompleted) return task; + + // var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + // var reusableTimeout = _reusableTimeouts.Get(); + + // CancellationTokenRegistration ctr = default; + + // // 超时 + 取消 Token + // if (timeoutMs != Timeout.Infinite || token.CanBeCanceled) + // { + // var ctsToken = reusableTimeout.GetTokenSource(timeoutMs, token); + + // ctr = ctsToken.Register(static (state, token2) => + // { + // var (tcs2, ctoken) = ((TaskCompletionSource, CancellationToken))state!; + // if (ctoken.IsCancellationRequested) + // tcs2.TrySetCanceled(ctoken); + // else + // tcs2.TrySetException(new TimeoutException("The operation has timed out.")); + // }, (tcs, token)); + // } + + // if (task.IsCompleted) + // { + // _reusableTimeouts.Return(reusableTimeout); + // ctr.Dispose(); + // return task; + // } + + // // 监听原始任务 + // task.ContinueWith(static (t, state) => + // { + // var (tcs2, ctr2, ctsPool, cts) = ((TaskCompletionSource, CancellationTokenRegistration, ObjectPoolLock, ReusableCancellationTokenSource))state!; + // try + // { + // if (t.IsCanceled) + // tcs2.TrySetCanceled(); + // else if (t.IsFaulted) + // tcs2.TrySetException(t.Exception!.InnerExceptions); + // else + // tcs2.TrySetResult(null); + // } + // finally + // { + // ctsPool.Return(cts); + // ctr2.Dispose(); + // } + // }, (tcs, ctr, _reusableTimeouts, reusableTimeout), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + + // return tcs.Task; + //} + #endif /// @@ -132,7 +194,7 @@ public sealed class WaitLock : IDisposable return _waiterLock.WaitAsync(Timeout.Infinite, CancellationToken.None); #else - return _waiterLock.WaitAsync(millisecondsTimeout,cancellationToken); + return _waiterLock.WaitAsync(millisecondsTimeout, cancellationToken); #endif } @@ -140,6 +202,9 @@ public sealed class WaitLock : IDisposable public void Dispose() { DisposedValue = true; +#if NET6_0_OR_GREATER + //_reusableTimeouts?.TryDispose(); +#endif _waiterLock?.TryDispose(); GC.SuppressFinalize(this); } diff --git a/src/Admin/ThingsGateway.NewLife.X/PooledAwait/Internal/ManualResetValueTaskSourceCore.cs b/src/Admin/ThingsGateway.NewLife.X/PooledAwait/Internal/ManualResetValueTaskSourceCore.cs index 758832f1f..62cde5c26 100644 --- a/src/Admin/ThingsGateway.NewLife.X/PooledAwait/Internal/ManualResetValueTaskSourceCore.cs +++ b/src/Admin/ThingsGateway.NewLife.X/PooledAwait/Internal/ManualResetValueTaskSourceCore.cs @@ -56,7 +56,8 @@ namespace PooledAwait.Internal /// Whether the current operation has completed. private bool _completed; /// The result with which the operation succeeded, or the default value if it hasn't yet completed or failed. - /* [AllowNull, MaybeNull] */ private TResult _result; + /* [AllowNull, MaybeNull] */ + private TResult _result; /// The exception with which the operation failed, or null if it hasn't yet completed or completed successfully. private ExceptionDispatchInfo? _error; /// The current version of this value, used to help prevent misuse. diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/SugarParameter.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/SugarParameter.cs index 73d0e028d..bdfd56d50 100644 --- a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/SugarParameter.cs +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/SugarParameter.cs @@ -1,6 +1,5 @@ using System.Data; using System.Data.Common; -using System.Numerics; using ThingsGateway.NewLife.Reflection; namespace ThingsGateway.SqlSugar diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 29c307f08..4dd9fd735 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,9 +1,9 @@ - 10.11.111 - 10.11.111 - 10.11.111 + 10.11.114 + 10.11.114 + 10.11.114 10.11.6 10.11.6 8.0.21 @@ -12,7 +12,7 @@ false 10.11.87 10.11.87 - 4.0.0-beta.135 + 4.0.0-beta.140 diff --git a/src/Foundation/ThingsGateway.Foundation.Variable/VariableObject.cs b/src/Foundation/ThingsGateway.Foundation.Variable/VariableObject.cs index 8d6040cb5..d7206f301 100644 --- a/src/Foundation/ThingsGateway.Foundation.Variable/VariableObject.cs +++ b/src/Foundation/ThingsGateway.Foundation.Variable/VariableObject.cs @@ -11,6 +11,8 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using PooledAwait; + using System.Linq.Expressions; using ThingsGateway.Gateway.Application.Extensions; @@ -139,18 +141,27 @@ public abstract class VariableObject /// /// 特性连读,反射赋值到继承类中的属性 /// - public virtual async ValueTask MultiReadAsync(CancellationToken cancellationToken = default) + public virtual ValueTask MultiReadAsync(CancellationToken cancellationToken = default) { try { GetVariableSources(); //连读 - foreach (var item in DeviceVariableSourceReads) + return MultiReadAsync(this, cancellationToken); + } + catch (Exception ex) + { + return EasyValueTask.FromResult(new OperResult(ex)); + } + + static async PooledValueTask MultiReadAsync(VariableObject @this, CancellationToken cancellationToken) + { + foreach (var item in @this.DeviceVariableSourceReads) { - var result = await Device.ReadAsync(item.RegisterAddress, item.Length, cancellationToken).ConfigureAwait(false); + var result = await @this.Device.ReadAsync(item.RegisterAddress, item.Length, cancellationToken).ConfigureAwait(false); if (result.IsSuccess) { - var result1 = item.VariableRuntimes.PraseStructContent(Device, result.Content.Span, exWhenAny: true); + var result1 = item.VariableRuntimes.PraseStructContent(@this.Device, result.Content.Span, exWhenAny: true); if (!result1.IsSuccess) { item.LastErrorMessage = result1.ErrorMessage; @@ -168,13 +179,9 @@ public abstract class VariableObject } } - SetValue(); + @this.SetValue(); return OperResult.Success; } - catch (Exception ex) - { - return new OperResult(ex); - } } /// @@ -205,30 +212,31 @@ public abstract class VariableObject /// 属性名称,必须使用特性 /// 写入值 /// 取消令箭 - public virtual async ValueTask WriteValueAsync(string propertyName, object value, CancellationToken cancellationToken = default) + public virtual ValueTask WriteValueAsync(string propertyName, object value, CancellationToken cancellationToken = default) { try { GetVariableSources(); if (string.IsNullOrEmpty(propertyName)) { - return new OperResult($"PropertyName cannot be null or empty."); + return EasyValueTask.FromResult(new OperResult($"PropertyName cannot be null or empty.")); } if (!VariableRuntimePropertyDict.TryGetValue(propertyName, out var variableRuntimeProperty)) { - return new OperResult($"This attribute is not recognized and may not have been identified using the {typeof(VariableRuntimeAttribute)} attribute"); + return EasyValueTask.FromResult(new OperResult($"This attribute is not recognized and may not have been identified using the {typeof(VariableRuntimeAttribute)} attribute")); } JToken jToken = GetExpressionsValue(value, variableRuntimeProperty); - var result = await Device.WriteJTokenAsync(variableRuntimeProperty.VariableClass.RegisterAddress, jToken, variableRuntimeProperty.VariableClass.DataType, cancellationToken).ConfigureAwait(false); - return result; + return Device.WriteJTokenAsync(variableRuntimeProperty.VariableClass.RegisterAddress, jToken, variableRuntimeProperty.VariableClass.DataType, cancellationToken); } catch (Exception ex) { - return new OperResult(ex); + return EasyValueTask.FromResult(new OperResult(ex)); } + + } /// diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPTcpSessionClientChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPTcpSessionClientChannel.cs index c65ea2da8..904bd0129 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPTcpSessionClientChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPTcpSessionClientChannel.cs @@ -8,6 +8,8 @@ // QQ群:605534569 //------------------------------------------------------------------------------ +using PooledAwait; + using System.Runtime.CompilerServices; using TouchSocket.Resources; @@ -138,82 +140,86 @@ public class DDPTcpSessionClientChannel : TcpSessionClientChannel private DeviceSingleStreamDataHandleAdapter DDPAdapter = new(); - protected override async ValueTask OnTcpReceiving(IBytesReader byteBlock) + protected override ValueTask OnTcpReceiving(IBytesReader byteBlock) { if (DDPAdapter.TryParseRequest(ref byteBlock, out var message)) { - return true; + return EasyValueTask.FromResult(true); } + return OnTcpReceiving(this, message); - if (message != null) + static async PooledValueTask OnTcpReceiving(DDPTcpSessionClientChannel @this, DDPTcpMessage message) { - if (message.IsSuccess) + if (message != null) { - var id = $"ID={message.Id}"; - if (message.Type == 0x09) + if (message.IsSuccess) { - var reader = new ClassBytesReader(message.Content); - - if (this.DataHandlingAdapter == null) + var id = $"ID={message.Id}"; + if (message.Type == 0x09) { - await this.OnTcpReceived(new ReceivedDataEventArgs(message.Content, default)).ConfigureAwait(false); + var reader = new ClassBytesReader(message.Content); + + if (@this.DataHandlingAdapter == null) + { + await @this.OnTcpReceived(new ReceivedDataEventArgs(message.Content, default)).ConfigureAwait(false); + } + else + { + await @this.DataHandlingAdapter.ReceivedInputAsync(reader).ConfigureAwait(false); + } + + return true; } else { - await this.DataHandlingAdapter.ReceivedInputAsync(reader).ConfigureAwait(false); - } - - return true; - } - else - { - if (message.Type == 0x01) - { - bool log = false; - if (id != Id) log = true; - - //注册ID - if (Service is ITcpServiceChannel tcpService && tcpService.TryGetClient(id, out var oldClient) && oldClient != this) + if (message.Type == 0x01) { - Logger?.Debug($"Old socket connections with the same ID {id} will be closed"); - try - { - //await oldClient.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false); - await oldClient.CloseAsync().ConfigureAwait(false); - } - catch - { - } - try - { - oldClient.Dispose(); - } - catch + bool log = false; + if (id != @this.Id) log = true; + + //注册ID + if (@this.Service is ITcpServiceChannel tcpService && tcpService.TryGetClient(id, out var oldClient) && oldClient != @this) { + @this.Logger?.Debug($"Old socket connections with the same ID {id} will be closed"); + try + { + //await oldClient.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false); + await oldClient.CloseAsync().ConfigureAwait(false); + } + catch + { + } + try + { + oldClient.Dispose(); + } + catch + { + } } + + await @this.ResetIdAsync(id, @this.ClosedToken).ConfigureAwait(false); + + //发送成功 + await @this.ProtectedSendAsync(new DDPSend(ReadOnlyMemory.Empty, id, true, 0x81), @this.ClosedToken).ConfigureAwait(false); + if (log) + @this.Logger?.Info(string.Format(AppResource.DtuConnected, @this.Id)); + } + else if (message.Type == 0x02) + { + await @this.ProtectedSendAsync(new DDPSend(ReadOnlyMemory.Empty, @this.Id, true, 0x82), @this.ClosedToken).ConfigureAwait(false); + @this.Logger?.Info(string.Format(AppResource.DtuDisconnecting, @this.Id)); + await Task.Delay(100).ConfigureAwait(false); + await @this.CloseAsync().ConfigureAwait(false); + @this.SafeDispose(); } - - await ResetIdAsync(id, ClosedToken).ConfigureAwait(false); - - //发送成功 - await base.ProtectedSendAsync(new DDPSend(ReadOnlyMemory.Empty, id, true, 0x81), ClosedToken).ConfigureAwait(false); - if (log) - Logger?.Info(string.Format(AppResource.DtuConnected, Id)); - } - else if (message.Type == 0x02) - { - await base.ProtectedSendAsync(new DDPSend(ReadOnlyMemory.Empty, Id, true, 0x82), ClosedToken).ConfigureAwait(false); - Logger?.Info(string.Format(AppResource.DtuDisconnecting, Id)); - await Task.Delay(100).ConfigureAwait(false); - await this.CloseAsync().ConfigureAwait(false); - this.SafeDispose(); } } } - } - return true; + return true; + } } #region Throw diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPUdpSessionChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPUdpSessionChannel.cs index 0ae5ff376..6660393d3 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPUdpSessionChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPUdpSessionChannel.cs @@ -8,6 +8,8 @@ // QQ群:605534569 //------------------------------------------------------------------------------ +using PooledAwait; + using System.Collections.Concurrent; using System.Net; using System.Runtime.CompilerServices; @@ -80,73 +82,78 @@ public class DDPUdpSessionChannel : UdpSessionChannel, IClientChannel, IDtuUdpSe } - protected override async ValueTask OnUdpReceiving(UdpReceiveingEventArgs e) + protected override ValueTask OnUdpReceiving(UdpReceiveingEventArgs e) { var byteBlock = e.Memory; var endPoint = e.EndPoint; if (!DDPAdapter.TryParseRequest(endPoint, byteBlock, out var message)) - return true; + return EasyValueTask.FromResult(true); - if (message != null) + return OnUdpReceiving(this, endPoint, message); + + static async PooledValueTask OnUdpReceiving(DDPUdpSessionChannel @this, EndPoint endPoint, DDPUdpMessage message) { - if (message.IsSuccess) + if (message != null) { - var id = $"ID={message.Id}"; - if (message.Type == 0x09) + if (message.IsSuccess) { - if (this.DataHandlingAdapter == null) + var id = $"ID={message.Id}"; + if (message.Type == 0x09) { - await this.OnUdpReceived(new UdpReceivedDataEventArgs(endPoint, message.Content, default)).ConfigureAwait(false); + if (@this.DataHandlingAdapter == null) + { + await @this.OnUdpReceived(new UdpReceivedDataEventArgs(endPoint, message.Content, default)).ConfigureAwait(false); + } + else + { + await @this.DataHandlingAdapter.ReceivedInputAsync(endPoint, message.Content).ConfigureAwait(false); + } + + return true; } else { - await this.DataHandlingAdapter.ReceivedInputAsync(endPoint, message.Content).ConfigureAwait(false); - } + if (message.Type == 0x01) + { + bool log = false; - return true; - } - else - { - if (message.Type == 0x01) - { - bool log = false; + //注册ID + if (!@this.IdDict.TryAdd(endPoint, id)) + { + @this.IdDict[endPoint] = id; + } + else + { + log = true; + } + if (!@this.EndPointDcit.TryAdd(id, endPoint)) + { + @this.EndPointDcit[id] = endPoint; + } + else + { + log = true; + } - //注册ID - if (!IdDict.TryAdd(endPoint, id)) - { - IdDict[endPoint] = id; + //发送成功 + await @this.DDPAdapter.SendInputAsync(endPoint, new DDPSend(ReadOnlyMemory.Empty, id, false, 0x81), @this.ClosedToken).ConfigureAwait(false); + if (log) + @this.Logger?.Info(string.Format(AppResource.DtuConnected, id)); } - else + else if (message.Type == 0x02) { - log = true; + await @this.DDPAdapter.SendInputAsync(endPoint, new DDPSend(ReadOnlyMemory.Empty, id, false, 0x82), @this.ClosedToken).ConfigureAwait(false); + @this.Logger?.Info(string.Format(AppResource.DtuDisconnecting, id)); + await Task.Delay(100).ConfigureAwait(false); + @this.IdDict.TryRemove(endPoint, out _); + @this.EndPointDcit.TryRemove(id, out _); } - if (!EndPointDcit.TryAdd(id, endPoint)) - { - EndPointDcit[id] = endPoint; - } - else - { - log = true; - } - - //发送成功 - await DDPAdapter.SendInputAsync(endPoint, new DDPSend(ReadOnlyMemory.Empty, id, false, 0x81), ClosedToken).ConfigureAwait(false); - if (log) - Logger?.Info(string.Format(AppResource.DtuConnected, id)); - } - else if (message.Type == 0x02) - { - await DDPAdapter.SendInputAsync(endPoint, new DDPSend(ReadOnlyMemory.Empty, id, false, 0x82), ClosedToken).ConfigureAwait(false); - Logger?.Info(string.Format(AppResource.DtuDisconnecting, id)); - await Task.Delay(100).ConfigureAwait(false); - IdDict.TryRemove(endPoint, out _); - EndPointDcit.TryRemove(id, out _); } } } + return true; } - return true; } #region Throw diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/DtuPlugin.cs b/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/DtuPlugin.cs index 42a9f1818..736dedcba 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/DtuPlugin.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/DtuPlugin.cs @@ -8,6 +8,8 @@ // QQ群:605534569 //------------------------------------------------------------------------------ +using PooledAwait; + using System.Text; using ThingsGateway.Foundation.Extension.String; @@ -59,64 +61,71 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin public bool DtuIdHex { get; set; } /// - public async Task OnTcpReceiving(ITcpSession client, BytesReaderEventArgs e) + public Task OnTcpReceiving(ITcpSession client, BytesReaderEventArgs e) { - var len = HeartbeatByte.Length; - if (client is TcpSessionClientChannel socket && socket.Service is ITcpServiceChannel tcpServiceChannel) + return OnTcpReceiving(this, client, e); + + + static async PooledTask OnTcpReceiving(DtuPlugin @this, ITcpSession client, BytesReaderEventArgs e) { - if (!socket.Id.StartsWith("ID=")) + var len = @this.HeartbeatByte.Length; + if (client is TcpSessionClientChannel socket && socket.Service is ITcpServiceChannel tcpServiceChannel) { - var id = DtuIdHex ? $"ID={e.Reader.ToHexString()}" : $"ID={e.Reader.TotalSequence.ToString(Encoding.UTF8)}"; - if (tcpServiceChannel.TryGetClient(id, out var oldClient)) + if (!socket.Id.StartsWith("ID=")) + { + var id = @this.DtuIdHex ? $"ID={e.Reader.ToHexString()}" : $"ID={e.Reader.TotalSequence.ToString(Encoding.UTF8)}"; + if (tcpServiceChannel.TryGetClient(id, out var oldClient)) + { + try + { + await oldClient.CloseAsync().ConfigureAwait(false); + oldClient.Dispose(); + } + catch + { + } + } + await socket.ResetIdAsync(id, client.ClosedToken).ConfigureAwait(false); + client.Logger?.Info(string.Format(AppResource.DtuConnected, id)); + e.Reader.Advance((int)e.Reader.BytesRemaining); + e.Handled = true; + } + + if (!socket.Service.ClientExists(socket.Id)) { try { - await oldClient.CloseAsync().ConfigureAwait(false); - oldClient.Dispose(); + await socket.CloseAsync().ConfigureAwait(false); + socket.Dispose(); } catch { } - } - await socket.ResetIdAsync(id, client.ClosedToken).ConfigureAwait(false); - client.Logger?.Info(string.Format(AppResource.DtuConnected, id)); - e.Reader.Advance((int)e.Reader.BytesRemaining); - e.Handled = true; - } - if (!socket.Service.ClientExists(socket.Id)) - { - try - { - await socket.CloseAsync().ConfigureAwait(false); - socket.Dispose(); - } - catch - { + await e.InvokeNext().ConfigureAwait(false);//如果本插件无法处理当前数据,请将数据转至下一个插件。 + return; } - await e.InvokeNext().ConfigureAwait(false);//如果本插件无法处理当前数据,请将数据转至下一个插件。 - return; - } - - if (len > 0) - { - if (HeartbeatByte.Span.SequenceEqual(e.Reader.TotalSequence.Slice(0, (int)Math.Min(len, e.Reader.BytesRemaining + e.Reader.BytesRead)).First.Span)) + if (len > 0) { - if (DateTimeOffset.Now - socket.LastSentTime < TimeSpan.FromMilliseconds(200)) + if (@this.HeartbeatByte.Span.SequenceEqual(e.Reader.TotalSequence.Slice(0, (int)Math.Min(len, e.Reader.BytesRemaining + e.Reader.BytesRead)).First.Span)) { - await Task.Delay(200, client.ClosedToken).ConfigureAwait(false); + if (DateTimeOffset.Now - socket.LastSentTime < TimeSpan.FromMilliseconds(200)) + { + await Task.Delay(200, client.ClosedToken).ConfigureAwait(false); + } + //回应心跳包 + await socket.SendAsync(@this.HeartbeatByte, socket.ClosedToken).ConfigureAwait(false); + e.Reader.Advance((int)Math.Min(len, e.Reader.BytesRemaining)); + e.Handled = true; + if (socket.Logger?.LogLevel <= LogLevel.Trace) + socket.Logger?.Trace($"{socket}- Heartbeat"); } - //回应心跳包 - await socket.SendAsync(HeartbeatByte, socket.ClosedToken).ConfigureAwait(false); - e.Reader.Advance((int)Math.Min(len, e.Reader.BytesRemaining)); - e.Handled = true; - if (socket.Logger?.LogLevel <= LogLevel.Trace) - socket.Logger?.Trace($"{socket}- Heartbeat"); } } + await e.InvokeNext().ConfigureAwait(false);//如果本插件无法处理当前数据,请将数据转至下一个插件。 + return; } - await e.InvokeNext().ConfigureAwait(false);//如果本插件无法处理当前数据,请将数据转至下一个插件。 } diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/HeartbeatAndReceivePlugin.cs b/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/HeartbeatAndReceivePlugin.cs index d7cffffa6..91c031022 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/HeartbeatAndReceivePlugin.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/HeartbeatAndReceivePlugin.cs @@ -167,14 +167,14 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi await e.InvokeNext().ConfigureAwait(false); } - public async Task OnTcpReceiving(ITcpSession client, BytesReaderEventArgs e) + public Task OnTcpReceiving(ITcpSession client, BytesReaderEventArgs e) { if (client is ITcpSessionClient) { - return;//此处可判断,如果为服务器,则不用使用心跳。 + return Task.CompletedTask;//此处可判断,如果为服务器,则不用使用心跳。 } - if (DtuId.IsNullOrWhiteSpace()) return; + if (DtuId.IsNullOrWhiteSpace()) return Task.CompletedTask; if (client is ITcpClient tcpClient) { @@ -187,8 +187,9 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi e.Handled = true; } } - await e.InvokeNext().ConfigureAwait(false);//如果本插件无法处理当前数据,请将数据转至下一个插件。 + return e.InvokeNext();//如果本插件无法处理当前数据,请将数据转至下一个插件。 } + return Task.CompletedTask; } diff --git a/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs b/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs index d70276e9a..032f4611a 100644 --- a/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs +++ b/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs @@ -331,21 +331,25 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice } public bool AutoConnect { get; protected set; } = true; /// - private async Task SendAsync(ISendMessage sendMessage, IClientChannel channel, CancellationToken token = default) + private Task SendAsync(ISendMessage sendMessage, IClientChannel channel, CancellationToken token = default) { + return SendAsync(this, sendMessage, channel, token); - if (SendDelayTime != 0) - await Task.Delay(SendDelayTime, token).ConfigureAwait(false); - - if (channel is IDtuUdpSessionChannel udpSession) + static async PooledTask SendAsync(DeviceBase @this, ISendMessage sendMessage, IClientChannel channel, CancellationToken token) { - EndPoint? endPoint = GetUdpEndpoint(); - await udpSession.SendAsync(endPoint, sendMessage, token).ConfigureAwait(false); + if (@this.SendDelayTime != 0) + await Task.Delay(@this.SendDelayTime, token).ConfigureAwait(false); - } - else - { - await channel.SendAsync(sendMessage, token).ConfigureAwait(false); + if (channel is IDtuUdpSessionChannel udpSession) + { + EndPoint? endPoint = @this.GetUdpEndpoint(); + await udpSession.SendAsync(endPoint, sendMessage, token).ConfigureAwait(false); + + } + else + { + await channel.SendAsync(sendMessage, token).ConfigureAwait(false); + } } } @@ -365,59 +369,69 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice private WaitLock connectWaitLock = new(nameof(DeviceBase)); - public async ValueTask ConnectAsync(CancellationToken token) + public ValueTask ConnectAsync(CancellationToken token) { - if (AutoConnect && Channel != null && Channel?.Online != true) + return ConnectAsync(this, token); + + static async PooledValueTask ConnectAsync(DeviceBase @this, CancellationToken token) { - try + if (@this.AutoConnect && @this.Channel != null && @this.Channel?.Online != true) { - await connectWaitLock.WaitAsync(token).ConfigureAwait(false); - if (AutoConnect && Channel != null && Channel?.Online != true) + try { - if (Channel.PluginManager == null) - await Channel.SetupAsync(Channel.Config.Clone()).ConfigureAwait(false); - await Channel.CloseAsync().ConfigureAwait(false); - using var ctsTime = new CancellationTokenSource(Channel.ChannelOptions.ConnectTimeout); - using var cts = CancellationTokenSource.CreateLinkedTokenSource(ctsTime.Token, token); - await Channel.ConnectAsync(cts.Token).ConfigureAwait(false); + await @this.connectWaitLock.WaitAsync(token).ConfigureAwait(false); + if (@this.AutoConnect && @this.Channel != null && @this.Channel?.Online != true) + { + if (@this.Channel.PluginManager == null) + await @this.Channel.SetupAsync(@this.Channel.Config.Clone()).ConfigureAwait(false); + await @this.Channel.CloseAsync().ConfigureAwait(false); + using var ctsTime = new CancellationTokenSource(@this.Channel.ChannelOptions.ConnectTimeout); + using var cts = CancellationTokenSource.CreateLinkedTokenSource(ctsTime.Token, token); + await @this.Channel.ConnectAsync(cts.Token).ConfigureAwait(false); + } + } + finally + { + @this.connectWaitLock.Release(); } - } - finally - { - connectWaitLock.Release(); } } } /// - public virtual async ValueTask SendAsync(ISendMessage sendMessage, CancellationToken cancellationToken) + public virtual ValueTask SendAsync(ISendMessage sendMessage, CancellationToken cancellationToken) { - try - { - var channelResult = GetChannel(); - if (!channelResult.IsSuccess) return new OperResult(channelResult); - WaitLock? waitLock = GetWaitLock(channelResult.Content); + return SendAsync(this, sendMessage, cancellationToken); + static async PooledValueTask SendAsync(DeviceBase @this, ISendMessage sendMessage, CancellationToken cancellationToken) + { try { - await BeforeSendAsync(channelResult.Content, cancellationToken).ConfigureAwait(false); + var channelResult = @this.GetChannel(); + if (!channelResult.IsSuccess) return new OperResult(channelResult); + WaitLock? waitLock = @this.GetWaitLock(channelResult.Content); - await waitLock.WaitAsync(cancellationToken).ConfigureAwait(false); - channelResult.Content.SetDataHandlingAdapterLogger(Logger); + try + { + await @this.BeforeSendAsync(channelResult.Content, cancellationToken).ConfigureAwait(false); + + await waitLock.WaitAsync(cancellationToken).ConfigureAwait(false); + channelResult.Content.SetDataHandlingAdapterLogger(@this.Logger); - await SendAsync(sendMessage, channelResult.Content, cancellationToken).ConfigureAwait(false); - return OperResult.Success; + await @this.SendAsync(sendMessage, channelResult.Content, cancellationToken).ConfigureAwait(false); + return OperResult.Success; + } + finally + { + waitLock.Release(); + } } - finally + catch (Exception ex) { - waitLock.Release(); + return new(ex); } } - catch (Exception ex) - { - return new(ex); - } } /// @@ -531,7 +545,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice } private ObjectPoolLock _reusableTimeouts = new(); - + /// /// 发送并等待数据 /// @@ -568,8 +582,8 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice try { - var cts = reusableTimeout.GetTokenSource(timeout, cancellationToken, @this.Channel.ClosedToken); - await waitData.WaitAsync(cts.Token).ConfigureAwait(false); + var ctsToken = reusableTimeout.GetTokenSource(timeout, cancellationToken, @this.Channel.ClosedToken); + await waitData.WaitAsync(ctsToken).ConfigureAwait(false); } catch (OperationCanceledException) @@ -659,54 +673,59 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice } /// - public virtual async ValueTask WriteJTokenAsync(string address, JToken value, DataTypeEnum dataType, CancellationToken cancellationToken = default) + public virtual ValueTask WriteJTokenAsync(string address, JToken value, DataTypeEnum dataType, CancellationToken cancellationToken = default) { - try + return WriteJTokenAsync(this, address, value, dataType, cancellationToken); + + static async PooledValueTask WriteJTokenAsync(DeviceBase @this, string address, JToken value, DataTypeEnum dataType, CancellationToken cancellationToken) { - var bitConverter = ThingsGatewayBitConverter.GetTransByAddress(address); - if (value is JArray jArray) + try { - return dataType switch + var bitConverter = @this.ThingsGatewayBitConverter.GetTransByAddress(address); + if (value is JArray jArray) { - DataTypeEnum.String => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), - DataTypeEnum.Boolean => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken).ConfigureAwait(false), - DataTypeEnum.Byte => await WriteAsync(address, jArray.ToObject().AsMemory(), dataType, cancellationToken).ConfigureAwait(false), - DataTypeEnum.Int16 => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), - DataTypeEnum.UInt16 => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), - DataTypeEnum.Int32 => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), - DataTypeEnum.UInt32 => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), - DataTypeEnum.Int64 => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), - DataTypeEnum.UInt64 => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), - DataTypeEnum.Float => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), - DataTypeEnum.Double => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), - DataTypeEnum.Decimal => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), - _ => new OperResult(string.Format(AppResource.DataTypeNotSupported, dataType)), - }; + return dataType switch + { + DataTypeEnum.String => await @this.WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.Boolean => await @this.WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken).ConfigureAwait(false), + DataTypeEnum.Byte => await @this.WriteAsync(address, jArray.ToObject().AsMemory(), dataType, cancellationToken).ConfigureAwait(false), + DataTypeEnum.Int16 => await @this.WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.UInt16 => await @this.WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.Int32 => await @this.WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.UInt32 => await @this.WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.Int64 => await @this.WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.UInt64 => await @this.WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.Float => await @this.WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.Double => await @this.WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.Decimal => await @this.WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + _ => new OperResult(string.Format(AppResource.DataTypeNotSupported, dataType)), + }; + } + else + { + return dataType switch + { + DataTypeEnum.String => await @this.WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), + DataTypeEnum.Boolean => await @this.WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), + DataTypeEnum.Byte => await @this.WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), + DataTypeEnum.Int16 => await @this.WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), + DataTypeEnum.UInt16 => await @this.WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), + DataTypeEnum.Int32 => await @this.WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), + DataTypeEnum.UInt32 => await @this.WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), + DataTypeEnum.Int64 => await @this.WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), + DataTypeEnum.UInt64 => await @this.WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), + DataTypeEnum.Float => await @this.WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), + DataTypeEnum.Double => await @this.WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), + DataTypeEnum.Decimal => await @this.WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), + _ => new OperResult(string.Format(AppResource.DataTypeNotSupported, dataType)), + }; + } } - else + catch (Exception ex) { - return dataType switch - { - DataTypeEnum.String => await WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), - DataTypeEnum.Boolean => await WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), - DataTypeEnum.Byte => await WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), - DataTypeEnum.Int16 => await WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), - DataTypeEnum.UInt16 => await WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), - DataTypeEnum.Int32 => await WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), - DataTypeEnum.UInt32 => await WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), - DataTypeEnum.Int64 => await WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), - DataTypeEnum.UInt64 => await WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), - DataTypeEnum.Float => await WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), - DataTypeEnum.Double => await WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), - DataTypeEnum.Decimal => await WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), - _ => new OperResult(string.Format(AppResource.DataTypeNotSupported, dataType)), - }; + return new OperResult(ex); } } - catch (Exception ex) - { - return new OperResult(ex); - } } #endregion 动态类型读写 diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Common/AsyncReadWriteLock.cs b/src/Gateway/ThingsGateway.Gateway.Application/Common/AsyncReadWriteLock.cs index b6fc8739b..5d7795a10 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Common/AsyncReadWriteLock.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Common/AsyncReadWriteLock.cs @@ -8,6 +8,8 @@ // QQ群:605534569 //------------------------------------------------------------------------------ +using PooledAwait; + using TouchSocket.Core; namespace ThingsGateway.Gateway.Application; @@ -28,22 +30,26 @@ public class AsyncReadWriteLock : IAsyncDisposable /// /// 获取读锁,支持多个线程并发读取,但写入时会阻止所有读取。 /// - public async ValueTask ReaderLockAsync(CancellationToken cancellationToken) + public ValueTask ReaderLockAsync(CancellationToken cancellationToken) { + return ReaderLockAsync(this, cancellationToken); - if (Interlocked.Read(ref _writerCount) > 0) + static async PooledValueTask ReaderLockAsync(AsyncReadWriteLock @this, CancellationToken cancellationToken) { - Interlocked.Increment(ref _readerCount); + if (Interlocked.Read(ref @this._writerCount) > 0) + { + Interlocked.Increment(ref @this._readerCount); - // 第一个读者需要获取写入锁,防止写操作 - await _readerLock.WaitOneAsync(cancellationToken).ConfigureAwait(false); + // 第一个读者需要获取写入锁,防止写操作 + await @this._readerLock.WaitOneAsync(cancellationToken).ConfigureAwait(false); - Interlocked.Decrement(ref _readerCount); + Interlocked.Decrement(ref @this._readerCount); + } + return @this._cancellationTokenSource.Token; } - return _cancellationTokenSource.Token; } public bool WriteWaited => _writerCount > 0; @@ -51,21 +57,25 @@ public class AsyncReadWriteLock : IAsyncDisposable /// /// 获取写锁,阻止所有读取。 /// - public async ValueTask WriterLockAsync(CancellationToken cancellationToken) + public ValueTask WriterLockAsync(CancellationToken cancellationToken) { + return WriterLockAsync(this); - if (Interlocked.Increment(ref _writerCount) == 1) + static async PooledValueTask WriterLockAsync(AsyncReadWriteLock @this) { - if (_writePriority) + if (Interlocked.Increment(ref @this._writerCount) == 1) { - var cancellationTokenSource = _cancellationTokenSource; - _cancellationTokenSource = new(); - await cancellationTokenSource.SafeCancelAsync().ConfigureAwait(false); // 取消读取 - cancellationTokenSource.SafeDispose(); + if (@this._writePriority) + { + var cancellationTokenSource = @this._cancellationTokenSource; + @this._cancellationTokenSource = new(); + await cancellationTokenSource.SafeCancelAsync().ConfigureAwait(false); // 取消读取 + cancellationTokenSource.SafeDispose(); + } } - } - return new Writer(this); + return new Writer(@this); + } } private object lockObject = new(); private void ReleaseWriter() diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Common/Tasks/DoTask.cs b/src/Gateway/ThingsGateway.Gateway.Application/Common/Tasks/DoTask.cs deleted file mode 100644 index 37052dfab..000000000 --- a/src/Gateway/ThingsGateway.Gateway.Application/Common/Tasks/DoTask.cs +++ /dev/null @@ -1,146 +0,0 @@ -////------------------------------------------------------------------------------ -//// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 -//// 此代码版权(除特别声明外的代码)归作者本人Diego所有 -//// 源代码使用协议遵循本仓库的开源协议及附加协议 -//// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway -//// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway -//// 使用文档:https://thingsgateway.cn/ -//// QQ群:605534569 -////------------------------------------------------------------------------------ - -//using Microsoft.Extensions.Logging; - -//using ThingsGateway.NewLife; - -//using TouchSocket.Core; - -//namespace ThingsGateway.Gateway.Application; - -//[ThingsGateway.DependencyInjection.SuppressSniffer] -//public class DoTask -//{ -// /// -// /// 取消令牌 -// /// -// private CancellationTokenSource? _cancelTokenSource; -// private object? _state; - -// public DoTask(Func doWork, ILog logger, object? state = null, string taskName = null) -// { -// DoWork = doWork; Logger = logger; TaskName = taskName; _state = state; -// } - -// /// -// /// 执行任务方法 -// /// -// public Func DoWork { get; } -// private ILog Logger { get; } -// private Task PrivateTask { get; set; } -// private string TaskName { get; } - -// /// -// /// 开始 -// /// -// /// 调度取消令牌 -// public void Start(CancellationToken cancellationToken) -// { -// try -// { -// WaitLock.Wait(cancellationToken); - -// if (cancellationToken.CanBeCanceled) -// { -// _cancelTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); -// } -// else -// { -// _cancelTokenSource = new CancellationTokenSource(); -// } - -// // 异步执行 -// PrivateTask = Do(); -// } -// finally -// { -// WaitLock.Release(); -// } -// } - -// private async Task Do() -// { -// await Task.Yield(); -// while (!_cancelTokenSource.IsCancellationRequested) -// { -// try -// { -// if (_cancelTokenSource.IsCancellationRequested) -// return; -// await DoWork(_state, _cancelTokenSource.Token).ConfigureAwait(false); -// } -// catch (OperationCanceledException) -// { -// } -// catch (ObjectDisposedException) -// { -// } -// catch (Exception ex) -// { -// Logger?.LogWarning(ex, "DoWork"); -// } -// } -// } - -// private WaitLock WaitLock = new(); -// /// -// /// 停止操作 -// /// -// public async Task StopAsync(TimeSpan? waitTime = null) -// { -// try -// { -// await WaitLock.WaitAsync().ConfigureAwait(false); - -// try -// { -// _cancelTokenSource?.Cancel(); -// _cancelTokenSource?.Dispose(); -// } -// catch (Exception ex) -// { -// Logger?.LogWarning(ex, "Cancel error"); -// } - -// if (PrivateTask != null) -// { -// try -// { -// if (TaskName != null) -// Logger?.LogInformation($"{TaskName} Stoping"); -// if (waitTime != null) -// await PrivateTask.WaitAsync(waitTime.Value).ConfigureAwait(false); -// if (TaskName != null) -// Logger?.LogInformation($"{TaskName} Stoped"); -// } -// catch (ObjectDisposedException) -// { -// } -// catch (TimeoutException) -// { -// if (TaskName != null) -// Logger?.LogWarning($"{TaskName} Stop timeout, exiting wait block"); -// } -// catch (Exception ex) -// { -// if (TaskName != null) -// Logger?.LogWarning(ex, $"{TaskName} Stop error"); -// } -// PrivateTask = null; - -// } -// } -// finally -// { -// WaitLock.Release(); -// } -// } -//} diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Driver/Business/Cache/BusinessBaseWithCache.cs b/src/Gateway/ThingsGateway.Gateway.Application/Driver/Business/Cache/BusinessBaseWithCache.cs index d976eda35..391873015 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Driver/Business/Cache/BusinessBaseWithCache.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Driver/Business/Cache/BusinessBaseWithCache.cs @@ -8,6 +8,8 @@ // QQ群:605534569 //------------------------------------------------------------------------------ +using PooledAwait; + using System.Collections.Concurrent; using ThingsGateway.Extension; @@ -64,51 +66,56 @@ public abstract class BusinessBaseWithCache : BusinessBase return base.InitChannelAsync(channel, cancellationToken); } - protected override async Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) + protected override Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) { - await Update(cancellationToken).ConfigureAwait(false); + return Update(cancellationToken); } - protected virtual async Task Update(CancellationToken cancellationToken) + protected virtual Task Update(CancellationToken cancellationToken) { - if (VarModelEnable) - { - await UpdateVarModelMemory(cancellationToken).ConfigureAwait(false); - await UpdateVarModelsMemory(cancellationToken).ConfigureAwait(false); - } + return Update(this, cancellationToken); - if (DevModelEnable) + static async PooledTask Update(BusinessBaseWithCache @this, CancellationToken cancellationToken) { - await UpdateDevModelMemory(cancellationToken).ConfigureAwait(false); - } + if (@this.VarModelEnable) + { + await @this.UpdateVarModelMemory(cancellationToken).ConfigureAwait(false); + await @this.UpdateVarModelsMemory(cancellationToken).ConfigureAwait(false); + } - if (AlarmModelEnable) - { - await UpdateAlarmModelMemory(cancellationToken).ConfigureAwait(false); - } - if (PluginEventDataModelEnable) - { - await UpdatePluginEventDataModelMemory(cancellationToken).ConfigureAwait(false); - } - if (VarModelEnable) - { - await UpdateVarModelCache(cancellationToken).ConfigureAwait(false); - await UpdateVarModelsCache(cancellationToken).ConfigureAwait(false); - } + if (@this.DevModelEnable) + { + await @this.UpdateDevModelMemory(cancellationToken).ConfigureAwait(false); + } - if (DevModelEnable) - { - await UpdateDevModelCache(cancellationToken).ConfigureAwait(false); - } + if (@this.AlarmModelEnable) + { + await @this.UpdateAlarmModelMemory(cancellationToken).ConfigureAwait(false); + } + if (@this.PluginEventDataModelEnable) + { + await @this.UpdatePluginEventDataModelMemory(cancellationToken).ConfigureAwait(false); + } + if (@this.VarModelEnable) + { + await @this.UpdateVarModelCache(cancellationToken).ConfigureAwait(false); + await @this.UpdateVarModelsCache(cancellationToken).ConfigureAwait(false); + } - if (AlarmModelEnable) - { - await UpdateAlarmModelCache(cancellationToken).ConfigureAwait(false); + if (@this.DevModelEnable) + { + await @this.UpdateDevModelCache(cancellationToken).ConfigureAwait(false); + } - } - if (PluginEventDataModelEnable) - { - await UpdatePluginEventDataModelCache(cancellationToken).ConfigureAwait(false); + if (@this.AlarmModelEnable) + { + await @this.UpdateAlarmModelCache(cancellationToken).ConfigureAwait(false); + } + if (@this.PluginEventDataModelEnable) + { + await @this.UpdatePluginEventDataModelCache(cancellationToken).ConfigureAwait(false); + + } } } #endregion @@ -349,106 +356,203 @@ public abstract class BusinessBaseWithCache : BusinessBase protected abstract ValueTask UpdatePluginEventDataModel(List> item, CancellationToken cancellationToken); - protected async Task UpdateAlarmModelCache(CancellationToken cancellationToken) + protected Task UpdateAlarmModelCache(CancellationToken cancellationToken) { - if (_businessPropertyWithCache.CacheEnable) - { - #region //成功上传时,补上传缓存数据 + return UpdateAlarmModelCache(this, cancellationToken); - if (IsConnected()) + static async PooledTask UpdateAlarmModelCache(BusinessBaseWithCache @this, CancellationToken cancellationToken) + { + if (@this._businessPropertyWithCache.CacheEnable) { - try + #region //成功上传时,补上传缓存数据 + + if (@this.IsConnected()) { - while (!cancellationToken.IsCancellationRequested) + try { - //循环获取,固定读最大行数量,执行完成需删除行 - var varList = await DBCacheAlarm.DBProvider.Queryable>().Take(_businessPropertyWithCache.SplitSize).ToListAsync(cancellationToken).ConfigureAwait(false); - if (varList.Count != 0) + while (!cancellationToken.IsCancellationRequested) { - try + //循环获取,固定读最大行数量,执行完成需删除行 + var varList = await @this.DBCacheAlarm.DBProvider.Queryable>().Take(@this._businessPropertyWithCache.SplitSize).ToListAsync(cancellationToken).ConfigureAwait(false); + if (varList.Count != 0) { - if (!cancellationToken.IsCancellationRequested) + try { - var result = await UpdateAlarmModel(varList, cancellationToken).ConfigureAwait(false); - if (result.IsSuccess) + if (!cancellationToken.IsCancellationRequested) { - //删除缓存 - await DBCacheAlarm.DBProvider.Deleteable>(varList).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false); + var result = await @this.UpdateAlarmModel(varList, cancellationToken).ConfigureAwait(false); + if (result.IsSuccess) + { + //删除缓存 + await @this.DBCacheAlarm.DBProvider.Deleteable>(varList).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false); + } + else + break; } else + { break; + } } - else + catch (Exception ex) { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; break; } } - catch (Exception ex) + else { - if (success) - LogMessage?.LogWarning(ex); - success = false; break; } } + } + catch (Exception ex) + { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; + } + } + + + #endregion //成功上传时,补上传缓存数据 + } + } + } + protected Task UpdatePluginEventDataModelCache(CancellationToken cancellationToken) + { + return UpdatePluginEventDataModelCache(this, cancellationToken); + + static async PooledTask UpdatePluginEventDataModelCache(BusinessBaseWithCache @this, CancellationToken cancellationToken) + { + if (@this._businessPropertyWithCache.CacheEnable) + { + #region //成功上传时,补上传缓存数据 + + if (@this.IsConnected()) + { + try + { + while (!cancellationToken.IsCancellationRequested) + { + //循环获取,固定读最大行数量,执行完成需删除行 + var varList = await @this.DBCachePluginEventData.DBProvider.Queryable>().Take(@this._businessPropertyWithCache.SplitSize).ToListAsync(cancellationToken).ConfigureAwait(false); + if (varList.Count != 0) + { + try + { + if (!cancellationToken.IsCancellationRequested) + { + var result = await @this.UpdatePluginEventDataModel(varList, cancellationToken).ConfigureAwait(false); + if (result.IsSuccess) + { + //删除缓存 + await @this.DBCachePluginEventData.DBProvider.Deleteable>(varList).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false); + } + else + break; + } + else + { + break; + } + } + catch (Exception ex) + { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; + break; + } + } + else + { + break; + } + } + } + catch (Exception ex) + { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; + } + } + + #endregion //成功上传时,补上传缓存数据 + } + } + } + protected Task UpdateAlarmModelMemory(CancellationToken cancellationToken) + { + return UpdateAlarmModelMemory(this, cancellationToken); + + static async PooledTask UpdateAlarmModelMemory(BusinessBaseWithCache @this, CancellationToken cancellationToken) + { + #region //上传设备内存队列中的数据 + + try + { + var list = @this._memoryAlarmModelQueue.ToListWithDequeue().ChunkBetter(@this._businessPropertyWithCache.SplitSize); + foreach (var item in list) + { + try + { + if (!cancellationToken.IsCancellationRequested) + { + var result = await @this.UpdateAlarmModel(item, cancellationToken).ConfigureAwait(false); + if (!result.IsSuccess) + { + @this.AddCache(item); + } + } else { break; } } - } - catch (Exception ex) - { - if (success) - LogMessage?.LogWarning(ex); - success = false; + catch (Exception ex) + { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; + } } } + catch (Exception ex) + { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; + } - - #endregion //成功上传时,补上传缓存数据 + #endregion //上传设备内存队列中的数据 } } - protected async Task UpdatePluginEventDataModelCache(CancellationToken cancellationToken) + protected Task UpdatePluginEventDataModelMemory(CancellationToken cancellationToken) { - if (_businessPropertyWithCache.CacheEnable) - { - #region //成功上传时,补上传缓存数据 + return UpdatePluginEventDataModelMemory(this, cancellationToken); - if (IsConnected()) + static async PooledTask UpdatePluginEventDataModelMemory(BusinessBaseWithCache @this, CancellationToken cancellationToken) + { + #region //上传设备内存队列中的数据 + + + try { - try + var list = @this._memoryPluginEventDataModelQueue.ToListWithDequeue().ChunkBetter(@this._businessPropertyWithCache.SplitSize); + foreach (var item in list) { - while (!cancellationToken.IsCancellationRequested) + try { - //循环获取,固定读最大行数量,执行完成需删除行 - var varList = await DBCachePluginEventData.DBProvider.Queryable>().Take(_businessPropertyWithCache.SplitSize).ToListAsync(cancellationToken).ConfigureAwait(false); - if (varList.Count != 0) + if (!cancellationToken.IsCancellationRequested) { - try + var result = await @this.UpdatePluginEventDataModel(item, cancellationToken).ConfigureAwait(false); + if (!result.IsSuccess) { - if (!cancellationToken.IsCancellationRequested) - { - var result = await UpdatePluginEventDataModel(varList, cancellationToken).ConfigureAwait(false); - if (result.IsSuccess) - { - //删除缓存 - await DBCachePluginEventData.DBProvider.Deleteable>(varList).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false); - } - else - break; - } - else - { - break; - } - } - catch (Exception ex) - { - if (success) - LogMessage?.LogWarning(ex); - success = false; - break; + @this.AddCache(item); } } else @@ -456,99 +560,22 @@ public abstract class BusinessBaseWithCache : BusinessBase break; } } - } - catch (Exception ex) - { - if (success) - LogMessage?.LogWarning(ex); - success = false; + catch (Exception ex) + { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; + } } } - - #endregion //成功上传时,补上传缓存数据 - } - } - protected async Task UpdateAlarmModelMemory(CancellationToken cancellationToken) - { - #region //上传设备内存队列中的数据 - - try - { - var list = _memoryAlarmModelQueue.ToListWithDequeue().ChunkBetter(_businessPropertyWithCache.SplitSize); - foreach (var item in list) + catch (Exception ex) { - try - { - if (!cancellationToken.IsCancellationRequested) - { - var result = await UpdateAlarmModel(item, cancellationToken).ConfigureAwait(false); - if (!result.IsSuccess) - { - AddCache(item); - } - } - else - { - break; - } - } - catch (Exception ex) - { - if (success) - LogMessage?.LogWarning(ex); - success = false; - } + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; } + #endregion //上传设备内存队列中的数据 } - catch (Exception ex) - { - if (success) - LogMessage?.LogWarning(ex); - success = false; - } - - #endregion //上传设备内存队列中的数据 - } - protected async Task UpdatePluginEventDataModelMemory(CancellationToken cancellationToken) - { - #region //上传设备内存队列中的数据 - - - try - { - var list = _memoryPluginEventDataModelQueue.ToListWithDequeue().ChunkBetter(_businessPropertyWithCache.SplitSize); - foreach (var item in list) - { - try - { - if (!cancellationToken.IsCancellationRequested) - { - var result = await UpdatePluginEventDataModel(item, cancellationToken).ConfigureAwait(false); - if (!result.IsSuccess) - { - AddCache(item); - } - } - else - { - break; - } - } - catch (Exception ex) - { - if (success) - LogMessage?.LogWarning(ex); - success = false; - } - } - } - catch (Exception ex) - { - if (success) - LogMessage?.LogWarning(ex); - success = false; - } - #endregion //上传设备内存队列中的数据 } #endregion @@ -672,106 +699,116 @@ public abstract class BusinessBaseWithCache : BusinessBase /// protected abstract ValueTask UpdateDevModel(List> item, CancellationToken cancellationToken); - protected async Task UpdateDevModelCache(CancellationToken cancellationToken) + protected Task UpdateDevModelCache(CancellationToken cancellationToken) { - if (_businessPropertyWithCache.CacheEnable) - { - #region //成功上传时,补上传缓存数据 + return UpdateDevModelCache(this, cancellationToken); - if (IsConnected()) + static async PooledTask UpdateDevModelCache(BusinessBaseWithCache @this, CancellationToken cancellationToken) + { + if (@this._businessPropertyWithCache.CacheEnable) { - try + #region //成功上传时,补上传缓存数据 + + if (@this.IsConnected()) { - while (!cancellationToken.IsCancellationRequested) + try { - //循环获取 - var varList = await DBCacheDev.DBProvider.Queryable>().Take(_businessPropertyWithCache.SplitSize).ToListAsync(cancellationToken).ConfigureAwait(false); - if (varList.Count != 0) + while (!cancellationToken.IsCancellationRequested) { - try + //循环获取 + var varList = await @this.DBCacheDev.DBProvider.Queryable>().Take(@this._businessPropertyWithCache.SplitSize).ToListAsync(cancellationToken).ConfigureAwait(false); + if (varList.Count != 0) { - if (!cancellationToken.IsCancellationRequested) + try { - var result = await UpdateDevModel(varList, cancellationToken).ConfigureAwait(false); - if (result.IsSuccess) + if (!cancellationToken.IsCancellationRequested) { - //删除缓存 - await DBCacheDev.DBProvider.Deleteable>(varList).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false); + var result = await @this.UpdateDevModel(varList, cancellationToken).ConfigureAwait(false); + if (result.IsSuccess) + { + //删除缓存 + await @this.DBCacheDev.DBProvider.Deleteable>(varList).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false); + } + else + break; } else + { break; + } } - else + catch (Exception ex) { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; break; } } - catch (Exception ex) + else { - if (success) - LogMessage?.LogWarning(ex); - success = false; break; } } + } + catch (Exception ex) + { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; + } + } + + #endregion //成功上传时,补上传缓存数据 + } + } + } + + protected Task UpdateDevModelMemory(CancellationToken cancellationToken) + { + return UpdateDevModelMemory(this, cancellationToken); + + static async PooledTask UpdateDevModelMemory(BusinessBaseWithCache @this, CancellationToken cancellationToken) + { + #region //上传设备内存队列中的数据 + + try + { + var list = @this._memoryDevModelQueue.ToListWithDequeue().ChunkBetter(@this._businessPropertyWithCache.SplitSize); + foreach (var item in list) + { + try + { + if (!cancellationToken.IsCancellationRequested) + { + var result = await @this.UpdateDevModel(item, cancellationToken).ConfigureAwait(false); + if (!result.IsSuccess) + { + @this.AddCache(item); + } + } else { break; } } - } - catch (Exception ex) - { - if (success) - LogMessage?.LogWarning(ex); - success = false; + catch (Exception ex) + { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; + } } } - - #endregion //成功上传时,补上传缓存数据 - } - } - - protected async Task UpdateDevModelMemory(CancellationToken cancellationToken) - { - #region //上传设备内存队列中的数据 - - try - { - var list = _memoryDevModelQueue.ToListWithDequeue().ChunkBetter(_businessPropertyWithCache.SplitSize); - foreach (var item in list) + catch (Exception ex) { - try - { - if (!cancellationToken.IsCancellationRequested) - { - var result = await UpdateDevModel(item, cancellationToken).ConfigureAwait(false); - if (!result.IsSuccess) - { - AddCache(item); - } - } - else - { - break; - } - } - catch (Exception ex) - { - if (success) - LogMessage?.LogWarning(ex); - success = false; - } + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; } - } - catch (Exception ex) - { - if (success) - LogMessage?.LogWarning(ex); - success = false; - } - #endregion //上传设备内存队列中的数据 + #endregion //上传设备内存队列中的数据 + } } #endregion @@ -1010,192 +1047,158 @@ public abstract class BusinessBaseWithCache : BusinessBase /// protected abstract ValueTask UpdateVarModels(List item, CancellationToken cancellationToken); - protected async Task UpdateVarModelCache(CancellationToken cancellationToken) + protected Task UpdateVarModelCache(CancellationToken cancellationToken) { - if (_businessPropertyWithCache.CacheEnable) + return UpdateVarModelCache(this, cancellationToken); + + static async PooledTask UpdateVarModelCache(BusinessBaseWithCache @this, CancellationToken cancellationToken) { - #region //成功上传时,补上传缓存数据 - - if (IsConnected()) + if (@this._businessPropertyWithCache.CacheEnable) { - try - { - while (!cancellationToken.IsCancellationRequested) - { - //循环获取 + #region //成功上传时,补上传缓存数据 - var varList = await DBCacheVar.DBProvider.Queryable>().Take(_businessPropertyWithCache.SplitSize).ToListAsync(cancellationToken).ConfigureAwait(false); - if (varList.Count != 0) + if (@this.IsConnected()) + { + try + { + while (!cancellationToken.IsCancellationRequested) { - try + //循环获取 + + var varList = await @this.DBCacheVar.DBProvider.Queryable>().Take(@this._businessPropertyWithCache.SplitSize).ToListAsync(cancellationToken).ConfigureAwait(false); + if (varList.Count != 0) { - if (!cancellationToken.IsCancellationRequested) + try { - var result = await UpdateVarModel(varList, cancellationToken).ConfigureAwait(false); - if (result.IsSuccess) + if (!cancellationToken.IsCancellationRequested) { - //删除缓存 - await DBCacheVar.DBProvider.Deleteable>(varList).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false); + var result = await @this.UpdateVarModel(varList, cancellationToken).ConfigureAwait(false); + if (result.IsSuccess) + { + //删除缓存 + await @this.DBCacheVar.DBProvider.Deleteable>(varList).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false); + } + else + break; } else + { break; + } } - else + catch (Exception ex) { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; break; } } - catch (Exception ex) + else { - if (success) - LogMessage?.LogWarning(ex); - success = false; break; } } - else - { - break; - } + } + catch (Exception ex) + { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; } } - catch (Exception ex) - { - if (success) - LogMessage?.LogWarning(ex); - success = false; - } - } - #endregion //成功上传时,补上传缓存数据 + #endregion //成功上传时,补上传缓存数据 + } } } - protected async Task UpdateVarModelsCache(CancellationToken cancellationToken) + protected Task UpdateVarModelsCache(CancellationToken cancellationToken) { - if (_businessPropertyWithCache.CacheEnable) - { - #region //成功上传时,补上传缓存数据 + return UpdateVarModelsCache(this, cancellationToken); - if (IsConnected()) + static async PooledTask UpdateVarModelsCache(BusinessBaseWithCache @this, CancellationToken cancellationToken) + { + if (@this._businessPropertyWithCache.CacheEnable) { - try + #region //成功上传时,补上传缓存数据 + + if (@this.IsConnected()) { - while (!cancellationToken.IsCancellationRequested) + try { - //循环获取 - var varList = await DBCacheVars.DBProvider.Queryable>>().FirstAsync(cancellationToken).ConfigureAwait(false); - if (varList?.Value?.Count > 0) + while (!cancellationToken.IsCancellationRequested) { - try + //循环获取 + var varList = await @this.DBCacheVars.DBProvider.Queryable>>().FirstAsync(cancellationToken).ConfigureAwait(false); + if (varList?.Value?.Count > 0) { - if (!cancellationToken.IsCancellationRequested) + try { - var result = await UpdateVarModels(varList.Value, cancellationToken).ConfigureAwait(false); - if (result.IsSuccess) + if (!cancellationToken.IsCancellationRequested) { - //删除缓存 - await DBCacheVars.DBProvider.DeleteableT>>(varList).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false); + var result = await @this.UpdateVarModels(varList.Value, cancellationToken).ConfigureAwait(false); + if (result.IsSuccess) + { + //删除缓存 + await @this.DBCacheVars.DBProvider.DeleteableT>>(varList).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false); + } + else + break; } else + { break; + } } - else + catch (Exception ex) { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; break; } } - catch (Exception ex) + else { - if (success) - LogMessage?.LogWarning(ex); - success = false; break; } } - else - { - break; - } + } + catch (Exception ex) + { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; } } - catch (Exception ex) - { - if (success) - LogMessage?.LogWarning(ex); - success = false; - } - } - #endregion //成功上传时,补上传缓存数据 + #endregion //成功上传时,补上传缓存数据 + } } } - protected async Task UpdateVarModelMemory(CancellationToken cancellationToken) + protected Task UpdateVarModelMemory(CancellationToken cancellationToken) { - #region //上传变量内存队列中的数据 + return UpdateVarModelMemory(this, cancellationToken); - try + static async PooledTask UpdateVarModelMemory(BusinessBaseWithCache @this, CancellationToken cancellationToken) { - var list = _memoryVarModelQueue.ToListWithDequeue().ChunkBetter(_businessPropertyWithCache.SplitSize); - foreach (var item in list) + #region //上传变量内存队列中的数据 + + try { - try - { - if (!cancellationToken.IsCancellationRequested) - { - var result = await UpdateVarModel(item, cancellationToken).ConfigureAwait(false); - if (!result.IsSuccess) - { - AddCache(item); - } - } - else - { - break; - } - } - catch (Exception ex) - { - if (success) - LogMessage?.LogWarning(ex); - success = false; - } - } - } - catch (Exception ex) - { - if (success) - LogMessage?.LogWarning(ex); - success = false; - } - - #endregion //上传变量内存队列中的数据 - } - - protected async Task UpdateVarModelsMemory(CancellationToken cancellationToken) - { - #region //上传变量内存队列中的数据 - - try - { - var queues = _memoryVarModelsQueue.ToListWithDequeue(); - foreach (var cacheDBItem in queues) - { - if (cancellationToken.IsCancellationRequested) - break; - var list = cacheDBItem.Value; - var data = list.ChunkBetter(_businessPropertyWithCache.SplitSize); - foreach (var item in data) + var list = @this._memoryVarModelQueue.ToListWithDequeue().ChunkBetter(@this._businessPropertyWithCache.SplitSize); + foreach (var item in list) { try { if (!cancellationToken.IsCancellationRequested) { - var result = await UpdateVarModels(item, cancellationToken).ConfigureAwait(false); + var result = await @this.UpdateVarModel(item, cancellationToken).ConfigureAwait(false); if (!result.IsSuccess) { - AddCache(new List>>() { new CacheDBItem>(item) }); + @this.AddCache(item); } } else @@ -1205,21 +1208,75 @@ public abstract class BusinessBaseWithCache : BusinessBase } catch (Exception ex) { - if (success) - LogMessage?.LogWarning(ex); - success = false; + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; } } } - } - catch (Exception ex) - { - if (success) - LogMessage?.LogWarning(ex); - success = false; - } + catch (Exception ex) + { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; + } - #endregion //上传变量内存队列中的数据 + #endregion //上传变量内存队列中的数据 + } + } + + protected Task UpdateVarModelsMemory(CancellationToken cancellationToken) + { + return UpdateVarModelsMemory(this, cancellationToken); + + static async PooledTask UpdateVarModelsMemory(BusinessBaseWithCache @this, CancellationToken cancellationToken) + { + #region //上传变量内存队列中的数据 + + try + { + var queues = @this._memoryVarModelsQueue.ToListWithDequeue(); + foreach (var cacheDBItem in queues) + { + if (cancellationToken.IsCancellationRequested) + break; + var list = cacheDBItem.Value; + var data = list.ChunkBetter(@this._businessPropertyWithCache.SplitSize); + foreach (var item in data) + { + try + { + if (!cancellationToken.IsCancellationRequested) + { + var result = await @this.UpdateVarModels(item, cancellationToken).ConfigureAwait(false); + if (!result.IsSuccess) + { + @this.AddCache(new List>>() { new CacheDBItem>(item) }); + } + } + else + { + break; + } + } + catch (Exception ex) + { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; + } + } + } + } + catch (Exception ex) + { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; + } + + #endregion //上传变量内存队列中的数据 + } } #endregion diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Driver/Collect/CollectBase.cs b/src/Gateway/ThingsGateway.Gateway.Application/Driver/Collect/CollectBase.cs index e045feee3..d0027c29c 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Driver/Collect/CollectBase.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Driver/Collect/CollectBase.cs @@ -20,6 +20,8 @@ using ThingsGateway.Common.Extension; using ThingsGateway.Extension.Generic; #if !Management using ThingsGateway.Gateway.Application.Extensions; +using ThingsGateway.NewLife; + #endif using ThingsGateway.NewLife.Json.Extension; using ThingsGateway.NewLife.Threading; @@ -292,69 +294,75 @@ public abstract partial class CollectBase : DriverBase #region 执行方法 - async ValueTask ReadVariableMed(object? state, CancellationToken cancellationToken) + ValueTask ReadVariableMed(object? state, CancellationToken cancellationToken) { - if (state is not VariableMethod readVariableMethods) return; + if (state is not VariableMethod readVariableMethods) + return ValueTask.CompletedTask; if (Pause) - return; + return ValueTask.CompletedTask; if (cancellationToken.IsCancellationRequested) - return; + return ValueTask.CompletedTask; + return ReadVariableMed(this, readVariableMethods, cancellationToken); - var readErrorCount = 0; - - //if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) - // LogMessage?.Trace(string.Format("{0} - Executing method [{1}]", DeviceName, readVariableMethods.MethodInfo.Name)); - var readResult = await InvokeMethodAsync(readVariableMethods, cancellationToken: cancellationToken).ConfigureAwait(false); - - // 方法调用失败时重试一定次数 - while (!readResult.IsSuccess && readErrorCount < CollectProperties.RetryCount) + static async PooledValueTask ReadVariableMed(CollectBase @this, VariableMethod readVariableMethods, CancellationToken cancellationToken) { - if (Pause) - return; - if (cancellationToken.IsCancellationRequested) - return; - readErrorCount++; - if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) - LogMessage?.Trace(string.Format("{0} - Execute method [{1}] - failed - {2}", DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage)); + var readErrorCount = 0; //if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) // LogMessage?.Trace(string.Format("{0} - Executing method [{1}]", DeviceName, readVariableMethods.MethodInfo.Name)); - readResult = await InvokeMethodAsync(readVariableMethods, cancellationToken: cancellationToken).ConfigureAwait(false); - } + var readResult = await @this.InvokeMethodAsync(readVariableMethods, cancellationToken: cancellationToken).ConfigureAwait(false); - if (readResult.IsSuccess) - { - // 方法调用成功时记录日志并增加成功计数器 - if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) - LogMessage?.Trace(string.Format("{0} - Execute method [{1}] - Succeeded {2}", DeviceName, readVariableMethods.MethodInfo.Name, readResult.Content?.ToSystemTextJsonString())); - CurrentDevice.SetDeviceStatus(TimerX.Now, null); - } - else - { - if (cancellationToken.IsCancellationRequested) - return; - - // 方法调用失败时记录日志并增加失败计数器,更新错误信息 - if (readVariableMethods.LastErrorMessage != readResult.ErrorMessage) + // 方法调用失败时重试一定次数 + while (!readResult.IsSuccess && readErrorCount < @this.CollectProperties.RetryCount) { - if (!cancellationToken.IsCancellationRequested) - LogMessage?.LogWarning(readResult.Exception, string.Format(AppResource.MethodFail, DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage)); + if (@this.Pause) + return; + if (cancellationToken.IsCancellationRequested) + return; + + readErrorCount++; + if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) + @this.LogMessage?.Trace(string.Format("{0} - Execute method [{1}] - failed - {2}", @this.DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage)); + + //if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) + // LogMessage?.Trace(string.Format("{0} - Executing method [{1}]", DeviceName, readVariableMethods.MethodInfo.Name)); + readResult = await @this.InvokeMethodAsync(readVariableMethods, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + if (readResult.IsSuccess) + { + // 方法调用成功时记录日志并增加成功计数器 + if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) + @this.LogMessage?.Trace(string.Format("{0} - Execute method [{1}] - Succeeded {2}", @this.DeviceName, readVariableMethods.MethodInfo.Name, readResult.Content?.ToSystemTextJsonString())); + @this.CurrentDevice.SetDeviceStatus(TimerX.Now, null); } else { - if (!cancellationToken.IsCancellationRequested) + if (cancellationToken.IsCancellationRequested) + return; + + // 方法调用失败时记录日志并增加失败计数器,更新错误信息 + if (readVariableMethods.LastErrorMessage != readResult.ErrorMessage) { - if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) - LogMessage?.Trace(string.Format("{0} - Execute method [{1}] - failed - {2}", DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage)); + if (!cancellationToken.IsCancellationRequested) + @this.LogMessage?.LogWarning(readResult.Exception, string.Format(AppResource.MethodFail, @this.DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage)); } + else + { + if (!cancellationToken.IsCancellationRequested) + { + if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) + @this.LogMessage?.Trace(string.Format("{0} - Execute method [{1}] - failed - {2}", @this.DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage)); + } + } + + readVariableMethods.LastErrorMessage = readResult.ErrorMessage; + @this.CurrentDevice.SetDeviceStatus(TimerX.Now, null); } - readVariableMethods.LastErrorMessage = readResult.ErrorMessage; - CurrentDevice.SetDeviceStatus(TimerX.Now, null); + return; } - - return; } #endregion @@ -674,9 +682,9 @@ public abstract partial class CollectBase : DriverBase #endregion - protected virtual Task TestOnline(object? state, CancellationToken cancellationToken) + protected virtual ValueTask TestOnline(object? state, CancellationToken cancellationToken) { - return Task.CompletedTask; + return ValueTask.CompletedTask; } protected void ScriptVariableRun(object? state, CancellationToken cancellationToken) @@ -727,38 +735,43 @@ public abstract partial class CollectBase : DriverBase { throw new NotImplementedException(); } - protected async Task Check(Dictionary writeInfoLists, NonBlockingDictionary operResults, CancellationToken cancellationToken) + protected Task Check(Dictionary writeInfoLists, NonBlockingDictionary operResults, CancellationToken cancellationToken) { - if (VariableSourceReadsEnable) + return Check(this, writeInfoLists, operResults, cancellationToken); + + static async PooledTask Check(CollectBase @this, Dictionary writeInfoLists, NonBlockingDictionary operResults, CancellationToken cancellationToken) { - // 如果成功,每个变量都读取一次最新值,再次比较写入值 - var successfulWriteNames = operResults.Where(a => a.Value.IsSuccess).Select(a => a.Key).ToHashSet(); - - var groups = writeInfoLists.Select(a => a.Key).Where(a => a.RpcWriteCheck && a.ProtectType != ProtectTypeEnum.WriteOnly && successfulWriteNames.Contains(a.Name) && a.VariableSource != null).GroupBy(a => a.VariableSource as VariableSourceRead).Where(a => a.Key != null).ToArray(); - - await groups.ParallelForEachAsync(async (varRead, token) => + if (@this.VariableSourceReadsEnable) { - var result = await ReadSourceAsync(varRead.Key, token).ConfigureAwait(false); - if (result.IsSuccess) + // 如果成功,每个变量都读取一次最新值,再次比较写入值 + var successfulWriteNames = operResults.Where(a => a.Value.IsSuccess).Select(a => a.Key).ToHashSet(); + + var groups = writeInfoLists.Select(a => a.Key).Where(a => a.RpcWriteCheck && a.ProtectType != ProtectTypeEnum.WriteOnly && successfulWriteNames.Contains(a.Name) && a.VariableSource != null).GroupBy(a => a.VariableSource as VariableSourceRead).Where(a => a.Key != null).ToArray(); + + await groups.ParallelForEachAsync(async (varRead, token) => { - foreach (var item in varRead) + var result = await @this.ReadSourceAsync(varRead.Key, token).ConfigureAwait(false); + if (result.IsSuccess) { - if (!item.Value.Equals(writeInfoLists[item].ToObject(item.Value?.GetType()))) + foreach (var item in varRead) { - // 如果写入值与读取值不同,则更新操作结果为失败 - operResults[item.Name] = new OperResult($"The write value is inconsistent with the read value, Write value: {writeInfoLists[item].ToObject(item.Value?.GetType())}, read value: {item.Value}"); + if (!item.Value.Equals(writeInfoLists[item].ToObject(item.Value?.GetType()))) + { + // 如果写入值与读取值不同,则更新操作结果为失败 + operResults[item.Name] = new OperResult($"The write value is inconsistent with the read value, Write value: {writeInfoLists[item].ToObject(item.Value?.GetType())}, read value: {item.Value}"); + } } } - } - else - { - foreach (var item in varRead) + else { - // 如果写入值与读取值不同,则更新操作结果为失败 - operResults[item.Name] = new OperResult($"Reading and rechecking resulted in an error: {result.ErrorMessage}", result.Exception); + foreach (var item in varRead) + { + // 如果写入值与读取值不同,则更新操作结果为失败 + operResults[item.Name] = new OperResult($"Reading and rechecking resulted in an error: {result.ErrorMessage}", result.Exception); + } } - } - }, cancellationToken).ConfigureAwait(false); + }, cancellationToken).ConfigureAwait(false); + } } } @@ -770,66 +783,71 @@ public abstract partial class CollectBase : DriverBase /// 要写入的变量及其对应的数据 /// 取消操作的通知 /// 写入操作的结果字典 - public async ValueTask>> InvokeMethodAsync(Dictionary writeInfoLists, CancellationToken cancellationToken) + public ValueTask>> InvokeMethodAsync(Dictionary writeInfoLists, CancellationToken cancellationToken) { - // 初始化结果字典 - Dictionary> results = new Dictionary>(); + return InvokeMethodAsync(this, writeInfoLists, cancellationToken); - // 遍历写入信息列表 - foreach (var (deviceVariable, jToken) in writeInfoLists) + static async PooledValueTask>> InvokeMethodAsync(CollectBase @this, Dictionary writeInfoLists, CancellationToken cancellationToken) { - // 检查是否有写入表达式 - if (!string.IsNullOrEmpty(deviceVariable.WriteExpressions)) + // 初始化结果字典 + Dictionary> results = new Dictionary>(); + + // 遍历写入信息列表 + foreach (var (deviceVariable, jToken) in writeInfoLists) + { + // 检查是否有写入表达式 + if (!string.IsNullOrEmpty(deviceVariable.WriteExpressions)) + { + // 提取原始数据 + object rawdata = jToken.GetObjectFromJToken(); + try + { + // 根据写入表达式转换数据 + object data = deviceVariable.WriteExpressions.GetExpressionsResult(rawdata, @this.LogMessage); + // 将转换后的数据重新赋值给写入信息列表 + writeInfoLists[deviceVariable] = JTokenUtil.GetJTokenFromObj(data); + } + catch (Exception ex) + { + // 如果转换失败,则记录错误信息 + results.Add(deviceVariable.Name, new OperResult(string.Format(AppResource.WriteExpressionsError, deviceVariable.Name, deviceVariable.WriteExpressions, ex.Message), ex)); + } + } + } + + NonBlockingDictionary> operResults = new(); + + + using var writeLock = await @this.ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false); + var list = writeInfoLists + .Where(a => !results.Any(b => b.Key == a.Key.Name)) + .ToArray(); + // 使用并发方式遍历写入信息列表,并进行异步写入操作 + await list.ParallelForEachAsync(async (writeInfo, cancellationToken) => { - // 提取原始数据 - object rawdata = jToken.GetObjectFromJToken(); try { - // 根据写入表达式转换数据 - object data = deviceVariable.WriteExpressions.GetExpressionsResult(rawdata, LogMessage); - // 将转换后的数据重新赋值给写入信息列表 - writeInfoLists[deviceVariable] = JTokenUtil.GetJTokenFromObj(data); + // 调用协议的写入方法,将写入信息中的数据写入到对应的寄存器地址,并获取操作结果 + var result = await @this.InvokeMethodAsync(writeInfo.Key.VariableMethod, writeInfo.Value?.ToString(), false, cancellationToken).ConfigureAwait(false); + + // 将操作结果添加到结果字典中,使用变量名称作为键 + operResults.TryAdd(writeInfo.Key.Name, result); } catch (Exception ex) { - // 如果转换失败,则记录错误信息 - results.Add(deviceVariable.Name, new OperResult(string.Format(AppResource.WriteExpressionsError, deviceVariable.Name, deviceVariable.WriteExpressions, ex.Message), ex)); + operResults.TryAdd(writeInfo.Key.Name, new(ex)); } - } - } + }, @this.CollectProperties.MaxConcurrentCount, cancellationToken).ConfigureAwait(false); - NonBlockingDictionary> operResults = new(); - - - using var writeLock = await ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false); - var list = writeInfoLists - .Where(a => !results.Any(b => b.Key == a.Key.Name)) - .ToArray(); - // 使用并发方式遍历写入信息列表,并进行异步写入操作 - await list.ParallelForEachAsync(async (writeInfo, cancellationToken) => - { - try - { - // 调用协议的写入方法,将写入信息中的数据写入到对应的寄存器地址,并获取操作结果 - var result = await InvokeMethodAsync(writeInfo.Key.VariableMethod, writeInfo.Value?.ToString(), false, cancellationToken).ConfigureAwait(false); - - // 将操作结果添加到结果字典中,使用变量名称作为键 - operResults.TryAdd(writeInfo.Key.Name, result); - } - catch (Exception ex) - { - operResults.TryAdd(writeInfo.Key.Name, new(ex)); - } - }, CollectProperties.MaxConcurrentCount, cancellationToken).ConfigureAwait(false); - - // 将转换失败的变量和写入成功的变量的操作结果合并到结果字典中 - return new Dictionary>() + // 将转换失败的变量和写入成功的变量的操作结果合并到结果字典中 + return new Dictionary>() { { - DeviceName , + @this.DeviceName , results.Concat(operResults).ToDictionary(a => a.Key, a => (IOperResult)a.Value) } }; + } } /// @@ -838,58 +856,63 @@ public abstract partial class CollectBase : DriverBase /// 要写入的变量及其对应的数据 /// 取消操作的通知 /// 写入操作的结果字典 - public async ValueTask>> InVokeWriteAsync(Dictionary writeInfoLists, CancellationToken cancellationToken) + public ValueTask>> InVokeWriteAsync(Dictionary writeInfoLists, CancellationToken cancellationToken) { - // 初始化结果字典 - Dictionary results = new Dictionary(); + return InVokeWriteAsync(this, writeInfoLists, cancellationToken); - // 遍历写入信息列表 - foreach (var (deviceVariable, jToken) in writeInfoLists) + static async PooledValueTask>> InVokeWriteAsync(CollectBase @this, Dictionary writeInfoLists, CancellationToken cancellationToken) { - // 检查是否有写入表达式 - if (!string.IsNullOrEmpty(deviceVariable.WriteExpressions)) + // 初始化结果字典 + Dictionary results = new Dictionary(); + + // 遍历写入信息列表 + foreach (var (deviceVariable, jToken) in writeInfoLists) { - // 提取原始数据 - object rawdata = jToken.GetObjectFromJToken(); - try + // 检查是否有写入表达式 + if (!string.IsNullOrEmpty(deviceVariable.WriteExpressions)) { - // 根据写入表达式转换数据 - object data = deviceVariable.WriteExpressions.GetExpressionsResult(rawdata, LogMessage); - // 将转换后的数据重新赋值给写入信息列表 - writeInfoLists[deviceVariable] = JTokenUtil.GetJTokenFromObj(data); - } - catch (Exception ex) - { - // 如果转换失败,则记录错误信息 - results.Add(deviceVariable.Name, new OperResult(string.Format(AppResource.WriteExpressionsError, deviceVariable.Name, deviceVariable.WriteExpressions, ex.Message), ex)); + // 提取原始数据 + object rawdata = jToken.GetObjectFromJToken(); + try + { + // 根据写入表达式转换数据 + object data = deviceVariable.WriteExpressions.GetExpressionsResult(rawdata, @this.LogMessage); + // 将转换后的数据重新赋值给写入信息列表 + writeInfoLists[deviceVariable] = JTokenUtil.GetJTokenFromObj(data); + } + catch (Exception ex) + { + // 如果转换失败,则记录错误信息 + results.Add(deviceVariable.Name, new OperResult(string.Format(AppResource.WriteExpressionsError, deviceVariable.Name, deviceVariable.WriteExpressions, ex.Message), ex)); + } } } - } - var writePList = writeInfoLists.Where(a => !CurrentDevice.VariableScriptReads.Select(a => a.VariableRuntime).Any(b => a.Key.Name == b.Name)); - var writeSList = writeInfoLists.Where(a => CurrentDevice.VariableScriptReads.Select(a => a.VariableRuntime).Any(b => a.Key.Name == b.Name)); + var writePList = writeInfoLists.Where(a => !@this.CurrentDevice.VariableScriptReads.Select(a => a.VariableRuntime).Any(b => a.Key.Name == b.Name)); + var writeSList = writeInfoLists.Where(a => @this.CurrentDevice.VariableScriptReads.Select(a => a.VariableRuntime).Any(b => a.Key.Name == b.Name)); - DateTime now = DateTime.Now; - foreach (var item in writeSList) - { - results.TryAdd(item.Key.Name, item.Key.SetValue(item.Value, now)); - } + DateTime now = DateTime.Now; + foreach (var item in writeSList) + { + results.TryAdd(item.Key.Name, item.Key.SetValue(item.Value, now)); + } - // 过滤掉转换失败的变量,只保留写入成功的变量进行写入操作 - var results1 = await WriteValuesAsync(writePList - .Where(a => !results.Any(b => b.Key == a.Key.Name)) - .ToDictionary(item => item.Key, item => item.Value), - cancellationToken).ConfigureAwait(false); + // 过滤掉转换失败的变量,只保留写入成功的变量进行写入操作 + var results1 = await @this.WriteValuesAsync(writePList + .Where(a => !results.Any(b => b.Key == a.Key.Name)) + .ToDictionary(item => item.Key, item => item.Value), + cancellationToken).ConfigureAwait(false); - // 将转换失败的变量和写入成功的变量的操作结果合并到结果字典中 + // 将转换失败的变量和写入成功的变量的操作结果合并到结果字典中 - return new Dictionary>() + return new Dictionary>() { { - DeviceName , + @this. DeviceName , results.Concat(results1).ToDictionary(a => a.Key, a => (IOperResult)a.Value) } }; + } } /// @@ -900,56 +923,61 @@ public abstract partial class CollectBase : DriverBase /// 指示是否为读取操作 /// 取消操作的通知 /// 操作结果,包含执行方法的结果 - protected virtual async ValueTask> InvokeMethodAsync(VariableMethod variableMethod, string? value = null, bool isRead = true, CancellationToken cancellationToken = default) + protected virtual ValueTask> InvokeMethodAsync(VariableMethod variableMethod, string? value = null, bool isRead = true, CancellationToken cancellationToken = default) { - try + return InvokeMethodAsync(this, variableMethod, value, isRead, cancellationToken); + + static async PooledValueTask> InvokeMethodAsync(CollectBase @this, VariableMethod variableMethod, string? value, bool isRead, CancellationToken cancellationToken) { - // 初始化操作结果 - OperResult result = new OperResult(); - - // 获取要执行的方法 - var method = variableMethod.MethodInfo; - - // 如果方法未找到,则返回错误结果 - if (method == null) + try { - result.OperCode = 999; - result.ErrorMessage = string.Format(AppResource.MethodNotNull, variableMethod.Variable.Name, variableMethod.Variable.OtherMethod); - return result; - } - else - { - // 调用方法并获取结果 - var data = await variableMethod.InvokeMethodAsync(this, value, cancellationToken).ConfigureAwait(false); + // 初始化操作结果 + OperResult result = new OperResult(); - result = data.GetOperResult(); + // 获取要执行的方法 + var method = variableMethod.MethodInfo; - // 如果方法有返回值,并且是读取操作 - if (method.HasReturn && isRead) + // 如果方法未找到,则返回错误结果 + if (method == null) { - var time = DateTime.Now; - if (result.IsSuccess == true) - { - // 将结果序列化并设置到变量中 - var variableResult = variableMethod.Variable.SetValue(result.Content, time); - if (!variableResult.IsSuccess) - variableMethod.LastErrorMessage = result.ErrorMessage; - } - else - { - // 如果读取操作失败,则将变量标记为离线 - var variableResult = variableMethod.Variable.SetValue(null, time, isOnline: false); - if (!variableResult.IsSuccess) - variableMethod.LastErrorMessage = result.ErrorMessage; - } + result.OperCode = 999; + result.ErrorMessage = string.Format(AppResource.MethodNotNull, variableMethod.Variable.Name, variableMethod.Variable.OtherMethod); + return result; + } + else + { + // 调用方法并获取结果 + var data = await variableMethod.InvokeMethodAsync(@this, value, cancellationToken).ConfigureAwait(false); + + result = data.GetOperResult(); + + // 如果方法有返回值,并且是读取操作 + if (method.HasReturn && isRead) + { + var time = DateTime.Now; + if (result.IsSuccess == true) + { + // 将结果序列化并设置到变量中 + var variableResult = variableMethod.Variable.SetValue(result.Content, time); + if (!variableResult.IsSuccess) + variableMethod.LastErrorMessage = result.ErrorMessage; + } + else + { + // 如果读取操作失败,则将变量标记为离线 + var variableResult = variableMethod.Variable.SetValue(null, time, isOnline: false); + if (!variableResult.IsSuccess) + variableMethod.LastErrorMessage = result.ErrorMessage; + } + } + return result; } - return result; } - } - catch (Exception ex) - { - // 捕获异常并返回错误结果 - return new OperResult(ex); + catch (Exception ex) + { + // 捕获异常并返回错误结果 + return new OperResult(ex); + } } } diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Driver/Collect/CollectFoundationBase.cs b/src/Gateway/ThingsGateway.Gateway.Application/Driver/Collect/CollectFoundationBase.cs index aeac0120d..271bca43e 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Driver/Collect/CollectFoundationBase.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Driver/Collect/CollectFoundationBase.cs @@ -81,72 +81,77 @@ public abstract class CollectFoundationBase : CollectBase } - protected override async Task TestOnline(object? state, CancellationToken cancellationToken) + protected override ValueTask TestOnline(object? state, CancellationToken cancellationToken) { - if (FoundationDevice != null) + return TestOnline(this, cancellationToken); + + + static async PooledValueTask TestOnline(CollectFoundationBase @this, CancellationToken cancellationToken) { - if (!FoundationDevice.OnLine) + if (@this.FoundationDevice != null) { - if (!FoundationDevice.DisposedValue || FoundationDevice.Channel?.DisposedValue != false) return; - Exception exception = null; - try + if (!@this.FoundationDevice.OnLine) { - if (!cancellationToken.IsCancellationRequested) + if (!@this.FoundationDevice.DisposedValue || @this.FoundationDevice.Channel?.DisposedValue != false) return; + Exception exception = null; + try { - if (!FoundationDevice.DisposedValue || FoundationDevice.Channel?.DisposedValue != false) return; - - await FoundationDevice.ConnectAsync(cancellationToken).ConfigureAwait(false); - - if (CurrentDevice.DeviceStatusChangeTime < TimerX.Now.AddMinutes(-1)) + if (!cancellationToken.IsCancellationRequested) { - await Task.Delay(30000, cancellationToken).ConfigureAwait(false); + if (!@this.FoundationDevice.DisposedValue || @this.FoundationDevice.Channel?.DisposedValue != false) return; + + await @this.FoundationDevice.ConnectAsync(cancellationToken).ConfigureAwait(false); + + if (@this.CurrentDevice.DeviceStatusChangeTime < TimerX.Now.AddMinutes(-1)) + { + await Task.Delay(30000, cancellationToken).ConfigureAwait(false); + } } } - } - catch (OperationCanceledException) - { - } - catch (Exception ex) - { - exception = ex; - } - if (cancellationToken.IsCancellationRequested) - { - return; - } - if (FoundationDevice.OnLine == false && exception != null) - { - foreach (var item in CurrentDevice.VariableSourceReads) + catch (OperationCanceledException) { - if (item.LastErrorMessage != exception.Message) - { - if (!cancellationToken.IsCancellationRequested) - LogMessage?.LogWarning(exception, string.Format(AppResource.CollectFail, DeviceName, item?.RegisterAddress, item?.Length, exception.Message)); - } - item.LastErrorMessage = exception.Message; - CurrentDevice.SetDeviceStatus(TimerX.Now, null, exception.Message); - var time = DateTime.Now; - item.VariableRuntimes.ForEach(a => a.SetValue(null, time, isOnline: false)); } - foreach (var item in CurrentDevice.ReadVariableMethods) + catch (Exception ex) { - if (item.LastErrorMessage != exception.Message) - { - if (!cancellationToken.IsCancellationRequested) - LogMessage?.LogWarning(exception, string.Format(AppResource.MethodFail, DeviceName, item.MethodInfo.Name, exception.Message)); - } - item.LastErrorMessage = exception.Message; - CurrentDevice.SetDeviceStatus(TimerX.Now, null, exception.Message); - var time = DateTime.Now; - item.Variable.SetValue(null, time, isOnline: false); + exception = ex; } + if (cancellationToken.IsCancellationRequested) + { + return; + } + if (@this.FoundationDevice.OnLine == false && exception != null) + { + foreach (var item in @this.CurrentDevice.VariableSourceReads) + { + if (item.LastErrorMessage != exception.Message) + { + if (!cancellationToken.IsCancellationRequested) + @this.LogMessage?.LogWarning(exception, string.Format(AppResource.CollectFail, @this.DeviceName, item?.RegisterAddress, item?.Length, exception.Message)); + } + item.LastErrorMessage = exception.Message; + @this.CurrentDevice.SetDeviceStatus(TimerX.Now, null, exception.Message); + var time = DateTime.Now; + item.VariableRuntimes.ForEach(a => a.SetValue(null, time, isOnline: false)); + } + foreach (var item in @this.CurrentDevice.ReadVariableMethods) + { + if (item.LastErrorMessage != exception.Message) + { + if (!cancellationToken.IsCancellationRequested) + @this.LogMessage?.LogWarning(exception, string.Format(AppResource.MethodFail, @this.DeviceName, item.MethodInfo.Name, exception.Message)); + } + item.LastErrorMessage = exception.Message; + @this.CurrentDevice.SetDeviceStatus(TimerX.Now, null, exception.Message); + var time = DateTime.Now; + item.Variable.SetValue(null, time, isOnline: false); + } - return; + } } } + } - return; } @@ -309,46 +314,51 @@ public abstract class CollectFoundationBase : CollectBase /// 批量写入变量值,需返回变量名称/结果,注意非通用设备需重写 /// /// - protected override async ValueTask> WriteValuesAsync(Dictionary writeInfoLists, CancellationToken cancellationToken) + protected override ValueTask> WriteValuesAsync(Dictionary writeInfoLists, CancellationToken cancellationToken) { - using var writeLock = await ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false); - // 检查协议是否为空,如果为空则抛出异常 - if (FoundationDevice == null) - throw new NotSupportedException(); + return WriteValuesAsync(this, writeInfoLists, cancellationToken); - // 创建用于存储操作结果的并发字典 - NonBlockingDictionary operResults = new(); - // 使用并发方式遍历写入信息列表,并进行异步写入操作 - await writeInfoLists.ParallelForEachAsync(async (writeInfo, cancellationToken) => + static async PooledValueTask> WriteValuesAsync(CollectFoundationBase @this, Dictionary writeInfoLists, CancellationToken cancellationToken) { - try + using var writeLock = await @this.ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false); + // 检查协议是否为空,如果为空则抛出异常 + if (@this.FoundationDevice == null) + throw new NotSupportedException(); + + // 创建用于存储操作结果的并发字典 + NonBlockingDictionary operResults = new(); + // 使用并发方式遍历写入信息列表,并进行异步写入操作 + await writeInfoLists.ParallelForEachAsync(async (writeInfo, cancellationToken) => { - - // 调用协议的写入方法,将写入信息中的数据写入到对应的寄存器地址,并获取操作结果 - var result = await FoundationDevice.WriteJTokenAsync(writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType, cancellationToken).ConfigureAwait(false); - - if (result.IsSuccess) + try { - if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) - LogMessage?.Debug(string.Format("{0} - Write [{1} - {2} - {3}] data succeeded", DeviceName, writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType)); + + // 调用协议的写入方法,将写入信息中的数据写入到对应的寄存器地址,并获取操作结果 + var result = await @this.FoundationDevice.WriteJTokenAsync(writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType, cancellationToken).ConfigureAwait(false); + + if (result.IsSuccess) + { + if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) + @this.LogMessage?.Debug(string.Format("{0} - Write [{1} - {2} - {3}] data succeeded", @this.DeviceName, writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType)); + } + else + { + @this.LogMessage?.Warning(string.Format("{0} - Write [{1} - {2} - {3}] data failed {4}", @this.DeviceName, writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType, result.ToString())); + } + // 将操作结果添加到结果字典中,使用变量名称作为键 + operResults.TryAdd(writeInfo.Key.Name, result); } - else + catch (Exception ex) { - LogMessage?.Warning(string.Format("{0} - Write [{1} - {2} - {3}] data failed {4}", DeviceName, writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType, result.ToString())); + operResults.TryAdd(writeInfo.Key.Name, new(ex)); } - // 将操作结果添加到结果字典中,使用变量名称作为键 - operResults.TryAdd(writeInfo.Key.Name, result); - } - catch (Exception ex) - { - operResults.TryAdd(writeInfo.Key.Name, new(ex)); - } - }, CollectProperties.MaxConcurrentCount, cancellationToken).ConfigureAwait(false); + }, @this.CollectProperties.MaxConcurrentCount, cancellationToken).ConfigureAwait(false); - await Check(writeInfoLists, operResults, cancellationToken).ConfigureAwait(false); + await @this.Check(writeInfoLists, operResults, cancellationToken).ConfigureAwait(false); - // 返回包含操作结果的字典 - return new Dictionary(operResults); + // 返回包含操作结果的字典 + return new Dictionary(operResults); + } } diff --git a/src/Gateway/ThingsGateway.Gateway.Application/GlobalData/GlobalData.cs b/src/Gateway/ThingsGateway.Gateway.Application/GlobalData/GlobalData.cs index 965c32805..f504af2b8 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/GlobalData/GlobalData.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/GlobalData/GlobalData.cs @@ -10,6 +10,8 @@ using BootstrapBlazor.Components; +using PooledAwait; + using System.Collections.Concurrent; using ThingsGateway.Extension.Generic; @@ -89,38 +91,63 @@ public static class GlobalData public static event PluginEventHandler? PluginEventHandler; - public static async Task> GetCurrentUserChannels() + public static Task> GetCurrentUserChannels() { - var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false); - return ReadOnlyIdChannels.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询 - .WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value); + return GetCurrentUserChannels(); + + static async PooledTask> GetCurrentUserChannels() + { + var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false); + return ReadOnlyIdChannels.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询 + .WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value); + } } - public static async Task> GetCurrentUserDevices() + public static Task> GetCurrentUserDevices() { - var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false); - return ReadOnlyIdDevices.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询 - .WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value); + return GetCurrentUserDevices(); + + static async PooledTask> GetCurrentUserDevices() + { + var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false); + return ReadOnlyIdDevices.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询 + .WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value); + } } - public static async Task> GetCurrentUserIdVariables() + public static Task> GetCurrentUserIdVariables() { - var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false); - return IdVariables.Where(a => a.Value.IsInternalMemoryVariable == false).WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询 - .WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value); + return GetCurrentUserIdVariables(); + + static async PooledTask> GetCurrentUserIdVariables() + { + var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false); + return IdVariables.Where(a => a.Value.IsInternalMemoryVariable == false).WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询 + .WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value); + } } - public static async Task> GetCurrentUserRealAlarmVariablesAsync() + public static Task> GetCurrentUserRealAlarmVariablesAsync() { - var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false); - return RealAlarmIdVariables.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询 - .WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value); + return GetCurrentUserRealAlarmVariablesAsync(); + + static async PooledTask> GetCurrentUserRealAlarmVariablesAsync() + { + var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false); + return RealAlarmIdVariables.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询 + .WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value); + } } - public static async Task> GetCurrentUserAlarmEnableVariables() + public static Task> GetCurrentUserAlarmEnableVariables() { - var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false); - return AlarmEnableIdVariables.Where(a => a.Value.IsInternalMemoryVariable == false).WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询 - .WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value); + return GetCurrentUserAlarmEnableVariables(); + + static async PooledTask> GetCurrentUserAlarmEnableVariables() + { + var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false); + return AlarmEnableIdVariables.Where(a => a.Value.IsInternalMemoryVariable == false).WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询 + .WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value); + } } public static bool ContainsVariable(long businessDeviceId, VariableRuntime a) diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Model/VariableMethod.cs b/src/Gateway/ThingsGateway.Gateway.Application/Model/VariableMethod.cs index e6c9f30f7..d7b5827d0 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Model/VariableMethod.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Model/VariableMethod.cs @@ -8,6 +8,8 @@ // QQ群:605534569 //------------------------------------------------------------------------------ +using PooledAwait; + using TouchSocket.Core; namespace ThingsGateway.Gateway.Application; @@ -51,42 +53,47 @@ public class VariableMethod /// 以,逗号分割的参数 /// 取消令箭 /// - public async ValueTask InvokeMethodAsync(object driverBase, string? value = null, CancellationToken cancellationToken = default) + public ValueTask InvokeMethodAsync(object driverBase, string? value = null, CancellationToken cancellationToken = default) { - try + return InvokeMethodAsync(this, driverBase, value, cancellationToken); + + static async PooledValueTask InvokeMethodAsync(VariableMethod @this, object driverBase, string? value, CancellationToken cancellationToken) { - object?[]? os = null; - if (value == null && OS == null) + try { - //默认的参数 - var addresss = Variable.RegisterAddress.SplitOS(); - //通过逗号分割,并且合并参数 - var strs = addresss; + object?[]? os = null; + if (value == null && @this.OS == null) + { + //默认的参数 + var addresss = @this.Variable.RegisterAddress.SplitOS(); + //通过逗号分割,并且合并参数 + var strs = addresss; - OS = GetOS(strs, cancellationToken); - os = OS; + @this.OS = @this.GetOS(strs, cancellationToken); + os = @this.OS; + } + else + { + var addresss = @this.Variable.RegisterAddress.SplitOS(); + var values = value.SplitOS(); + //通过分号分割,并且合并参数 + var strs = addresss.Concat(values).ToList(); + os = @this.GetOS(strs, cancellationToken); + } + + dynamic result; + + result = await @this.MethodInfo.InvokeAsync(driverBase, os).ConfigureAwait(false); + if (@this.MethodInfo.HasReturn) + { + return result; + } + return OperResult.Success; } - else + catch (Exception ex) { - var addresss = Variable.RegisterAddress.SplitOS(); - var values = value.SplitOS(); - //通过分号分割,并且合并参数 - var strs = addresss.Concat(values).ToList(); - os = GetOS(strs, cancellationToken); + return new OperResult(ex); } - - dynamic result; - - result = await MethodInfo.InvokeAsync(driverBase, os).ConfigureAwait(false); - if (MethodInfo.HasReturn) - { - return result; - } - return OperResult.Success; - } - catch (Exception ex) - { - return new OperResult(ex); } } diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Model/VariableRunTime.cs b/src/Gateway/ThingsGateway.Gateway.Application/Model/VariableRunTime.cs index af6e147ff..a23635f10 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Model/VariableRunTime.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Model/VariableRunTime.cs @@ -12,6 +12,8 @@ using BootstrapBlazor.Components; using Newtonsoft.Json.Linq; +using PooledAwait; + using Riok.Mapperly.Abstractions; @@ -448,13 +450,18 @@ public partial class VariableRuntime : Variable } /// - public virtual async ValueTask RpcAsync(string value, string executive = "brower", CancellationToken cancellationToken = default) + public virtual ValueTask RpcAsync(string value, string executive = "brower", CancellationToken cancellationToken = default) { - var data = await GlobalData.RpcService.InvokeDeviceMethodAsync(executive, new Dictionary>() + return RpcAsync(DeviceName, Name, value, executive, cancellationToken); + + static async PooledValueTask RpcAsync(string deviceName, string name, string value, string executive, CancellationToken cancellationToken) { - { DeviceName, new Dictionary() { { Name,value} } } + var data = await GlobalData.RpcService.InvokeDeviceMethodAsync(executive, new Dictionary>() + { + { deviceName, new Dictionary() { { name,value} } } }, cancellationToken).ConfigureAwait(false); - return data.FirstOrDefault().Value.FirstOrDefault().Value; + return data.FirstOrDefault().Value.FirstOrDefault().Value; + } } public void SetErrorMessage(string lastErrorMessage) diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/RulesEngine/Services/RulesEngineHostedService.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/RulesEngine/Services/RulesEngineHostedService.cs index 1991c0a2e..5216ea021 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Services/RulesEngine/Services/RulesEngineHostedService.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Services/RulesEngine/Services/RulesEngineHostedService.cs @@ -13,6 +13,8 @@ using BootstrapBlazor.Components; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using PooledAwait; + using ThingsGateway.Blazor.Diagrams.Core; using ThingsGateway.Blazor.Diagrams.Core.Models; using ThingsGateway.NewLife; @@ -139,68 +141,76 @@ internal sealed class RulesEngineHostedService : BackgroundService, IRulesEngine } } - private static async Task Analysis(NodeModel targetNode, NodeInput input, RulesLog rulesLog, CancellationToken cancellationToken) + private static Task Analysis(NodeModel targetNode, NodeInput input, RulesLog rulesLog, CancellationToken cancellationToken) { - if (targetNode is INode node) - { - node.Logger = rulesLog.Log; - node.RulesEngineName = rulesLog.Rules.Name; - } + return Analysis(targetNode, input, rulesLog, cancellationToken); - try + + static async PooledTask Analysis(NodeModel targetNode, NodeInput input, RulesLog rulesLog, CancellationToken cancellationToken) { - if (targetNode == null) - return; - if (targetNode is IConditionNode conditionNode) + if (targetNode is INode node) { - var next = await conditionNode.ExecuteAsync(input, cancellationToken).ConfigureAwait(false); - if (next) + node.Logger = rulesLog.Log; + node.RulesEngineName = rulesLog.Rules.Name; + } + + try + { + if (targetNode == null) + return; + if (targetNode is IConditionNode conditionNode) { - foreach (var link in targetNode.PortLinks.Where(a => ((a.Target.Model as PortModel)?.Parent) != targetNode)) + var next = await conditionNode.ExecuteAsync(input, cancellationToken).ConfigureAwait(false); + if (next) { - await Analysis((link.Target.Model as PortModel)?.Parent, input, rulesLog, cancellationToken).ConfigureAwait(false); + foreach (var link in targetNode.PortLinks.Where(a => ((a.Target.Model as PortModel)?.Parent) != targetNode)) + { + await RulesEngineHostedService.Analysis((link.Target.Model as PortModel)?.Parent, input, rulesLog, cancellationToken).ConfigureAwait(false); + } } } - } - else if (targetNode is IExpressionNode expressionNode) - { - var nodeOutput = await expressionNode.ExecuteAsync(input, cancellationToken).ConfigureAwait(false); - if (nodeOutput.IsSuccess) + else if (targetNode is IExpressionNode expressionNode) { - foreach (var link in targetNode.PortLinks.Where(a => ((a.Target.Model as PortModel)?.Parent) != targetNode)) + var nodeOutput = await expressionNode.ExecuteAsync(input, cancellationToken).ConfigureAwait(false); + if (nodeOutput.IsSuccess) { - await Analysis((link.Target.Model as PortModel)?.Parent, new NodeInput() { Value = nodeOutput.Content.Value, }, rulesLog, cancellationToken).ConfigureAwait(false); + foreach (var link in targetNode.PortLinks.Where(a => ((a.Target.Model as PortModel)?.Parent) != targetNode)) + { + await RulesEngineHostedService.Analysis((link.Target.Model as PortModel)?.Parent, new NodeInput() { Value = nodeOutput.Content.Value, }, rulesLog, cancellationToken).ConfigureAwait(false); + } } } - } - else if (targetNode is IActuatorNode actuatorNode) - { - var nodeOutput = await actuatorNode.ExecuteAsync(input, cancellationToken).ConfigureAwait(false); - if (nodeOutput.IsSuccess) + else if (targetNode is IActuatorNode actuatorNode) { - foreach (var link in targetNode.PortLinks.Where(a => ((a.Target.Model as PortModel)?.Parent) != targetNode)) + var nodeOutput = await actuatorNode.ExecuteAsync(input, cancellationToken).ConfigureAwait(false); + if (nodeOutput.IsSuccess) { - await Analysis((link.Target.Model as PortModel)?.Parent, new NodeInput() { Value = nodeOutput.Content.Value }, rulesLog, cancellationToken).ConfigureAwait(false); + foreach (var link in targetNode.PortLinks.Where(a => ((a.Target.Model as PortModel)?.Parent) != targetNode)) + { + await RulesEngineHostedService.Analysis((link.Target.Model as PortModel)?.Parent, new NodeInput() { Value = nodeOutput.Content.Value }, rulesLog, cancellationToken).ConfigureAwait(false); + } } } - } - else if (targetNode is ITriggerNode triggerNode) - { - Func func = (async (a, token) => + else if (targetNode is ITriggerNode triggerNode) { - foreach (var link in targetNode.PortLinks.Where(a => ((a.Target.Model as PortModel)?.Parent) != targetNode)) + Func func = (async (a, token) => { - await Analysis((link.Target.Model as PortModel)?.Parent, new NodeInput() { Value = a.Value }, rulesLog, token).ConfigureAwait(false); - } - }); - await triggerNode.StartAsync(func, cancellationToken).ConfigureAwait(false); + foreach (var link in targetNode.PortLinks.Where(a => ((a.Target.Model as PortModel)?.Parent) != targetNode)) + { + await RulesEngineHostedService.Analysis((link.Target.Model as PortModel)?.Parent, new NodeInput() { Value = a.Value }, rulesLog, token).ConfigureAwait(false); + } + }); + await triggerNode.StartAsync(func, cancellationToken).ConfigureAwait(false); + } } - } - catch (TaskCanceledException) { } - catch (OperationCanceledException) { } - catch (Exception ex) - { - rulesLog.Log?.LogWarning(ex); + catch (TaskCanceledException) { } + catch (OperationCanceledException) { } + catch (Exception ex) + { + rulesLog.Log?.LogWarning(ex); + } + + return; } } diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/ChannelDeviceTree.razor.cs b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/ChannelDeviceTree.razor.cs index 9ad23ec68..5160afa96 100644 --- a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/ChannelDeviceTree.razor.cs +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/ChannelDeviceTree.razor.cs @@ -321,6 +321,33 @@ public partial class ChannelDeviceTree return "enable--text"; } + private static string GetClass(ChannelDeviceTreeItemStruct item) + { + if (item.TryGetChannelRuntime(out var channelRuntime)) + { + return channelRuntime.DeviceThreadManage != null ? " enable--text" : " disabled--text "; + } + else if (item.TryGetDeviceRuntime(out var deviceRuntime)) + { + if (deviceRuntime.Driver?.DeviceThreadManage != null) + { + if (deviceRuntime.DeviceStatus == DeviceStatusEnum.OnLine) + { + return "green--text"; + } + else + { + return "red--text"; + } + } + else + { + return "disabled--text"; + } + } + return "enable--text"; + } + [Inject] DialogService DialogService { get; set; } @@ -1617,7 +1644,7 @@ EventCallback.Factory.Create(this, async e => List ret = new(jsstring.Count); foreach (var str in jsstring) { - var item = ChannelDeviceTreeItem.FromJSString(str); + var item = ChannelDeviceTreeItemStruct.FromJSString(str); ret.Add(GetClass(item)); } return ret; diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/ChannelDeviceTreeItem.cs b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/ChannelDeviceTreeItem.cs index 6b72444ce..2337d343f 100644 --- a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/ChannelDeviceTreeItem.cs +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/ChannelDeviceTreeItem.cs @@ -149,7 +149,75 @@ public class ChannelDeviceTreeItem : IEqualityComparer return $"{channelDeviceTreeItem.ChannelDevicePluginType}.{channelDeviceTreeItem.DeviceRuntimeId}.{channelDeviceTreeItem.ChannelRuntimeId}.{channelDeviceTreeItem.PluginName}.{channelDeviceTreeItem.PluginType}"; } - public static ChannelDeviceTreeItem FromJSString(string jsString) + +} + +public struct ChannelDeviceTreeItemStruct +{ + public long Id { get; set; } + public ChannelDevicePluginTypeEnum ChannelDevicePluginType { get; set; } + + public long DeviceRuntimeId { get; set; } + + public long ChannelRuntimeId { get; set; } + public string PluginName { get; set; } + public PluginTypeEnum? PluginType { get; set; } + + + public bool TryGetDeviceRuntime(out DeviceRuntime deviceRuntime) + { + if (ChannelDevicePluginType == ChannelDevicePluginTypeEnum.Device && DeviceRuntimeId > 0) + { + if (GlobalData.ReadOnlyIdDevices.TryGetValue(DeviceRuntimeId, out deviceRuntime)) + { + return true; + } + } + deviceRuntime = null; + return false; + } + + public bool TryGetPluginName(out string pluginName) + { + if (ChannelDevicePluginType == ChannelDevicePluginTypeEnum.PluginName) + { + pluginName = PluginName; + return true; + } + else + { + pluginName = default; + return false; + } + } + + public bool TryGetPluginType(out PluginTypeEnum? pluginType) + { + if (ChannelDevicePluginType == ChannelDevicePluginTypeEnum.PluginType) + { + pluginType = PluginType; + return true; + } + else + { + pluginType = default; + return false; + } + } + public bool TryGetChannelRuntime(out ChannelRuntime channelRuntime) + { + if (ChannelDevicePluginType == ChannelDevicePluginTypeEnum.Channel && ChannelRuntimeId > 0) + { + if (GlobalData.ReadOnlyIdChannels.TryGetValue(ChannelRuntimeId, out channelRuntime)) + { + return true; + } + } + channelRuntime = null; + return false; + } + + public static ChannelDeviceTreeItemStruct FromJSString(string jsString) { if (string.IsNullOrWhiteSpace(jsString)) throw new ArgumentNullException(nameof(jsString)); @@ -204,7 +272,7 @@ public class ChannelDeviceTreeItem : IEqualityComparer parsedPluginType = tmp; } - return new ChannelDeviceTreeItem + return new ChannelDeviceTreeItemStruct { ChannelDevicePluginType = pluginType, DeviceRuntimeId = deviceRuntimeId, @@ -215,3 +283,4 @@ public class ChannelDeviceTreeItem : IEqualityComparer } } + diff --git a/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/BenchmarkAsyncWaitData.cs b/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/BenchmarkAsyncWaitData.cs new file mode 100644 index 000000000..48b78c73b --- /dev/null +++ b/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/BenchmarkAsyncWaitData.cs @@ -0,0 +1,426 @@ +// ------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// API首页:https://touchsocket.net/ +// 交流QQ群:234762506 +// 感谢您的下载和使用 +// ------------------------------------------------------------------------------ + +using BenchmarkDotNet.Attributes; + +using System.Collections.Concurrent; +using System.Runtime.CompilerServices; +using System.Threading.Tasks.Sources; + +using TouchSocket.Core; + +namespace BenchmarkConsoleApp; + + +[MemoryDiagnoser] +public class BenchmarkAsyncWaitData +{ + private int Count = 100000; + + [Benchmark] + public async Task RunAsyncWaitDataPool() + { + var waitHandlePool = new WaitHandlePool(); + var cts = new CancellationTokenSource(1000 * 60); + for (var i = 0; i < this.Count; i++) + { + var data = new MyWaitData(); + using (var waitData = waitHandlePool.GetWaitDataAsync(data)) + { + var task = Task.Run(() => + { + waitHandlePool.Set(data); + }); + + + await waitData.WaitAsync(cts.Token).ConfigureAwait(false); + + await task; + } + } + + } + + [Benchmark] + public async Task RunAsyncWaitData() + { + var waitHandlePool = new WaitHandlePool2(); + var cts = new CancellationTokenSource(1000 * 60); + for (var i = 0; i < this.Count; i++) + { + var data = new MyWaitData(); + using (var waitData = waitHandlePool.GetWaitDataAsync(data)) + { + var task = Task.Run(() => + { + waitHandlePool.Set(data); + }); + + + await waitData.WaitAsync(cts.Token).ConfigureAwait(false); + + await task; + } + } + } + + [Benchmark] + public async Task RunAsyncWaitDataDelayPool() + { + var waitHandlePool = new WaitHandlePool(); + var cts = new CancellationTokenSource(1000 * 60); + for (var i = 0; i < this.Count; i++) + { + var data = new MyWaitData(); + using (var waitData = waitHandlePool.GetWaitDataAsync(data)) + { + var task = waitData.WaitAsync(cts.Token).ConfigureAwait(false); + + waitData.Set(data); + + await task; + } + } + + } + + [Benchmark] + public async Task RunAsyncWaitDataDelay() + { + var waitHandlePool = new WaitHandlePool2(); + var cts = new CancellationTokenSource(1000 * 60); + for (var i = 0; i < this.Count; i++) + { + var data = new MyWaitData(); + using (var waitData = waitHandlePool.GetWaitDataAsync(data)) + { + var task = waitData.WaitAsync(cts.Token).ConfigureAwait(false); + + waitData.Set(data); + + await task; + } + } + + } + + private class MyWaitData : IWaitHandle + { + public int Sign { get; set; } + } + + public sealed class WaitHandlePool2 + where T : class, IWaitHandle + { + private readonly int m_maxSign; + private readonly int m_minSign; + private readonly ConcurrentDictionary> m_waitDic = new(); + private readonly Action _remove; + private int m_currentSign; + + /// + /// 初始化类的新实例。 + /// + /// 签名的最小值,默认为1。 + /// 签名的最大值,默认为。 + /// + /// 签名范围用于控制自动生成的唯一标识符的取值范围。 + /// 当签名达到最大值时,会自动重置到最小值重新开始分配。 + /// + public WaitHandlePool2(int minSign = 1, int maxSign = int.MaxValue) + { + this.m_minSign = minSign; + this.m_currentSign = minSign; + this.m_maxSign = maxSign; + + this._remove = this.Remove; + } + + /// + /// 取消池中所有等待操作。 + /// + /// + /// 此方法会遍历池中所有的等待数据,并调用其方法来取消等待。 + /// 取消后的等待数据会从池中移除。适用于应用程序关闭或需要批量取消所有等待操作的场景。 + /// + public void CancelAll() + { + var signs = this.m_waitDic.Keys.ToList(); + foreach (var sign in signs) + { + if (this.m_waitDic.TryRemove(sign, out var item)) + { + item.Cancel(); + } + } + } + + /// + /// 获取与指定结果关联的异步等待数据。 + /// + /// 要关联的结果对象。 + /// 指示是否自动为结果对象分配签名,默认为。 + /// 创建的实例。 + /// 当指定的签名已被使用时抛出。 + /// + /// 如果,方法会自动为结果对象生成唯一签名。 + /// 创建的等待数据会被添加到池中,直到被设置结果或取消时才会移除。 + /// + public AsyncWaitData2 GetWaitDataAsync(T result, bool autoSign = true) + { + if (autoSign) + { + result.Sign = this.GetSign(); + } + var waitDataAsyncSlim = new AsyncWaitData2(result.Sign, this._remove, result); + + if (!this.m_waitDic.TryAdd(result.Sign, waitDataAsyncSlim)) + { + //ThrowHelper.ThrowInvalidOperationException($"The sign '{result.Sign}' is already in use."); + return default; + } + return waitDataAsyncSlim; + } + + /// + /// 获取具有自动生成签名的异步等待数据。 + /// + /// 输出参数,返回自动生成的签名值。 + /// 创建的实例。 + /// 当生成的签名已被使用时抛出。 + /// + /// 此方法会自动生成唯一签名,并创建不包含挂起数据的等待对象。 + /// 适用于只需要等待通知而不关心具体数据内容的场景。 + /// + public AsyncWaitData2 GetWaitDataAsync(out int sign) + { + sign = this.GetSign(); + var waitDataAsyncSlim = new AsyncWaitData2(sign, this._remove, default); + if (!this.m_waitDic.TryAdd(sign, waitDataAsyncSlim)) + { + return default; + } + return waitDataAsyncSlim; + } + + /// + /// 使用指定结果设置对应签名的等待操作。 + /// + /// 包含签名和结果数据的对象。 + /// 如果成功设置等待操作则返回;否则返回 + /// + /// 此方法根据结果对象的签名查找对应的等待数据,并设置其结果。 + /// 设置成功后,等待数据会从池中移除,正在等待的任务会被完成。 + /// 如果找不到对应签名的等待数据,则返回。 + /// + public bool Set(T result) + { + if (this.m_waitDic.TryRemove(result.Sign, out var waitDataAsync)) + { + waitDataAsync.Set(result); + return true; + } + return false; + } + + /// + /// 尝试获取指定签名的异步等待数据。 + /// + /// 要查找的签名。 + /// 输出参数,如果找到则返回对应的等待数据;否则为。 + /// 如果找到指定签名的等待数据则返回;否则返回 + /// + /// 此方法允许查询池中是否存在特定签名的等待数据,而不会修改池的状态。 + /// 适用于需要检查等待状态或获取等待数据进行进一步操作的场景。 + /// + public bool TryGetDataAsync(int sign, out AsyncWaitData2 waitDataAsync) + { + return this.m_waitDic.TryGetValue(sign, out waitDataAsync); + } + + /// + /// 生成下一个可用的唯一签名。 + /// + /// 生成的唯一签名值。 + /// + /// 使用原子递增操作确保签名的唯一性和线程安全性。 + /// 当签名达到最大值时,会重新开始分配以避免溢出。 + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int GetSign() + { + while (true) + { + var currentSign = this.m_currentSign; + var nextSign = currentSign >= this.m_maxSign ? this.m_minSign : currentSign + 1; + + if (Interlocked.CompareExchange(ref this.m_currentSign, nextSign, currentSign) == currentSign) + { + return nextSign; + } + // 如果CAS失败,继续重试 + } + } + + /// + /// 从池中移除指定签名的等待数据。 + /// + /// 要移除的签名。 + /// + /// 此方法由等待数据在释放时自动调用,确保池中不会保留已完成或已取消的等待对象。 + /// + private void Remove(int sign) + { + this.m_waitDic.TryRemove(sign, out _); + } + } + + public sealed class AsyncWaitData2 : DisposableObject, IValueTaskSource + { + // ManualResetValueTaskSourceCore 是一个结构体,避免了额外托管对象分配,但需要配合 token 使用。 + private ManualResetValueTaskSourceCore _core; // 核心结构体,不会分配额外对象 + + // 缓存的移除回调,由 WaitHandlePool 构造时传入,避免每次分配委托。 + private readonly Action _remove; + + // 挂起时的临时数据 + private readonly T _pendingData; + + // 完成时的数据 + private T _completedData; + + // 当前等待状态(成功/取消/未完成等) + private WaitDataStatus _status; + private CancellationTokenRegistration Registration; + + /// + /// 使用指定签名和移除回调初始化一个新的 实例。 + /// + /// 此等待项对应的签名(用于在池中查找)。 + /// 完成或释放时调用的回调,用于将此实例从等待池中移除。 + /// 可选的挂起数据,当创建时可以携带一个初始占位数据。 + public AsyncWaitData2(int sign, Action remove, T pendingData) + { + this.Sign = sign; + this._remove = remove; + this._pendingData = pendingData; + this._core.RunContinuationsAsynchronously = true; // 确保续体异步执行,避免潜在的栈内联执行问题 + } + + /// + /// 获取此等待项的签名标识。 + /// + public int Sign { get; } + + /// + /// 获取挂起时的原始数据(如果在创建时传入)。 + /// + public T PendingData => this._pendingData; + + /// + /// 获取已完成时的返回数据。 + /// + public T CompletedData => this._completedData; + + /// + /// 获取当前等待状态(例如:Success、Canceled 等)。 + /// + public WaitDataStatus Status => this._status; + + /// + /// 取消当前等待,标记为已取消并触发等待任务的异常(OperationCanceledException)。 + /// + public void Cancel() + { + this.Set(WaitDataStatus.Canceled, default!); + } + + /// + /// 将等待项设置为成功并携带结果数据。 + /// + /// 要设置的完成数据。 + public void Set(T result) + { + this.Set(WaitDataStatus.Success, result); + } + + /// + /// 设置等待项的状态和数据,并完成对应的 ValueTask。 + /// + /// 要设置的状态。 + /// 要设置的完成数据。 + public void Set(WaitDataStatus status, T result) + { + this._status = status; + this._completedData = result; + + if (status == WaitDataStatus.Canceled) + this._core.SetException(new OperationCanceledException()); + else + this._core.SetResult(result); + } + + /// + /// 异步等待此项完成,返回一个 ,可传入取消令牌以取消等待。 + /// + /// 可选的取消令牌。若触发则会调用 。 + /// 表示等待状态的 ValueTask。 + public ValueTask WaitAsync(CancellationToken cancellationToken = default) + { + if (cancellationToken.CanBeCanceled) + { + this.Registration = cancellationToken.Register(this.Cancel); + } + + return new ValueTask(this, this._core.Version); + } + + /// + /// 从核心获取结果(显式接口实现)。 + /// 注意:此方法由 ValueTask 基础设施调用,不应直接在用户代码中调用。 + /// + WaitDataStatus IValueTaskSource.GetResult(short token) + { + this._core.GetResult(token); + return this._status; + } + + /// + /// 获取当前 ValueTask 源的状态(显式接口实现)。 + /// + ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) + => this._core.GetStatus(token); + + /// + /// 注册续体(显式接口实现)。 + /// 注意:flags 可以控制是否捕获上下文等行为。 + /// + void IValueTaskSource.OnCompleted(Action continuation, object? state, + short token, ValueTaskSourceOnCompletedFlags flags) + => this._core.OnCompleted(continuation, state, token, flags); + + /// + /// 释放托管资源时调用,会触发传入的移除回调,从所在的等待池中移除此等待项。 + /// + /// 是否为显式释放。 + protected override void Dispose(bool disposing) + { + if (disposing) + { + this.Registration.Dispose(); + this._remove(this.Sign); + } + base.Dispose(disposing); + } + } + +} diff --git a/src/Plugin/ThingsGateway.Foundation.Benchmark/Program.cs b/src/Plugin/ThingsGateway.Foundation.Benchmark/Program.cs index e5de23958..42f71e312 100644 --- a/src/Plugin/ThingsGateway.Foundation.Benchmark/Program.cs +++ b/src/Plugin/ThingsGateway.Foundation.Benchmark/Program.cs @@ -14,8 +14,6 @@ using BenchmarkDotNet.Configs; using BenchmarkDotNet.Running; -using ThingsGateway.Foundation; - namespace BenchmarkConsoleApp { internal class Program @@ -47,15 +45,18 @@ namespace BenchmarkConsoleApp //ManualConfig.Create(DefaultConfig.Instance) //.WithOptions(ConfigOptions.DisableOptimizationsValidator) //); - - BenchmarkRunner.Run( - ManualConfig.Create(DefaultConfig.Instance) - .WithOptions(ConfigOptions.DisableOptimizationsValidator) - ); -// BenchmarkRunner.Run( -//ManualConfig.Create(DefaultConfig.Instance) -//.WithOptions(ConfigOptions.DisableOptimizationsValidator) -//); + BenchmarkRunner.Run( +ManualConfig.Create(DefaultConfig.Instance) +.WithOptions(ConfigOptions.DisableOptimizationsValidator) +); + // BenchmarkRunner.Run( + // ManualConfig.Create(DefaultConfig.Instance) + // .WithOptions(ConfigOptions.DisableOptimizationsValidator) + //); + // BenchmarkRunner.Run( + //ManualConfig.Create(DefaultConfig.Instance) + //.WithOptions(ConfigOptions.DisableOptimizationsValidator) + //); // BenchmarkRunner.Run( //ManualConfig.Create(DefaultConfig.Instance) //.WithOptions(ConfigOptions.DisableOptimizationsValidator) diff --git a/src/Plugin/ThingsGateway.Foundation.Modbus/Slave/ModbusSlave.cs b/src/Plugin/ThingsGateway.Foundation.Modbus/Slave/ModbusSlave.cs index 4fc1c9349..4b94f50b0 100644 --- a/src/Plugin/ThingsGateway.Foundation.Modbus/Slave/ModbusSlave.cs +++ b/src/Plugin/ThingsGateway.Foundation.Modbus/Slave/ModbusSlave.cs @@ -8,6 +8,8 @@ // QQ群:605534569 //------------------------------------------------------------------------------ +using PooledAwait; + using System.Buffers; using System.Collections.Concurrent; using System.Runtime.InteropServices; @@ -157,7 +159,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress { return PackHelper.LoadSourceRead(this, deviceVariables, maxPack, defaultIntervalTime); } - + /// protected override Task DisposeAsync(bool disposing) { @@ -352,10 +354,10 @@ public class ModbusSlave : DeviceBase, IModbusAddress Init(mAddress); } - ModbusServer01ByteBlocks.TryGetValue(mAddress.Station,out var ModbusServer01ByteBlock); - ModbusServer02ByteBlocks.TryGetValue(mAddress.Station,out var ModbusServer02ByteBlock); - ModbusServer03ByteBlocks.TryGetValue(mAddress.Station,out var ModbusServer03ByteBlock); - ModbusServer04ByteBlocks.TryGetValue(mAddress.Station,out var ModbusServer04ByteBlock); + ModbusServer01ByteBlocks.TryGetValue(mAddress.Station, out var ModbusServer01ByteBlock); + ModbusServer02ByteBlocks.TryGetValue(mAddress.Station, out var ModbusServer02ByteBlock); + ModbusServer03ByteBlocks.TryGetValue(mAddress.Station, out var ModbusServer03ByteBlock); + ModbusServer04ByteBlocks.TryGetValue(mAddress.Station, out var ModbusServer04ByteBlock); if (read) { using (new ReadLock(_lockSlim)) @@ -475,33 +477,31 @@ public class ModbusSlave : DeviceBase, IModbusAddress return mAddress; } /// - public override async ValueTask WriteAsync(string address, ReadOnlyMemory value, DataTypeEnum dataType, CancellationToken cancellationToken = default) + public override ValueTask WriteAsync(string address, ReadOnlyMemory value, DataTypeEnum dataType, CancellationToken cancellationToken = default) { try { - await EasyValueTask.CompletedTask.ConfigureAwait(false); var mAddress = GetModbusAddress(address, Station); mAddress.SlaveWriteDatas = new(value); - return ModbusRequest(mAddress, false, cancellationToken); + return EasyValueTask.FromResult(ModbusRequest(mAddress, false, cancellationToken)); } catch (Exception ex) { - return new OperResult(ex); + return EasyValueTask.FromResult(new OperResult(ex)); } } /// - public override async ValueTask WriteAsync(string address, ReadOnlyMemory value, CancellationToken cancellationToken = default) + public override ValueTask WriteAsync(string address, ReadOnlyMemory value, CancellationToken cancellationToken = default) { try { - await EasyValueTask.CompletedTask.ConfigureAwait(false); var mAddress = GetModbusAddress(address, Station); if (mAddress.IsBitFunction) { mAddress.SlaveWriteDatas = new(value.Span.BoolToByte()); ModbusRequest(mAddress, false, cancellationToken); - return OperResult.Success; + return EasyValueTask.FromResult(OperResult.Success); } else { @@ -509,7 +509,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress { mAddress.Length = 2; var readData = ModbusRequest(mAddress, true, cancellationToken); - if (!readData.IsSuccess) return readData; + if (!readData.IsSuccess) return EasyValueTask.FromResult(readData); var writeData = TouchSocketBitConverter.BigEndian.To(readData.Content.Span); var span = value.Span; for (int i = 0; i < value.Length; i++) @@ -518,17 +518,17 @@ public class ModbusSlave : DeviceBase, IModbusAddress } mAddress.SlaveWriteDatas = new(ThingsGatewayBitConverter.GetBytes(writeData)); ModbusRequest(mAddress, false, cancellationToken); - return OperResult.Success; + return EasyValueTask.FromResult(OperResult.Success); } else { - return new OperResult(string.Format(AppResource.ValueOverlimit, nameof(mAddress.BitIndex), 16)); + return EasyValueTask.FromResult(new OperResult(string.Format(AppResource.ValueOverlimit, nameof(mAddress.BitIndex), 16))); } } } catch (Exception ex) { - return new OperResult(ex); + return EasyValueTask.FromResult(new OperResult(ex)); } } protected override ValueTask ChannelReceived(IClientChannel client, ReceivedDataEventArgs e, bool last) @@ -536,20 +536,26 @@ public class ModbusSlave : DeviceBase, IModbusAddress return HandleChannelReceivedAsync(client, e, last); } - private async ValueTask HandleChannelReceivedAsync(IClientChannel client, ReceivedDataEventArgs e, bool last) + private ValueTask HandleChannelReceivedAsync(IClientChannel client, ReceivedDataEventArgs e, bool last) { - if (!TryParseRequest(e.RequestInfo, out var modbusRequest, out var sequences, out var modbusRtu)) + return HandleChannelReceivedAsync(this, client, e); + + static async PooledValueTask HandleChannelReceivedAsync(ModbusSlave @this, IClientChannel client, ReceivedDataEventArgs e) + { + if (!TryParseRequest(e.RequestInfo, out var modbusRequest, out var sequences, out var modbusRtu)) + return; + + if (!@this.MulStation && modbusRequest.Station != @this.Station) + return; + + var function = NormalizeFunctionCode(modbusRequest.FunctionCode); + + if (function <= 4) + await @this.HandleReadRequestAsync(client, e, modbusRequest, sequences, modbusRtu).ConfigureAwait(false); + else + await @this.HandleWriteRequestAsync(client, e, modbusRequest, sequences, modbusRtu, function).ConfigureAwait(false); return; - - if (!MulStation && modbusRequest.Station != Station) - return; - - var function = NormalizeFunctionCode(modbusRequest.FunctionCode); - - if (function <= 4) - await HandleReadRequestAsync(client, e, modbusRequest, sequences, modbusRtu).ConfigureAwait(false); - else - await HandleWriteRequestAsync(client, e, modbusRequest, sequences, modbusRtu, function).ConfigureAwait(false); + } } private static bool TryParseRequest(object requestInfo, out ModbusRequest modbusRequest, out ReadOnlySequence sequences, out bool modbusRtu) @@ -580,7 +586,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress private static byte NormalizeFunctionCode(byte funcCode) => funcCode > 0x30 ? (byte)(funcCode - 0x30) : funcCode; - private async Task HandleReadRequestAsync( + private Task HandleReadRequestAsync( IClientChannel client, ReceivedDataEventArgs e, ModbusRequest modbusRequest, @@ -590,27 +596,31 @@ public class ModbusSlave : DeviceBase, IModbusAddress var data = ModbusRequest(modbusRequest, true); if (!data.IsSuccess) { - await WriteError(modbusRtu, client, sequences, e).ConfigureAwait(false); - return; + return WriteError(modbusRtu, client, sequences, e); } - ValueByteBlock byteBlock = new(1024); - try + return Write(this, client, e, modbusRequest, sequences, modbusRtu, data); + + static async PooledTask Write(ModbusSlave @this, IClientChannel client, ReceivedDataEventArgs e, ModbusRequest modbusRequest, ReadOnlySequence sequences, bool modbusRtu, OperResult> data) { - WriteReadResponse(modbusRequest, sequences, data.Content, ref byteBlock, modbusRtu); - await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false); - } - catch - { - await WriteError(modbusRtu, client, sequences, e).ConfigureAwait(false); - } - finally - { - byteBlock.SafeDispose(); + ValueByteBlock byteBlock = new(1024); + try + { + WriteReadResponse(modbusRequest, sequences, data.Content, ref byteBlock, modbusRtu); + await @this.ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false); + } + catch + { + await @this.WriteError(modbusRtu, client, sequences, e).ConfigureAwait(false); + } + finally + { + byteBlock.SafeDispose(); + } } } - private async Task HandleWriteRequestAsync( + private Task HandleWriteRequestAsync( IClientChannel client, ReceivedDataEventArgs e, ModbusRequest modbusRequest, @@ -618,50 +628,62 @@ public class ModbusSlave : DeviceBase, IModbusAddress bool modbusRtu, byte f) { - var modbusAddress = new ModbusAddress(modbusRequest); - bool isSuccess; + return HandleWriteRequestAsync(this, client, e, modbusRequest, sequences, modbusRtu, f); - switch (f) + + static async PooledTask HandleWriteRequestAsync(ModbusSlave @this, IClientChannel client, ReceivedDataEventArgs e, ModbusRequest modbusRequest, ReadOnlySequence sequences, bool modbusRtu, byte f) { - case 5: - case 15: - modbusAddress.WriteFunctionCode = modbusRequest.FunctionCode; - modbusAddress.FunctionCode = 1; - isSuccess = await HandleWriteCoreAsync(modbusAddress, client, modbusRequest).ConfigureAwait(false); - break; + var modbusAddress = new ModbusAddress(modbusRequest); + bool isSuccess; - case 6: - case 16: - modbusAddress.WriteFunctionCode = modbusRequest.FunctionCode; - modbusAddress.FunctionCode = 3; - isSuccess = await HandleWriteCoreAsync(modbusAddress, client, modbusRequest).ConfigureAwait(false); - break; + switch (f) + { + case 5: + case 15: + modbusAddress.WriteFunctionCode = modbusRequest.FunctionCode; + modbusAddress.FunctionCode = 1; + isSuccess = await @this.HandleWriteCoreAsync(modbusAddress, client, modbusRequest).ConfigureAwait(false); + break; - default: - return; + case 6: + case 16: + modbusAddress.WriteFunctionCode = modbusRequest.FunctionCode; + modbusAddress.FunctionCode = 3; + isSuccess = await @this.HandleWriteCoreAsync(modbusAddress, client, modbusRequest).ConfigureAwait(false); + break; + + default: + return; + } + + if (isSuccess) + await @this.WriteSuccess(modbusRtu, client, sequences, e).ConfigureAwait(false); + else + await @this.WriteError(modbusRtu, client, sequences, e).ConfigureAwait(false); + return; } - - if (isSuccess) - await WriteSuccess(modbusRtu, client, sequences, e).ConfigureAwait(false); - else - await WriteError(modbusRtu, client, sequences, e).ConfigureAwait(false); } - private async Task HandleWriteCoreAsync(ModbusAddress address, IClientChannel client, ModbusRequest modbusRequest) + private Task HandleWriteCoreAsync(ModbusAddress address, IClientChannel client, ModbusRequest modbusRequest) { - if (WriteData != null) - { - var result = await WriteData(address, ThingsGatewayBitConverter, client).ConfigureAwait(false); - if (!result.IsSuccess) return false; - } + return HandleWriteCoreAsync(this, address, client, modbusRequest); - if (IsWriteMemory) + static async PooledTask HandleWriteCoreAsync(ModbusSlave @this, ModbusAddress address, IClientChannel client, ModbusRequest modbusRequest) { - var memResult = ModbusRequest(modbusRequest, false); - return memResult.IsSuccess; - } + if (@this.WriteData != null) + { + var result = await @this.WriteData(address, @this.ThingsGatewayBitConverter, client).ConfigureAwait(false); + if (!result.IsSuccess) return false; + } - return true; + if (@this.IsWriteMemory) + { + var memResult = @this.ModbusRequest(modbusRequest, false); + return memResult.IsSuccess; + } + + return true; + } } private static void WriteReadResponse( @@ -696,63 +718,78 @@ public class ModbusSlave : DeviceBase, IModbusAddress ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), EndianType.Big, 5); } - private async Task ReturnData(IClientChannel client, ReadOnlyMemory sendData, ReceivedDataEventArgs e) + private Task ReturnData(IClientChannel client, ReadOnlyMemory sendData, ReceivedDataEventArgs e) { - if (SendDelayTime > 0) - await Task.Delay(SendDelayTime).ConfigureAwait(false); - if (client is IUdpClientSender udpClientSender) - await udpClientSender.SendAsync(((UdpReceivedDataEventArgs)e).EndPoint, sendData).ConfigureAwait(false); - else - await client.SendAsync(sendData, client.ClosedToken).ConfigureAwait(false); - } + return ReturnData(SendDelayTime, client, sendData, e); - private async Task WriteError(bool modbusRtu, IClientChannel client, ReadOnlySequence bytes, ReceivedDataEventArgs e) - { - ValueByteBlock byteBlock = new(20); - try + static async PooledTask ReturnData(int deley, IClientChannel client, ReadOnlyMemory sendData, ReceivedDataEventArgs e) { - if (modbusRtu) - { - ByteBlockExtension.Write(ref byteBlock, bytes.Slice(0, 2)); - WriterExtension.WriteValue(ref byteBlock, (byte)1); - byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Span)); - ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Span[1] + 128), EndianType.Big, 1); - } + if (deley > 0) + await Task.Delay(deley).ConfigureAwait(false); + if (client is IUdpClientSender udpClientSender) + await udpClientSender.SendAsync(((UdpReceivedDataEventArgs)e).EndPoint, sendData).ConfigureAwait(false); else - { - ByteBlockExtension.Write(ref byteBlock, bytes.Slice(0, 8)); - WriterExtension.WriteValue(ref byteBlock, (byte)1); - ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), EndianType.Big, 5); - ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Span[7] + 128), EndianType.Big, 7); - } - await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false); - } - finally - { - byteBlock.SafeDispose(); + await client.SendAsync(sendData, client.ClosedToken).ConfigureAwait(false); } } - private async Task WriteSuccess(bool modbusRtu, IClientChannel client, ReadOnlySequence bytes, ReceivedDataEventArgs e) + private Task WriteError(bool modbusRtu, IClientChannel client, ReadOnlySequence bytes, ReceivedDataEventArgs e) { - ValueByteBlock byteBlock = new(20); - try + return WriteError(this, modbusRtu, client, bytes, e); + + static async PooledTask WriteError(ModbusSlave @this, bool modbusRtu, IClientChannel client, ReadOnlySequence bytes, ReceivedDataEventArgs e) { - if (modbusRtu) + ValueByteBlock byteBlock = new(20); + try { - ByteBlockExtension.Write(ref byteBlock, bytes.Slice(0, 6)); - byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Span)); + if (modbusRtu) + { + ByteBlockExtension.Write(ref byteBlock, bytes.Slice(0, 2)); + WriterExtension.WriteValue(ref byteBlock, (byte)1); + byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Span)); + ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Span[1] + 128), EndianType.Big, 1); + } + else + { + ByteBlockExtension.Write(ref byteBlock, bytes.Slice(0, 8)); + WriterExtension.WriteValue(ref byteBlock, (byte)1); + ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), EndianType.Big, 5); + ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Span[7] + 128), EndianType.Big, 7); + } + await @this.ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false); } - else + finally { - ByteBlockExtension.Write(ref byteBlock, bytes.Slice(0, 12)); - ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), EndianType.Big, 5); + byteBlock.SafeDispose(); } - await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false); } - finally + } + + private Task WriteSuccess(bool modbusRtu, IClientChannel client, ReadOnlySequence bytes, ReceivedDataEventArgs e) + { + return WriteSuccess(this, modbusRtu, client, bytes, e); + + static async PooledTask WriteSuccess(ModbusSlave @this, bool modbusRtu, IClientChannel client, ReadOnlySequence bytes, ReceivedDataEventArgs e) { - byteBlock.SafeDispose(); + ValueByteBlock byteBlock = new(20); + try + { + if (modbusRtu) + { + ByteBlockExtension.Write(ref byteBlock, bytes.Slice(0, 6)); + byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Span)); + } + else + { + ByteBlockExtension.Write(ref byteBlock, bytes.Slice(0, 12)); + ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), EndianType.Big, 5); + } + await @this.ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false); + } + finally + { + byteBlock.SafeDispose(); + } } } diff --git a/src/Plugin/ThingsGateway.Foundation.OpcUa/OpcUaMaster/OpcUaMaster.cs b/src/Plugin/ThingsGateway.Foundation.OpcUa/OpcUaMaster/OpcUaMaster.cs index c775955b0..07ae603a6 100644 --- a/src/Plugin/ThingsGateway.Foundation.OpcUa/OpcUaMaster/OpcUaMaster.cs +++ b/src/Plugin/ThingsGateway.Foundation.OpcUa/OpcUaMaster/OpcUaMaster.cs @@ -395,18 +395,17 @@ public class OpcUaMaster : IAsyncDisposable /// /// 连接到服务器 /// - public async Task ConnectAsync(CancellationToken cancellationToken) + public Task ConnectAsync(CancellationToken cancellationToken) { - await ConnectAsync(OpcUaProperty.OpcUrl, cancellationToken).ConfigureAwait(false); + return ConnectAsync(OpcUaProperty.OpcUrl, cancellationToken); } /// /// 断开连接。 /// - public async Task DisconnectAsync() + public Task DisconnectAsync() { - await PrivateDisconnectAsync().ConfigureAwait(false); - + return PrivateDisconnectAsync(); } public async ValueTask DisposeAsync() diff --git a/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/SiemensS7Master.cs b/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/SiemensS7Master.cs index 6b519064a..41c595c4d 100644 --- a/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/SiemensS7Master.cs +++ b/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/SiemensS7Master.cs @@ -8,6 +8,8 @@ // QQ群:605534569 //------------------------------------------------------------------------------ +using PooledAwait; + using ThingsGateway.Foundation.Extension.String; using ThingsGateway.NewLife; using ThingsGateway.NewLife.Extension; @@ -144,71 +146,76 @@ public partial class SiemensS7Master : DeviceBase /// /// 此方法并不会智能分组以最大化效率,减少传输次数,因为返回值是byte[],所以一切都按地址数组的顺序执行,最后合并数组 /// - public async ValueTask>> S7ReadAsync( + public ValueTask>> S7ReadAsync( SiemensS7Address[] addresses, CancellationToken cancellationToken = default) { - var byteBuffer = new ValueByteBlock(512); + return S7ReadAsync(this, addresses, cancellationToken); - try + static async PooledValueTask>> S7ReadAsync(SiemensS7Master @this, SiemensS7Address[] addresses, CancellationToken cancellationToken) { - foreach (var address in addresses) + var byteBuffer = new ValueByteBlock(512); + + try { - int readCount = 0; - int totalLength = address.Length == 0 ? 1 : address.Length; - int originalStart = address.AddressStart; - - try + foreach (var address in addresses) { - while (readCount < totalLength) + int readCount = 0; + int totalLength = address.Length == 0 ? 1 : address.Length; + int originalStart = address.AddressStart; + + try { - // 每次读取的 PDU 长度,循环直到读取完整 - int chunkLength = Math.Min(totalLength - readCount, PduLength); - address.Length = chunkLength; - - var result = await SendThenReturnAsync( - new S7Send([address], true), - cancellationToken: cancellationToken - ).ConfigureAwait(false); - - if (!result.IsSuccess) - return result; - - byteBuffer.Write(result.Content.Span); - - if (readCount + chunkLength >= totalLength) + while (readCount < totalLength) { - if (addresses.Length == 1) - { + // 每次读取的 PDU 长度,循环直到读取完整 + int chunkLength = Math.Min(totalLength - readCount, @this.PduLength); + address.Length = chunkLength; + + var result = await @this.SendThenReturnAsync( + new S7Send([address], true), + cancellationToken: cancellationToken + ).ConfigureAwait(false); + + if (!result.IsSuccess) return result; + + byteBuffer.Write(result.Content.Span); + + if (readCount + chunkLength >= totalLength) + { + if (addresses.Length == 1) + { + return result; + } + break; } - break; + + readCount += chunkLength; + + // 更新地址起点 + if (address.DataCode == S7Area.TM || address.DataCode == S7Area.CT) + address.AddressStart += chunkLength / 2; + else + address.AddressStart += chunkLength * 8; } - - readCount += chunkLength; - - // 更新地址起点 - if (address.DataCode == S7Area.TM || address.DataCode == S7Area.CT) - address.AddressStart += chunkLength / 2; - else - address.AddressStart += chunkLength * 8; + } + finally + { + address.AddressStart = originalStart; } } - finally - { - address.AddressStart = originalStart; - } - } - return new OperResult> { Content = byteBuffer.ToArray() }; - } - catch (Exception ex) - { - return new OperResult>(ex); - } - finally - { - byteBuffer.SafeDispose(); + return new OperResult> { Content = byteBuffer.ToArray() }; + } + catch (Exception ex) + { + return new OperResult>(ex); + } + finally + { + byteBuffer.SafeDispose(); + } } } @@ -216,79 +223,65 @@ public partial class SiemensS7Master : DeviceBase /// /// 此方法并不会智能分组以最大化效率,减少传输次数,因为返回值是byte[],所以一切都按地址数组的顺序执行,最后合并数组 /// - public async ValueTask> S7WriteAsync( + public ValueTask> S7WriteAsync( SiemensS7Address[] addresses, CancellationToken cancellationToken = default) { - var dictOperResult = new Dictionary(); + return S7WriteAsync(this, addresses, cancellationToken); - void SetFailOperResult(OperResult operResult) + static async PooledValueTask> S7WriteAsync(SiemensS7Master @this, SiemensS7Address[] addresses, CancellationToken cancellationToken) { - foreach (var address in addresses) - { - dictOperResult.TryAdd(address, operResult); - } - } + var dictOperResult = new Dictionary(); - var firstAddress = addresses[0]; - - // 单位写入(位写入) - if (addresses.Length <= 1 && firstAddress.IsBit) - { - var byteBuffer = new ValueByteBlock(512); - try + void SetFailOperResult(OperResult operResult) { - var writeResult = await SendThenReturnAsync( - new S7Send([firstAddress], false), - cancellationToken: cancellationToken - ).ConfigureAwait(false); - - dictOperResult.TryAdd(firstAddress, writeResult); - return dictOperResult; - } - catch (Exception ex) - { - SetFailOperResult(new OperResult(ex)); - return dictOperResult; - } - finally - { - byteBuffer.SafeDispose(); - } - } - else - { - // 多写入 - var addressChunks = new List>(); - ushort dataLength = 0; - ushort itemCount = 1; - var currentChunk = new List(); - - for (int i = 0; i < addresses.Length; i++) - { - var address = addresses[i]; - dataLength += (ushort)(address.Data.Length + 4); - ushort telegramLength = (ushort)(itemCount * 12 + 19 + dataLength); - - if (telegramLength < PduLength) + foreach (var address in addresses) { - currentChunk.Add(address); - itemCount++; - - if (i == addresses.Length - 1) - addressChunks.Add(currentChunk); + dictOperResult.TryAdd(address, operResult); } - else + } + + var firstAddress = addresses[0]; + + // 单位写入(位写入) + if (addresses.Length <= 1 && firstAddress.IsBit) + { + var byteBuffer = new ValueByteBlock(512); + try { - addressChunks.Add(currentChunk); - currentChunk = new List(); - dataLength = 0; - itemCount = 1; + var writeResult = await @this.SendThenReturnAsync( + new S7Send([firstAddress], false), + cancellationToken: cancellationToken + ).ConfigureAwait(false); + dictOperResult.TryAdd(firstAddress, writeResult); + return dictOperResult; + } + catch (Exception ex) + { + SetFailOperResult(new OperResult(ex)); + return dictOperResult; + } + finally + { + byteBuffer.SafeDispose(); + } + } + else + { + // 多写入 + var addressChunks = new List>(); + ushort dataLength = 0; + ushort itemCount = 1; + var currentChunk = new List(); + + for (int i = 0; i < addresses.Length; i++) + { + var address = addresses[i]; dataLength += (ushort)(address.Data.Length + 4); - telegramLength = (ushort)(itemCount * 12 + 19 + dataLength); + ushort telegramLength = (ushort)(itemCount * 12 + 19 + dataLength); - if (telegramLength < PduLength) + if (telegramLength < @this.PduLength) { currentChunk.Add(address); itemCount++; @@ -298,34 +291,53 @@ public partial class SiemensS7Master : DeviceBase } else { - SetFailOperResult(new OperResult("Write length exceeds limit")); + addressChunks.Add(currentChunk); + currentChunk = new List(); + dataLength = 0; + itemCount = 1; + + dataLength += (ushort)(address.Data.Length + 4); + telegramLength = (ushort)(itemCount * 12 + 19 + dataLength); + + if (telegramLength < @this.PduLength) + { + currentChunk.Add(address); + itemCount++; + + if (i == addresses.Length - 1) + addressChunks.Add(currentChunk); + } + else + { + SetFailOperResult(new OperResult("Write length exceeds limit")); + return dictOperResult; + } + } + } + + foreach (var chunk in addressChunks) + { + try + { + var result = await @this.SendThenReturnAsync( + new S7Send(chunk.ToArray(), false), + cancellationToken: cancellationToken + ).ConfigureAwait(false); + + foreach (var addr in chunk) + { + dictOperResult.TryAdd(addr, result); + } + } + catch (Exception ex) + { + SetFailOperResult(new OperResult(ex)); return dictOperResult; } } + + return dictOperResult; } - - foreach (var chunk in addressChunks) - { - try - { - var result = await SendThenReturnAsync( - new S7Send(chunk.ToArray(), false), - cancellationToken: cancellationToken - ).ConfigureAwait(false); - - foreach (var addr in chunk) - { - dictOperResult.TryAdd(addr, result); - } - } - catch (Exception ex) - { - SetFailOperResult(new OperResult(ex)); - return dictOperResult; - } - } - - return dictOperResult; } } diff --git a/src/Plugin/ThingsGateway.Plugin.DB/QuestDB/QuestDBProducer.other.cs b/src/Plugin/ThingsGateway.Plugin.DB/QuestDB/QuestDBProducer.other.cs index 14a48c22a..87961c796 100644 --- a/src/Plugin/ThingsGateway.Plugin.DB/QuestDB/QuestDBProducer.other.cs +++ b/src/Plugin/ThingsGateway.Plugin.DB/QuestDB/QuestDBProducer.other.cs @@ -8,6 +8,8 @@ // QQ群:605534569 //------------------------------------------------------------------------------ +using PooledAwait; + using System.Diagnostics; using ThingsGateway.Extension.Generic; @@ -81,91 +83,101 @@ public partial class QuestDBProducer : BusinessBaseWithCacheIntervalVariable AddQueueVarModel(new CacheDBItem(variable)); } } - private async ValueTask UpdateVarModel(IEnumerable item, CancellationToken cancellationToken) + private ValueTask UpdateVarModel(IEnumerable item, CancellationToken cancellationToken) { - var result = await InserableAsync(item.WhereIf(_driverPropertys.OnlineFilter, a => a.IsOnline == true).ToList(), cancellationToken).ConfigureAwait(false); - if (success != result.IsSuccess) - { - if (!result.IsSuccess) - LogMessage?.LogWarning(result.ToString()); - success = result.IsSuccess; - } + return UpdateVarModel(this, item, cancellationToken); - return result; + static async PooledValueTask UpdateVarModel(QuestDBProducer @this, IEnumerable item, CancellationToken cancellationToken) + { + var result = await @this.InserableAsync(item.WhereIf(@this._driverPropertys.OnlineFilter, a => a.IsOnline == true).ToList(), cancellationToken).ConfigureAwait(false); + if (@this.success != result.IsSuccess) + { + if (!result.IsSuccess) + @this.LogMessage?.LogWarning(result.ToString()); + @this.success = result.IsSuccess; + } + + return result; + } } #region 方法 - private async ValueTask InserableAsync(List dbInserts, CancellationToken cancellationToken) + private ValueTask InserableAsync(List dbInserts, CancellationToken cancellationToken) { - try + return InserableAsync(this, dbInserts, cancellationToken); + + static async PooledValueTask InserableAsync(QuestDBProducer @this, List dbInserts, CancellationToken cancellationToken) { - _db.Ado.CancellationToken = cancellationToken; - if (!_driverPropertys.BigTextScriptHistoryTable.IsNullOrEmpty()) + try { - var getDeviceModel = CSharpScriptEngineExtension.Do(_driverPropertys.BigTextScriptHistoryTable); - getDeviceModel.Logger = LogMessage; - - await getDeviceModel.DBInsertable(_db, dbInserts, cancellationToken).ConfigureAwait(false); - } - else - { - var stringData = dbInserts.Where(a => (!a.IsNumber && a.Value is not bool)); - var numberData = dbInserts.Where(a => (a.IsNumber || a.Value is bool)); - - if (numberData.Any()) + @this._db.Ado.CancellationToken = cancellationToken; + if (!@this._driverPropertys.BigTextScriptHistoryTable.IsNullOrEmpty()) { - Stopwatch stopwatch = new(); - stopwatch.Start(); - var data = numberData.AdaptListQuestDBNumberHistoryValue(); - int result = 0; - if (_driverPropertys.RestApi) - { - result = await _db.RestApi(_driverPropertys.HttpPort).BulkCopyAsync(data, _driverPropertys.NumberTableName).ConfigureAwait(false);//不要加分表 - } - else - { - result = await _db.Insertable(data).AS(_driverPropertys.NumberTableName).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false);//不要加分表 - } - stopwatch.Stop(); + var getDeviceModel = CSharpScriptEngineExtension.Do(@this._driverPropertys.BigTextScriptHistoryTable); + getDeviceModel.Logger = @this.LogMessage; - //var result = await db.Insertable(dbInserts).SplitTable().ExecuteCommandAsync().ConfigureAwait(false); - if (result > 0) + await getDeviceModel.DBInsertable(@this._db, dbInserts, cancellationToken).ConfigureAwait(false); + } + else + { + var stringData = dbInserts.Where(a => (!a.IsNumber && a.Value is not bool)); + var numberData = dbInserts.Where(a => (a.IsNumber || a.Value is bool)); + + if (numberData.Any()) { - LogMessage?.Trace($"TableName:{_driverPropertys.NumberTableName},Count:{result},watchTime: {stopwatch.ElapsedMilliseconds} ms"); + Stopwatch stopwatch = new(); + stopwatch.Start(); + var data = numberData.AdaptListQuestDBNumberHistoryValue(); + int result = 0; + if (@this._driverPropertys.RestApi) + { + result = await @this._db.RestApi(@this._driverPropertys.HttpPort).BulkCopyAsync(data, @this._driverPropertys.NumberTableName).ConfigureAwait(false);//不要加分表 + } + else + { + result = await @this._db.Insertable(data).AS(@this._driverPropertys.NumberTableName).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false);//不要加分表 + } + stopwatch.Stop(); + + //var result = await db.Insertable(dbInserts).SplitTable().ExecuteCommandAsync().ConfigureAwait(false); + if (result > 0) + { + @this.LogMessage?.Trace($"TableName:{@this._driverPropertys.NumberTableName},Count:{result},watchTime: {stopwatch.ElapsedMilliseconds} ms"); + } + } + + if (stringData.Any()) + { + Stopwatch stopwatch = new(); + stopwatch.Start(); + var data = stringData.AdaptListQuestDBHistoryValue(); + int result = 0; + if (@this._driverPropertys.RestApi) + { + result = await @this._db.RestApi(@this._driverPropertys.HttpPort).BulkCopyAsync(data, @this._driverPropertys.StringTableName).ConfigureAwait(false);//不要加分表 + } + else + { + result = await @this._db.Insertable(data).AS(@this._driverPropertys.StringTableName).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false);//不要加分表 + } + + stopwatch.Stop(); + + //var result = await db.Insertable(dbInserts).SplitTable().ExecuteCommandAsync().ConfigureAwait(false); + if (result > 0) + { + @this.LogMessage?.Trace($"TableName:{@this._driverPropertys.StringTableName},Count:{result},watchTime: {stopwatch.ElapsedMilliseconds} ms"); + } } } - if (stringData.Any()) - { - Stopwatch stopwatch = new(); - stopwatch.Start(); - var data = stringData.AdaptListQuestDBHistoryValue(); - int result = 0; - if (_driverPropertys.RestApi) - { - result = await _db.RestApi(_driverPropertys.HttpPort).BulkCopyAsync(data, _driverPropertys.StringTableName).ConfigureAwait(false);//不要加分表 - } - else - { - result = await _db.Insertable(data).AS(_driverPropertys.StringTableName).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false);//不要加分表 - } - - stopwatch.Stop(); - - //var result = await db.Insertable(dbInserts).SplitTable().ExecuteCommandAsync().ConfigureAwait(false); - if (result > 0) - { - LogMessage?.Trace($"TableName:{_driverPropertys.StringTableName},Count:{result},watchTime: {stopwatch.ElapsedMilliseconds} ms"); - } - } + return OperResult.Success; + } + catch (Exception ex) + { + return new OperResult(ex); } - - return OperResult.Success; - } - catch (Exception ex) - { - return new OperResult(ex); } } diff --git a/src/Plugin/ThingsGateway.Plugin.DB/SqlDB/SqlDBProducer.cs b/src/Plugin/ThingsGateway.Plugin.DB/SqlDB/SqlDBProducer.cs index dcaed7986..e5e55f4de 100644 --- a/src/Plugin/ThingsGateway.Plugin.DB/SqlDB/SqlDBProducer.cs +++ b/src/Plugin/ThingsGateway.Plugin.DB/SqlDB/SqlDBProducer.cs @@ -10,6 +10,8 @@ using BootstrapBlazor.Components; +using PooledAwait; + using ThingsGateway.Common; using ThingsGateway.DB; using ThingsGateway.Debug; @@ -239,39 +241,44 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariable await base.ProtectedStartAsync(cancellationToken).ConfigureAwait(false); } - protected override async Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) + protected override Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) { - if (_driverPropertys.IsReadDB) + return ProtectedExecuteAsync(this, cancellationToken); + + static async PooledTask ProtectedExecuteAsync(SqlDBProducer @this, CancellationToken cancellationToken) { - var list = RealTimeVariables.ToListWithDequeue(); - try + if (@this._driverPropertys.IsReadDB) { - var varLists = list.Batch(_driverPropertys.SplitSize); - foreach (var varList in varLists) + var list = @this.RealTimeVariables.ToListWithDequeue(); + try { - var result = await UpdateAsync(varList, cancellationToken).ConfigureAwait(false); - if (success != result.IsSuccess) + var varLists = list.Batch(@this._driverPropertys.SplitSize); + foreach (var varList in varLists) { - if (!result.IsSuccess) - LogMessage?.LogWarning(result.ToString()); - success = result.IsSuccess; + var result = await @this.UpdateAsync(varList, cancellationToken).ConfigureAwait(false); + if (@this.success != result.IsSuccess) + { + if (!result.IsSuccess) + @this.LogMessage?.LogWarning(result.ToString()); + @this.success = result.IsSuccess; + } } } + catch (Exception ex) + { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; + + list.ForEach(variable => @this.RealTimeVariables.AddOrUpdate(variable.Id, variable, (key, oldValue) => variable)); + } } - catch (Exception ex) + + if (@this._driverPropertys.IsHistoryDB) { - if (success) - LogMessage?.LogWarning(ex); - success = false; - - list.ForEach(variable => RealTimeVariables.AddOrUpdate(variable.Id, variable, (key, oldValue) => variable)); + await @this.Update(cancellationToken).ConfigureAwait(false); } } - - if (_driverPropertys.IsHistoryDB) - { - await Update(cancellationToken).ConfigureAwait(false); - } } private ISugarQueryable Query(DBHistoryValuePageInput input) diff --git a/src/Plugin/ThingsGateway.Plugin.DB/SqlDB/SqlDbProducer.other.cs b/src/Plugin/ThingsGateway.Plugin.DB/SqlDB/SqlDbProducer.other.cs index 03116cfcd..2a397cc1f 100644 --- a/src/Plugin/ThingsGateway.Plugin.DB/SqlDB/SqlDbProducer.other.cs +++ b/src/Plugin/ThingsGateway.Plugin.DB/SqlDB/SqlDbProducer.other.cs @@ -8,6 +8,8 @@ // QQ群:605534569 //------------------------------------------------------------------------------ +using PooledAwait; + using System.Collections.Concurrent; using System.Diagnostics; @@ -97,131 +99,146 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariable } } - private async ValueTask UpdateVarModel(IEnumerable item, CancellationToken cancellationToken) + private ValueTask UpdateVarModel(IEnumerable item, CancellationToken cancellationToken) { - var result = await InserableAsync(item.WhereIf(_driverPropertys.OnlineFilter, a => a.IsOnline == true).ToList(), cancellationToken).ConfigureAwait(false); - if (success != result.IsSuccess) - { - if (!result.IsSuccess) - LogMessage?.LogWarning(result.ToString()); - success = result.IsSuccess; - } + return UpdateVarModel(this, item, cancellationToken); - return result; + static async PooledValueTask UpdateVarModel(SqlDBProducer @this, IEnumerable item, CancellationToken cancellationToken) + { + var result = await @this.InserableAsync(item.WhereIf(@this._driverPropertys.OnlineFilter, a => a.IsOnline == true).ToList(), cancellationToken).ConfigureAwait(false); + if (@this.success != result.IsSuccess) + { + if (!result.IsSuccess) + @this.LogMessage?.LogWarning(result.ToString()); + @this.success = result.IsSuccess; + } + + return result; + } } #region 方法 - private async ValueTask InserableAsync(List dbInserts, CancellationToken cancellationToken) + private ValueTask InserableAsync(List dbInserts, CancellationToken cancellationToken) { - try + return InserableAsync(this, dbInserts, cancellationToken); + + static async PooledValueTask InserableAsync(SqlDBProducer @this, List dbInserts, CancellationToken cancellationToken) { - _db.Ado.CancellationToken = cancellationToken; - if (!_driverPropertys.BigTextScriptHistoryTable.IsNullOrEmpty()) + try { - var getDeviceModel = CSharpScriptEngineExtension.Do(_driverPropertys.BigTextScriptHistoryTable); - - getDeviceModel.Logger = LogMessage; - - await getDeviceModel.DBInsertable(_db, dbInserts, cancellationToken).ConfigureAwait(false); - } - else - { - var stringData = dbInserts.Where(a => (!a.IsNumber && a.Value is not bool)); - var numberData = dbInserts.Where(a => (a.IsNumber || a.Value is bool)); - - if (numberData.Any()) + @this._db.Ado.CancellationToken = cancellationToken; + if (!@this._driverPropertys.BigTextScriptHistoryTable.IsNullOrEmpty()) { - var data = numberData.AdaptEnumerableSQLNumberHistoryValue(); - Stopwatch stopwatch = new(); - stopwatch.Start(); - var result = await _db.Fastest().SplitTable().BulkCopyAsync(data).ConfigureAwait(false); - stopwatch.Stop(); + var getDeviceModel = CSharpScriptEngineExtension.Do(@this._driverPropertys.BigTextScriptHistoryTable); - //var result = await db.Insertable(dbInserts).SplitTable().ExecuteCommandAsync().ConfigureAwait(false); - if (result > 0) - { - LogMessage?.Trace($"TableName:{_driverPropertys.NumberTableName},Count:{result},watchTime: {stopwatch.ElapsedMilliseconds} ms"); - } + getDeviceModel.Logger = @this.LogMessage; + + await getDeviceModel.DBInsertable(@this._db, dbInserts, cancellationToken).ConfigureAwait(false); } - - if (stringData.Any()) + else { - Stopwatch stopwatch = new(); - stopwatch.Start(); - var data = stringData.AdaptEnumerableSQLHistoryValue(); - var result = await _db.Fastest().SplitTable().BulkCopyAsync(data).ConfigureAwait(false); - stopwatch.Stop(); + var stringData = dbInserts.Where(a => (!a.IsNumber && a.Value is not bool)); + var numberData = dbInserts.Where(a => (a.IsNumber || a.Value is bool)); - //var result = await db.Insertable(dbInserts).SplitTable().ExecuteCommandAsync().ConfigureAwait(false); - if (result > 0) + if (numberData.Any()) { - LogMessage?.Trace($"TableName:{_driverPropertys.StringTableName},Count:{result},watchTime: {stopwatch.ElapsedMilliseconds} ms"); + var data = numberData.AdaptEnumerableSQLNumberHistoryValue(); + Stopwatch stopwatch = new(); + stopwatch.Start(); + var result = await @this._db.Fastest().SplitTable().BulkCopyAsync(data).ConfigureAwait(false); + stopwatch.Stop(); + + //var result = await db.Insertable(dbInserts).SplitTable().ExecuteCommandAsync().ConfigureAwait(false); + if (result > 0) + { + @this.LogMessage?.Trace($"TableName:{@this._driverPropertys.NumberTableName},Count:{result},watchTime: {stopwatch.ElapsedMilliseconds} ms"); + } } - } - } - return OperResult.Success; - } - catch (Exception ex) - { - return new OperResult(ex); - } - } - - private async ValueTask UpdateAsync(List datas, CancellationToken cancellationToken) - { - try - { - _db.Ado.CancellationToken = cancellationToken; - - if (!_driverPropertys.BigTextScriptRealTable.IsNullOrEmpty()) - { - var getDeviceModel = CSharpScriptEngineExtension.Do(_driverPropertys.BigTextScriptRealTable); - getDeviceModel.Logger = LogMessage; - - await getDeviceModel.DBInsertable(_db, datas, cancellationToken).ConfigureAwait(false); - return OperResult.Success; - } - else - { - if (!_initRealData) - { - Stopwatch stopwatch = new(); - stopwatch.Start(); - var ids = (await _db.Queryable().AS(_driverPropertys.ReadDBTableName).Select(a => a.Id).ToListAsync(cancellationToken).ConfigureAwait(false)).ToHashSet(); - var InsertData = IdVariableRuntimes.Where(a => !ids.Contains(a.Key)).Select(a => a.Value).AdaptEnumerableSQLRealValue(); - var result = await _db.Fastest().AS(_driverPropertys.ReadDBTableName).BulkCopyAsync(InsertData).ConfigureAwait(false); - _initRealData = true; - stopwatch.Stop(); - if (result > 0) - { - LogMessage?.Trace($"RealTable Insert Data Count:{result},watchTime: {stopwatch.ElapsedMilliseconds} ms"); - } - } - { - if (datas?.Count > 0) + if (stringData.Any()) { Stopwatch stopwatch = new(); stopwatch.Start(); + var data = stringData.AdaptEnumerableSQLHistoryValue(); + var result = await @this._db.Fastest().SplitTable().BulkCopyAsync(data).ConfigureAwait(false); + stopwatch.Stop(); - var data = datas.AdaptEnumerableSQLRealValue(); - var result = await _db.Fastest().AS(_driverPropertys.ReadDBTableName).BulkUpdateAsync(data).ConfigureAwait(false); + //var result = await db.Insertable(dbInserts).SplitTable().ExecuteCommandAsync().ConfigureAwait(false); + if (result > 0) + { + @this.LogMessage?.Trace($"TableName:{@this._driverPropertys.StringTableName},Count:{result},watchTime: {stopwatch.ElapsedMilliseconds} ms"); + } + } + } + return OperResult.Success; + } + catch (Exception ex) + { + return new OperResult(ex); + } + } + } + + private ValueTask UpdateAsync(List datas, CancellationToken cancellationToken) + { + return UpdateAsync(this, datas, cancellationToken); + + static async PooledValueTask UpdateAsync(SqlDBProducer @this, List datas, CancellationToken cancellationToken) + { + try + { + @this._db.Ado.CancellationToken = cancellationToken; + + if (!@this._driverPropertys.BigTextScriptRealTable.IsNullOrEmpty()) + { + var getDeviceModel = CSharpScriptEngineExtension.Do(@this._driverPropertys.BigTextScriptRealTable); + getDeviceModel.Logger = @this.LogMessage; + + await getDeviceModel.DBInsertable(@this._db, datas, cancellationToken).ConfigureAwait(false); + return OperResult.Success; + } + else + { + if (!@this._initRealData) + { + Stopwatch stopwatch = new(); + stopwatch.Start(); + var ids = (await @this._db.Queryable().AS(@this._driverPropertys.ReadDBTableName).Select(a => a.Id).ToListAsync(cancellationToken).ConfigureAwait(false)).ToHashSet(); + var InsertData = @this.IdVariableRuntimes.Where(a => !ids.Contains(a.Key)).Select(a => a.Value).AdaptEnumerableSQLRealValue(); + var result = await @this._db.Fastest().AS(@this._driverPropertys.ReadDBTableName).BulkCopyAsync(InsertData).ConfigureAwait(false); + @this._initRealData = true; stopwatch.Stop(); if (result > 0) { - LogMessage?.Trace($"RealTable Data Count:{result},watchTime: {stopwatch.ElapsedMilliseconds} ms"); + @this.LogMessage?.Trace($"RealTable Insert Data Count:{result},watchTime: {stopwatch.ElapsedMilliseconds} ms"); + } + } + { + if (datas?.Count > 0) + { + Stopwatch stopwatch = new(); + stopwatch.Start(); + + var data = datas.AdaptEnumerableSQLRealValue(); + var result = await @this._db.Fastest().AS(@this._driverPropertys.ReadDBTableName).BulkUpdateAsync(data).ConfigureAwait(false); + + stopwatch.Stop(); + if (result > 0) + { + @this.LogMessage?.Trace($"RealTable Data Count:{result},watchTime: {stopwatch.ElapsedMilliseconds} ms"); + } + return OperResult.Success; } return OperResult.Success; } - return OperResult.Success; } } - } - catch (Exception ex) - { - return new OperResult(ex); + catch (Exception ex) + { + return new OperResult(ex); + } } } diff --git a/src/Plugin/ThingsGateway.Plugin.DB/TDengineDB/TDengineDBProducer.other.cs b/src/Plugin/ThingsGateway.Plugin.DB/TDengineDB/TDengineDBProducer.other.cs index d25fda7f0..f7468ac6a 100644 --- a/src/Plugin/ThingsGateway.Plugin.DB/TDengineDB/TDengineDBProducer.other.cs +++ b/src/Plugin/ThingsGateway.Plugin.DB/TDengineDB/TDengineDBProducer.other.cs @@ -8,6 +8,8 @@ // QQ群:605534569 //------------------------------------------------------------------------------ +using PooledAwait; + using System.Diagnostics; using System.Text; @@ -85,66 +87,80 @@ public partial class TDengineDBProducer : BusinessBaseWithCacheIntervalVariable AddQueueVarModel(new CacheDBItem(variable)); } } - private async ValueTask UpdateVarModel(IEnumerable item, CancellationToken cancellationToken) + private ValueTask UpdateVarModel(IEnumerable item, CancellationToken cancellationToken) { - var result = await InserableAsync(item.WhereIf(_driverPropertys.OnlineFilter, a => a.IsOnline == true).ToList(), cancellationToken).ConfigureAwait(false); - if (success != result.IsSuccess) - { - if (!result.IsSuccess) - LogMessage?.LogWarning(result.ToString()); - success = result.IsSuccess; - } + return UpdateVarModel(this, item, cancellationToken); - return result; + static async PooledValueTask UpdateVarModel(TDengineDBProducer @this, IEnumerable item, CancellationToken cancellationToken) + { + var result = await @this.InserableAsync(item.WhereIf(@this._driverPropertys.OnlineFilter, a => a.IsOnline == true).ToList(), cancellationToken).ConfigureAwait(false); + if (@this.success != result.IsSuccess) + { + if (!result.IsSuccess) + @this.LogMessage?.LogWarning(result.ToString()); + @this.success = result.IsSuccess; + } + + return result; + } } #region 方法 - private async ValueTask InserableAsync(List dbInserts, CancellationToken cancellationToken) + private ValueTask InserableAsync(List dbInserts, CancellationToken cancellationToken) { - try + return InserableAsync(this, dbInserts, cancellationToken); + + static async PooledValueTask InserableAsync(TDengineDBProducer @this, List dbInserts, CancellationToken cancellationToken) { - _db.Ado.CancellationToken = cancellationToken; - - if (!_driverPropertys.BigTextScriptHistoryTable.IsNullOrEmpty()) + try { - var getDeviceModel = CSharpScriptEngineExtension.Do(_driverPropertys.BigTextScriptHistoryTable); - getDeviceModel.Logger = LogMessage; - await getDeviceModel.DBInsertable(_db, dbInserts, cancellationToken).ConfigureAwait(false); + @this._db.Ado.CancellationToken = cancellationToken; + + if (!@this._driverPropertys.BigTextScriptHistoryTable.IsNullOrEmpty()) + { + var getDeviceModel = CSharpScriptEngineExtension.Do(@this._driverPropertys.BigTextScriptHistoryTable); + getDeviceModel.Logger = @this.LogMessage; + await getDeviceModel.DBInsertable(@this._db, dbInserts, cancellationToken).ConfigureAwait(false); + } + else + { + var stringData = dbInserts.Where(a => (!a.IsNumber && a.Value is not bool)); + var numberData = dbInserts.Where(a => (a.IsNumber || a.Value is bool)); + + await @this.InserableAsync(numberData, @this._driverPropertys.NumberTableNameLow, cancellationToken).ConfigureAwait(false); + + await @this.InserableAsync(stringData, @this._driverPropertys.StringTableNameLow, cancellationToken).ConfigureAwait(false); + } + return OperResult.Success; } - else + catch (Exception ex) { - var stringData = dbInserts.Where(a => (!a.IsNumber && a.Value is not bool)); - var numberData = dbInserts.Where(a => (a.IsNumber || a.Value is bool)); - - await InserableAsync(numberData, _driverPropertys.NumberTableNameLow, cancellationToken).ConfigureAwait(false); - - await InserableAsync(stringData, _driverPropertys.StringTableNameLow, cancellationToken).ConfigureAwait(false); + return new OperResult(ex); } - return OperResult.Success; - } - catch (Exception ex) - { - return new OperResult(ex); } } - private async Task InserableAsync(IEnumerable dbInserts, string tableName, CancellationToken cancellationToken) + private Task InserableAsync(IEnumerable dbInserts, string tableName, CancellationToken cancellationToken) { + return InserableAsync(this, dbInserts, tableName, cancellationToken); - Stopwatch stopwatch = new(); - stopwatch.Start(); - StringBuilder stringBuilder = new(); - stringBuilder.Append($"INSERT INTO"); - bool any = false; - //(`id`,`createtime`,`collecttime`,`isonline`,`value`) - foreach (var deviceGroup in dbInserts.GroupBy(a => a.DeviceName)) + static async PooledTask InserableAsync(TDengineDBProducer @this, IEnumerable dbInserts, string tableName, CancellationToken cancellationToken) { - foreach (var variableGroup in deviceGroup.GroupBy(a => a.Name)) + Stopwatch stopwatch = new(); + stopwatch.Start(); + + StringBuilder stringBuilder = new(); + stringBuilder.Append($"INSERT INTO"); + bool any = false; + //(`id`,`createtime`,`collecttime`,`isonline`,`value`) + foreach (var deviceGroup in dbInserts.GroupBy(a => a.DeviceName)) { - any = true; - stringBuilder.Append($""" + foreach (var variableGroup in deviceGroup.GroupBy(a => a.Name)) + { + any = true; + stringBuilder.Append($""" `{tableName}_{deviceGroup.Key}_{variableGroup.Key}` USING `{tableName}` TAGS ("{deviceGroup.Key}", "{variableGroup.Key}") @@ -152,25 +168,28 @@ public partial class TDengineDBProducer : BusinessBaseWithCacheIntervalVariable """); - foreach (var item in variableGroup) - { - stringBuilder.Append($"""(NOW,"{item.CollectTime.ToString("yyyy-MM-dd HH:mm:ss.fff")}",{item.Id},{item.IsOnline},"{JsonElementExtensions.GetValue(item.Value, true)}"),"""); + foreach (var item in variableGroup) + { + stringBuilder.Append($"""(NOW,"{item.CollectTime.ToString("yyyy-MM-dd HH:mm:ss.fff")}",{item.Id},{item.IsOnline},"{JsonElementExtensions.GetValue(item.Value, true)}"),"""); + } + stringBuilder.Remove(stringBuilder.Length - 1, 1); } - stringBuilder.Remove(stringBuilder.Length - 1, 1); } - } - if (!any) return; + if (!any) return; - stringBuilder.Append(';'); - stringBuilder.AppendLine(); + stringBuilder.Append(';'); + stringBuilder.AppendLine(); - var result = await _db.Ado.ExecuteCommandAsync(stringBuilder.ToString(), default, cancellationToken: cancellationToken).ConfigureAwait(false); + var result = await @this._db.Ado.ExecuteCommandAsync(stringBuilder.ToString(), default, cancellationToken: cancellationToken).ConfigureAwait(false); - stopwatch.Stop(); - //if (result > 0) - { - LogMessage?.Trace($"TableName:{tableName},Count:{result},watchTime: {stopwatch.ElapsedMilliseconds} ms"); + stopwatch.Stop(); + //if (result > 0) + { + @this.LogMessage?.Trace($"TableName:{tableName},Count:{result},watchTime: {stopwatch.ElapsedMilliseconds} ms"); + } + + return; } } #endregion 方法 diff --git a/src/Plugin/ThingsGateway.Plugin.Http/Webhook/Webhook.other.cs b/src/Plugin/ThingsGateway.Plugin.Http/Webhook/Webhook.other.cs index 15ad9fdf6..c70158088 100644 --- a/src/Plugin/ThingsGateway.Plugin.Http/Webhook/Webhook.other.cs +++ b/src/Plugin/ThingsGateway.Plugin.Http/Webhook/Webhook.other.cs @@ -10,6 +10,8 @@ using BootstrapBlazor.Components; +using PooledAwait; + using ThingsGateway.Extension.Generic; using ThingsGateway.Foundation; @@ -148,70 +150,80 @@ public partial class Webhook : BusinessBaseWithCacheIntervalScriptAll private readonly HttpClient client = new HttpClient(); - private async Task WebhookUpAsync(TopicArray topicArray, CancellationToken cancellationToken) + private Task WebhookUpAsync(TopicArray topicArray, CancellationToken cancellationToken) { - // 设置请求内容 - //var content = new StringContent(json, Encoding.UTF8, "application/json"); - using var content = new ByteArrayContent(topicArray.Payload); - content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); + return WebhookUpAsync(this, topicArray, cancellationToken); - try + static async PooledTask WebhookUpAsync(Webhook @this, TopicArray topicArray, CancellationToken cancellationToken) { - // 发送POST请求 - HttpResponseMessage response = await client.PostAsync(topicArray.Topic, content, cancellationToken).ConfigureAwait(false); + // 设置请求内容 + //var content = new StringContent(json, Encoding.UTF8, "application/json"); + var content = new ByteArrayContent(topicArray.Payload); + content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - // 检查响应状态 - if (response.IsSuccessStatusCode) + try { - if (_driverPropertys.DetailLog) + // 发送POST请求 + HttpResponseMessage response = await @this.client.PostAsync(topicArray.Topic, content, cancellationToken).ConfigureAwait(false); + + // 检查响应状态 + if (response.IsSuccessStatusCode) { - if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) - LogMessage?.LogTrace(GetDetailLogString(topicArray, _memoryVarModelQueue.Count)); - else if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) - LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count)); + if (@this._driverPropertys.DetailLog) + { + if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) + @this.LogMessage?.LogTrace(@this.GetDetailLogString(topicArray, @this._memoryVarModelQueue.Count)); + else if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) + @this.LogMessage?.LogDebug(@this.GetCountLogString(topicArray, @this._memoryVarModelQueue.Count)); + } + else + { + if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) + @this.LogMessage?.LogDebug(@this.GetCountLogString(topicArray, @this._memoryVarModelQueue.Count)); + } + return new(); } else { - if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) - LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count)); + return new($"Failed to trigger webhook. Status code: {response.StatusCode}"); } - return new(); } - else + catch (Exception ex) { - return new($"Failed to trigger webhook. Status code: {response.StatusCode}"); + return new(ex); } } - catch (Exception ex) - { - return new(ex); - } } #region private - private async ValueTask Update(IEnumerable topicArrayList, CancellationToken cancellationToken) + private ValueTask Update(IEnumerable topicArrayList, CancellationToken cancellationToken) { - foreach (var topicArray in topicArrayList) - { - var result = await WebhookUpAsync(topicArray, cancellationToken).ConfigureAwait(false); + return Update(this, topicArrayList, cancellationToken); - if (cancellationToken.IsCancellationRequested) - return result; - if (success != result.IsSuccess) + static async PooledValueTask Update(Webhook @this, IEnumerable topicArrayList, CancellationToken cancellationToken) + { + foreach (var topicArray in topicArrayList) { + var result = await @this.WebhookUpAsync(topicArray, cancellationToken).ConfigureAwait(false); + + if (cancellationToken.IsCancellationRequested) + return result; + if (@this.success != result.IsSuccess) + { + if (!result.IsSuccess) + { + @this.LogMessage?.LogWarning(result.ToString()); + } + @this.success = result.IsSuccess; + } if (!result.IsSuccess) { - LogMessage?.LogWarning(result.ToString()); + return result; } - success = result.IsSuccess; - } - if (!result.IsSuccess) - { - return result; } + return OperResult.Success; } - return OperResult.Success; } diff --git a/src/Plugin/ThingsGateway.Plugin.Kafka/Kafka/KafkaProducer.other.cs b/src/Plugin/ThingsGateway.Plugin.Kafka/Kafka/KafkaProducer.other.cs index e988051fb..1fde5bfc3 100644 --- a/src/Plugin/ThingsGateway.Plugin.Kafka/Kafka/KafkaProducer.other.cs +++ b/src/Plugin/ThingsGateway.Plugin.Kafka/Kafka/KafkaProducer.other.cs @@ -10,6 +10,8 @@ using Confluent.Kafka; +using PooledAwait; + using ThingsGateway.Extension.Generic; using ThingsGateway.Foundation; using ThingsGateway.Foundation.Extension.Generic; @@ -145,117 +147,128 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScriptAll #region private - private async ValueTask Update(IEnumerable topicArrayList, CancellationToken cancellationToken) + private ValueTask Update(IEnumerable topicArrayList, CancellationToken cancellationToken) { - foreach (var topicArray in topicArrayList) + return Update(this, topicArrayList, cancellationToken); + + static async PooledValueTask Update(KafkaProducer @this, IEnumerable topicArrayList, CancellationToken cancellationToken) { - var result = await KafKaUpAsync(topicArray, cancellationToken).ConfigureAwait(false); - if (success != result.IsSuccess) + foreach (var topicArray in topicArrayList) { + var result = await @this.KafKaUpAsync(topicArray, cancellationToken).ConfigureAwait(false); + if (@this.success != result.IsSuccess) + { + if (!result.IsSuccess) + { + @this.LogMessage?.LogWarning(result.ToString()); + } + @this.success = result.IsSuccess; + } if (!result.IsSuccess) { - LogMessage?.LogWarning(result.ToString()); + return result; } - success = result.IsSuccess; - } - if (!result.IsSuccess) - { - return result; } + return OperResult.Success; } - return OperResult.Success; } - private async ValueTask UpdateAlarmModel(IEnumerable item, CancellationToken cancellationToken) + private ValueTask UpdateAlarmModel(IEnumerable item, CancellationToken cancellationToken) { var topicArrayList = GetAlarmTopicArrays(item); - return await Update(topicArrayList, cancellationToken).ConfigureAwait(false); + return Update(topicArrayList, cancellationToken); } - private async ValueTask UpdateDevModel(IEnumerable item, CancellationToken cancellationToken) + private ValueTask UpdateDevModel(IEnumerable item, CancellationToken cancellationToken) { var topicArrayList = GetDeviceTopicArray(item); - return await Update(topicArrayList, cancellationToken).ConfigureAwait(false); + return Update(topicArrayList, cancellationToken); } - private async ValueTask UpdateVarModel(IEnumerable item, CancellationToken cancellationToken) + private ValueTask UpdateVarModel(IEnumerable item, CancellationToken cancellationToken) { var topicArrayList = GetVariableBasicDataTopicArray(item.WhereIf(_driverPropertys.OnlineFilter, a => a.IsOnline == true)); - return await Update(topicArrayList, cancellationToken).ConfigureAwait(false); + return Update(topicArrayList, cancellationToken); } #endregion private #region 方法 + private async Task AllPublishAsync(CancellationToken cancellationToken) { //保留消息 //分解List,避免超出字节大小限制 - var varData = IdVariableRuntimes.Select(a => a.Value).AdaptIEnumerableVariableBasicData().ChunkBetter(_driverPropertys.SplitSize); - var devData = CollectDevices?.Select(a => a.Value).AdaptIEnumerableDeviceBasicData().ChunkBetter(_driverPropertys.SplitSize); - var alramData = GlobalData.ReadOnlyRealAlarmIdVariables.Select(a => a.Value).ChunkBetter(_driverPropertys.SplitSize); + var varData = this.IdVariableRuntimes.Select(a => a.Value).AdaptIEnumerableVariableBasicData().ChunkBetter(this._driverPropertys.SplitSize); + var devData = this.CollectDevices?.Select(a => a.Value).AdaptIEnumerableDeviceBasicData().ChunkBetter(this._driverPropertys.SplitSize); + var alramData = GlobalData.ReadOnlyRealAlarmIdVariables.Select(a => a.Value).ChunkBetter(this._driverPropertys.SplitSize); foreach (var item in varData) { - if (!success) + if (!this.success) break; - await UpdateVarModel(item, cancellationToken).ConfigureAwait(false); + await this.UpdateVarModel(item, cancellationToken).ConfigureAwait(false); } if (devData != null) { foreach (var item in devData) { - if (!success) + if (!this.success) break; - await UpdateDevModel(item, cancellationToken).ConfigureAwait(false); + await this.UpdateDevModel(item, cancellationToken).ConfigureAwait(false); } } foreach (var item in alramData) { - if (!success) + if (!this.success) break; - await UpdateAlarmModel(item, cancellationToken).ConfigureAwait(false); + await this.UpdateAlarmModel(item, cancellationToken).ConfigureAwait(false); } } /// /// kafka上传,返回上传结果 /// - public async ValueTask KafKaUpAsync(TopicArray topicArray, CancellationToken cancellationToken) + public ValueTask KafKaUpAsync(TopicArray topicArray, CancellationToken cancellationToken) { - try + return KafKaUpAsync(this, topicArray, cancellationToken); + + static async PooledValueTask KafKaUpAsync(KafkaProducer @this, TopicArray topicArray, CancellationToken cancellationToken) { - using CancellationTokenSource cancellationTokenSource = new(_driverPropertys.Timeout); - using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, cancellationToken); - var result = await _producer.ProduceAsync(topicArray.Topic, new Message { Value = topicArray.Payload }, stoppingToken.Token).ConfigureAwait(false); - if (result.Status != PersistenceStatus.Persisted) + try { - return new OperResult("Upload fail"); - } - else - { - if (_driverPropertys.DetailLog) + using CancellationTokenSource cancellationTokenSource = new(@this._driverPropertys.Timeout); + using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, cancellationToken); + var result = await @this._producer.ProduceAsync(topicArray.Topic, new Message { Value = topicArray.Payload }, stoppingToken.Token).ConfigureAwait(false); + if (result.Status != PersistenceStatus.Persisted) { - if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) - LogMessage?.LogTrace(GetDetailLogString(topicArray, _memoryVarModelQueue.Count)); - else if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) - LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count)); + return new OperResult("Upload fail"); } else { - if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) - LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count)); + if (@this._driverPropertys.DetailLog) + { + if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) + @this.LogMessage?.LogTrace(@this.GetDetailLogString(topicArray, @this._memoryVarModelQueue.Count)); + else if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) + @this.LogMessage?.LogDebug(@this.GetCountLogString(topicArray, @this._memoryVarModelQueue.Count)); + } + else + { + if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) + @this.LogMessage?.LogDebug(@this.GetCountLogString(topicArray, @this._memoryVarModelQueue.Count)); + } + return OperResult.Success; } - return OperResult.Success; } - } - catch (OperationCanceledException) - { - return new OperResult("Timeout"); - } - catch (Exception ex) - { - return new OperResult(ex); + catch (OperationCanceledException) + { + return new OperResult("Timeout"); + } + catch (Exception ex) + { + return new OperResult(ex); + } } } diff --git a/src/Plugin/ThingsGateway.Plugin.Modbus/ModbusSlave/ModbusSlave.cs b/src/Plugin/ThingsGateway.Plugin.Modbus/ModbusSlave/ModbusSlave.cs index 81c141c20..2dfb0c11e 100644 --- a/src/Plugin/ThingsGateway.Plugin.Modbus/ModbusSlave/ModbusSlave.cs +++ b/src/Plugin/ThingsGateway.Plugin.Modbus/ModbusSlave/ModbusSlave.cs @@ -10,6 +10,8 @@ using Microsoft.Extensions.Localization; +using PooledAwait; + using System.Collections.Concurrent; using ThingsGateway.Foundation.Modbus; @@ -151,95 +153,108 @@ public class ModbusSlave : BusinessBase await base.DisposeAsync(disposing).ConfigureAwait(false); } - protected override async Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) + protected override Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) { - //获取设备连接状态 - if (!IsConnected()) + return ProtectedExecuteAsync(this, cancellationToken); + + + static async PooledTask ProtectedExecuteAsync(ModbusSlave @this, CancellationToken cancellationToken) { - try + //获取设备连接状态 + if (!@this.IsConnected()) + { + try + { + if (cancellationToken.IsCancellationRequested) + return; + await @this._plc.ConnectAsync(cancellationToken).ConfigureAwait(false); + @this.success = true; + } + catch (ObjectDisposedException) { } + catch (Exception ex) + { + if (@this.success) + @this.LogMessage?.LogWarning(ex, "Failed to start service"); + @this.success = false; + await Task.Delay(10000, cancellationToken).ConfigureAwait(false); + } + } + var list = @this.ModbusVariableQueue.ToDictWithDequeue(); + foreach (var item in list) { if (cancellationToken.IsCancellationRequested) - return; - await _plc.ConnectAsync(cancellationToken).ConfigureAwait(false); - success = true; - } - catch (ObjectDisposedException) { } - catch (Exception ex) - { - if (success) - LogMessage?.LogWarning(ex, "Failed to start service"); - success = false; - await Task.Delay(10000, cancellationToken).ConfigureAwait(false); - } - } - var list = ModbusVariableQueue.ToDictWithDequeue(); - foreach (var item in list) - { - if (cancellationToken.IsCancellationRequested) - break; - if (!IdVariableRuntimes.TryGetValue(item.Value, out var variableRuntime)) - break; + break; + if (!@this.IdVariableRuntimes.TryGetValue(item.Value, out var variableRuntime)) + break; - var type = variableRuntime.GetPropertyValue(CurrentDevice.Id, nameof(ModbusSlaveVariableProperty.DataType)); - if (Enum.TryParse(type, out DataTypeEnum result)) - { - await _plc.WriteJTokenAsync(item.Key, (variableRuntime.Value).GetJTokenFromObj(), result, cancellationToken).ConfigureAwait(false); - } - else - { - await _plc.WriteJTokenAsync(item.Key, (variableRuntime.Value).GetJTokenFromObj(), variableRuntime.DataType, cancellationToken).ConfigureAwait(false); + var type = variableRuntime.GetPropertyValue(@this.CurrentDevice.Id, nameof(ModbusSlaveVariableProperty.DataType)); + if (Enum.TryParse(type, out DataTypeEnum result)) + { + await @this._plc.WriteJTokenAsync(item.Key, (variableRuntime.Value).GetJTokenFromObj(), result, cancellationToken).ConfigureAwait(false); + } + else + { + await @this._plc.WriteJTokenAsync(item.Key, (variableRuntime.Value).GetJTokenFromObj(), variableRuntime.DataType, cancellationToken).ConfigureAwait(false); + } } + + return; } } /// /// RPC写入 /// - private async ValueTask OnWriteData(ModbusRequest modbusRequest, IThingsGatewayBitConverter bitConverter, IChannel channel) + private ValueTask OnWriteData(ModbusRequest modbusRequest, IThingsGatewayBitConverter bitConverter, IChannel channel) { - try + return OnWriteData(this, modbusRequest, bitConverter, channel); + + static async PooledValueTask OnWriteData(ModbusSlave @this, ModbusRequest modbusRequest, IThingsGatewayBitConverter bitConverter, IChannel channel) { - var tag = ModbusVariables.Where(a => a.Key?.StartAddress == modbusRequest.StartAddress && a.Key?.Station == modbusRequest.Station && a.Key?.FunctionCode == modbusRequest.FunctionCode).ToArray(); - if (tag.Length == 0) return OperResult.Success; - - - foreach (var item in tag) + try { - if (!(item.Value.GetPropertyValue(DeviceId, nameof(_variablePropertys.VariableRpcEnable)).ToBoolean(false) && _driverPropertys.DeviceRpcEnable)) - return new OperResult("Not Permitted to Write"); + var tag = @this.ModbusVariables.Where(a => a.Key?.StartAddress == modbusRequest.StartAddress && a.Key?.Station == modbusRequest.Station && a.Key?.FunctionCode == modbusRequest.FunctionCode).ToArray(); + if (tag.Length == 0) return OperResult.Success; - var type = item.Value.GetPropertyValue(DeviceId, nameof(ModbusSlaveVariableProperty.DataType)); - var dType = Enum.TryParse(type, out DataTypeEnum dataType) ? dataType : item.Value.DataType; - var addressStr = item.Value.GetPropertyValue(DeviceId, nameof(ModbusSlaveVariableProperty.ServiceAddress)); - var thingsGatewayBitConverter = bitConverter.GetTransByAddress(addressStr); - - var bitIndex = _plc.GetBitOffset(addressStr); - if (modbusRequest.FunctionCode == 0x03 && dType == DataTypeEnum.Boolean && bitIndex != null) + foreach (var item in tag) { - var int16Data = thingsGatewayBitConverter.ToUInt16(modbusRequest.MasterWriteDatas.Span, 0); - var wData = BitHelper.GetBit(int16Data, bitIndex.Value); + if (!(item.Value.GetPropertyValue(@this.DeviceId, nameof(_variablePropertys.VariableRpcEnable)).ToBoolean(false) && @this._driverPropertys.DeviceRpcEnable)) + return new OperResult("Not Permitted to Write"); - var result = await item.Value.RpcAsync(wData.ToSystemTextJsonString(), $"{nameof(ModbusSlave)}-{CurrentDevice.Name}-{$"{channel}"}").ConfigureAwait(false); + var type = item.Value.GetPropertyValue(@this.DeviceId, nameof(ModbusSlaveVariableProperty.DataType)); + var dType = Enum.TryParse(type, out DataTypeEnum dataType) ? dataType : item.Value.DataType; + var addressStr = item.Value.GetPropertyValue(@this.DeviceId, nameof(ModbusSlaveVariableProperty.ServiceAddress)); - if (!result.IsSuccess) - return result; - } - else - { - _ = thingsGatewayBitConverter.GetChangedDataFormBytes(_plc, addressStr, modbusRequest.MasterWriteDatas.Span, 0, dType, item.Value.ArrayLength ?? 1, null, out var data); - - var result = await item.Value.RpcAsync(data.ToSystemTextJsonString(), $"{nameof(ModbusSlave)}-{CurrentDevice.Name}-{$"{channel}"}").ConfigureAwait(false); - - if (!result.IsSuccess) - return result; + var thingsGatewayBitConverter = bitConverter.GetTransByAddress(addressStr); + + var bitIndex = @this._plc.GetBitOffset(addressStr); + if (modbusRequest.FunctionCode == 0x03 && dType == DataTypeEnum.Boolean && bitIndex != null) + { + var int16Data = thingsGatewayBitConverter.ToUInt16(modbusRequest.MasterWriteDatas.Span, 0); + var wData = BitHelper.GetBit(int16Data, bitIndex.Value); + + var result = await item.Value.RpcAsync(wData.ToSystemTextJsonString(), $"{nameof(ModbusSlave)}-{@this.CurrentDevice.Name}-{$"{channel}"}").ConfigureAwait(false); + + if (!result.IsSuccess) + return result; + } + else + { + _ = thingsGatewayBitConverter.GetChangedDataFormBytes(@this._plc, addressStr, modbusRequest.MasterWriteDatas.Span, 0, dType, item.Value.ArrayLength ?? 1, null, out var data); + + var result = await item.Value.RpcAsync(data.ToSystemTextJsonString(), $"{nameof(ModbusSlave)}-{@this.CurrentDevice.Name}-{$"{channel}"}").ConfigureAwait(false); + + if (!result.IsSuccess) + return result; + } } + return OperResult.Success; + } + catch (Exception ex) + { + return new OperResult(ex); } - return OperResult.Success; - } - catch (Exception ex) - { - return new OperResult(ex); } } diff --git a/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttClient/MqttClient.cs b/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttClient/MqttClient.cs index 2f70b5bd3..6fb92227a 100644 --- a/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttClient/MqttClient.cs +++ b/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttClient/MqttClient.cs @@ -10,6 +10,9 @@ using MQTTnet; +using PooledAwait; + + @@ -202,31 +205,38 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScriptAll } } - protected override async Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) + protected override Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) { - var clientResult = await TryMqttClientAsync(cancellationToken).ConfigureAwait(false); - if (!clientResult.IsSuccess) + return ProtectedExecuteAsync(this, cancellationToken); + + + static async PooledTask ProtectedExecuteAsync(MqttClient @this, CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - return; - if (success != clientResult.IsSuccess) + var clientResult = await @this.TryMqttClientAsync(cancellationToken).ConfigureAwait(false); + if (!clientResult.IsSuccess) { - if (!clientResult.IsSuccess) - LogMessage?.LogWarning(clientResult.Exception, clientResult.ErrorMessage); - success = clientResult.IsSuccess; + if (cancellationToken.IsCancellationRequested) + return; + if (@this.success != clientResult.IsSuccess) + { + if (!clientResult.IsSuccess) + @this.LogMessage?.LogWarning(clientResult.Exception, clientResult.ErrorMessage); + @this.success = clientResult.IsSuccess; + } + await Task.Delay(10000, cancellationToken).ConfigureAwait(false); + //return; } - await Task.Delay(10000, cancellationToken).ConfigureAwait(false); - //return; - } - //TD设备上线 + //TD设备上线 - var data = ThingsBoardDeviceConnectQueue.ToListWithDequeue(); - foreach (var item in data) - { - await UpdateThingsBoardDeviceConnect(item).ConfigureAwait(false); - } + var data = @this.ThingsBoardDeviceConnectQueue.ToListWithDequeue(); + foreach (var item in data) + { + await @this.UpdateThingsBoardDeviceConnect(item).ConfigureAwait(false); + } - await Update(cancellationToken).ConfigureAwait(false); + await @this.Update(cancellationToken).ConfigureAwait(false); + return; + } } diff --git a/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttClient/MqttClient.other.cs b/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttClient/MqttClient.other.cs index 07851af12..dbf24362c 100644 --- a/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttClient/MqttClient.other.cs +++ b/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttClient/MqttClient.other.cs @@ -20,6 +20,8 @@ using MQTTnet.Client; using Newtonsoft.Json.Linq; +using PooledAwait; + using System.Collections.Concurrent; using System.Text; @@ -30,6 +32,7 @@ using ThingsGateway.NewLife; using ThingsGateway.NewLife.Extension; using ThingsGateway.NewLife.Json.Extension; + namespace ThingsGateway.Plugin.Mqtt; /// @@ -222,27 +225,32 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScriptAll #region private - private async ValueTask Update(IEnumerable topicArrayList, CancellationToken cancellationToken) + private ValueTask Update(IEnumerable topicArrayList, CancellationToken cancellationToken) { - foreach (TopicArray topicArray in topicArrayList) + return Update(this, topicArrayList, cancellationToken); + + static async PooledValueTask Update(MqttClient @this, IEnumerable topicArrayList, CancellationToken cancellationToken) { - var result = await MqttUpAsync(topicArray, cancellationToken).ConfigureAwait(false); - if (cancellationToken.IsCancellationRequested) - return result; - if (success != result.IsSuccess) + foreach (TopicArray topicArray in topicArrayList) { + var result = await @this.MqttUpAsync(topicArray, cancellationToken).ConfigureAwait(false); + if (cancellationToken.IsCancellationRequested) + return result; + if (@this.success != result.IsSuccess) + { + if (!result.IsSuccess) + { + @this.LogMessage?.LogWarning(result.ToString()); + } + @this.success = result.IsSuccess; + } if (!result.IsSuccess) { - LogMessage?.LogWarning(result.ToString()); + return result; } - success = result.IsSuccess; - } - if (!result.IsSuccess) - { - return result; } + return OperResult.Success; } - return OperResult.Success; } private ValueTask UpdateAlarmModel(IEnumerable item, CancellationToken cancellationToken) @@ -265,96 +273,102 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScriptAll #endregion private - private async ValueTask AllPublishAsync(CancellationToken cancellationToken) + private async Task AllPublishAsync(CancellationToken cancellationToken) { + //保留消息 //分解List,避免超出mqtt字节大小限制 - var varData = IdVariableRuntimes.Select(a => a.Value).AdaptIEnumerableVariableBasicData().ChunkBetter(_driverPropertys.SplitSize); - var devData = CollectDevices?.Select(a => a.Value).AdaptIEnumerableDeviceBasicData().ChunkBetter(_driverPropertys.SplitSize); - var alramData = GlobalData.ReadOnlyRealAlarmIdVariables.Select(a => a.Value).ChunkBetter(_driverPropertys.SplitSize); + var varData = this.IdVariableRuntimes.Select(a => a.Value).AdaptIEnumerableVariableBasicData().ChunkBetter(this._driverPropertys.SplitSize); + var devData = this.CollectDevices?.Select(a => a.Value).AdaptIEnumerableDeviceBasicData().ChunkBetter(this._driverPropertys.SplitSize); + var alramData = GlobalData.ReadOnlyRealAlarmIdVariables.Select(a => a.Value).ChunkBetter(this._driverPropertys.SplitSize); foreach (var item in varData) { - if (!success) + if (!this.success) break; - await UpdateVarModel(item, cancellationToken).ConfigureAwait(false); + await this.UpdateVarModel(item, cancellationToken).ConfigureAwait(false); } if (devData != null) { foreach (var item in devData) { - if (!success) + if (!this.success) break; - await UpdateDevModel(item, cancellationToken).ConfigureAwait(false); + await this.UpdateDevModel(item, cancellationToken).ConfigureAwait(false); } } foreach (var item in alramData) { - if (!success) + if (!this.success) break; - await UpdateAlarmModel(item, cancellationToken).ConfigureAwait(false); + await this.UpdateAlarmModel(item, cancellationToken).ConfigureAwait(false); } } - private async ValueTask>> GetRpcResult(string clientId, Dictionary> rpcDatas) + private ValueTask>> GetRpcResult(string clientId, Dictionary> rpcDatas) { - var mqttRpcResult = new Dictionary>(); - rpcDatas.ForEach(a => mqttRpcResult.Add(a.Key, new())); - try + return GetRpcResult(this, clientId, rpcDatas); + + static async PooledValueTask>> GetRpcResult(MqttClient @this, string clientId, Dictionary> rpcDatas) { - foreach (var rpcData in rpcDatas) + var mqttRpcResult = new Dictionary>(); + rpcDatas.ForEach(a => mqttRpcResult.Add(a.Key, new())); + try { - if (GlobalData.ReadOnlyDevices.TryGetValue(rpcData.Key, out var device)) + foreach (var rpcData in rpcDatas) { - foreach (var item in rpcData.Value) + if (GlobalData.ReadOnlyDevices.TryGetValue(rpcData.Key, out var device)) { - if (device.ReadOnlyVariableRuntimes.TryGetValue(item.Key, out var variable) && IdVariableRuntimes.TryGetValue(variable.Id, out var tag)) + foreach (var item in rpcData.Value) { - var rpcEnable = tag.GetPropertyValue(DeviceId, nameof(_variablePropertys.VariableRpcEnable))?.ToBoolean(); - if (rpcEnable == false) + if (device.ReadOnlyVariableRuntimes.TryGetValue(item.Key, out var variable) && @this.IdVariableRuntimes.TryGetValue(variable.Id, out var tag)) { - mqttRpcResult[rpcData.Key].Add(item.Key, new OperResult("RPCEnable is False")); + var rpcEnable = tag.GetPropertyValue(@this.DeviceId, nameof(_variablePropertys.VariableRpcEnable))?.ToBoolean(); + if (rpcEnable == false) + { + mqttRpcResult[rpcData.Key].Add(item.Key, new OperResult("RPCEnable is False")); + } + } + else + { + mqttRpcResult[rpcData.Key].Add(item.Key, new OperResult("The variable does not exist")); } } - else + } + } + + Dictionary> writeData = new(); + foreach (var item in rpcDatas) + { + writeData.Add(item.Key, new()); + + foreach (var kv in item.Value) + { + if (!mqttRpcResult[item.Key].ContainsKey(kv.Key)) { - mqttRpcResult[rpcData.Key].Add(item.Key, new OperResult("The variable does not exist")); + writeData[item.Key].Add(kv.Key, kv.Value?.ToString()); } } } - } - Dictionary> writeData = new(); - foreach (var item in rpcDatas) - { - writeData.Add(item.Key, new()); + var result = await GlobalData.RpcService.InvokeDeviceMethodAsync(@this.ToString() + "-" + clientId, + writeData).ConfigureAwait(false); - foreach (var kv in item.Value) + foreach (var dictKv in result) { - if (!mqttRpcResult[item.Key].ContainsKey(kv.Key)) + foreach (var item in dictKv.Value) { - writeData[item.Key].Add(kv.Key, kv.Value?.ToString()); + mqttRpcResult[dictKv.Key].TryAdd(item.Key, item.Value); } } } - - var result = await GlobalData.RpcService.InvokeDeviceMethodAsync(ToString() + "-" + clientId, - writeData).ConfigureAwait(false); - - foreach (var dictKv in result) + catch (Exception ex) { - foreach (var item in dictKv.Value) - { - mqttRpcResult[dictKv.Key].TryAdd(item.Key, item.Value); - } + @this.LogMessage?.LogWarning(ex); } - } - catch (Exception ex) - { - LogMessage?.LogWarning(ex); - } - return mqttRpcResult; + return mqttRpcResult; + } } private async Task MqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs args) @@ -482,77 +496,82 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScriptAll /// /// 上传mqtt,返回上传结果 /// - public async ValueTask MqttUpAsync(TopicArray topicArray, CancellationToken cancellationToken = default) + public ValueTask MqttUpAsync(TopicArray topicArray, CancellationToken cancellationToken = default) { - try + return MqttUpAsync(this, topicArray, cancellationToken); + + static async PooledValueTask MqttUpAsync(MqttClient @this, TopicArray topicArray, CancellationToken cancellationToken) { - var isConnect = await TryMqttClientAsync(cancellationToken).ConfigureAwait(false); - if (isConnect.IsSuccess) + try { - var variableMessage = new MqttApplicationMessageBuilder() - .WithTopic(topicArray.Topic).WithQualityOfServiceLevel(_driverPropertys.MqttQualityOfServiceLevel).WithRetainFlag() - .WithPayload(topicArray.Payload).Build(); - var result = await _mqttClient.PublishAsync(variableMessage, cancellationToken).ConfigureAwait(false); - if (result.IsSuccess) + var isConnect = await @this.TryMqttClientAsync(cancellationToken).ConfigureAwait(false); + if (isConnect.IsSuccess) { - if (_driverPropertys.DetailLog) + var variableMessage = new MqttApplicationMessageBuilder() + .WithTopic(topicArray.Topic).WithQualityOfServiceLevel(@this._driverPropertys.MqttQualityOfServiceLevel).WithRetainFlag() + .WithPayload(topicArray.Payload).Build(); + var result = await @this._mqttClient.PublishAsync(variableMessage, cancellationToken).ConfigureAwait(false); + if (result.IsSuccess) { - if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) - LogMessage?.LogTrace(GetDetailLogString(topicArray, _memoryVarModelQueue.Count)); - else if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) - LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count)); + if (@this._driverPropertys.DetailLog) + { + if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) + @this.LogMessage?.LogTrace(@this.GetDetailLogString(topicArray, @this._memoryVarModelQueue.Count)); + else if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) + @this.LogMessage?.LogDebug(@this.GetCountLogString(topicArray, @this._memoryVarModelQueue.Count)); + } + else + { + if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) + @this.LogMessage?.LogDebug(@this.GetCountLogString(topicArray, @this._memoryVarModelQueue.Count)); + } + return OperResult.Success; } else { - if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) - LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count)); + return new OperResult($"Upload fail{result.ReasonString}"); } - return OperResult.Success; } else { - return new OperResult($"Upload fail{result.ReasonString}"); + return isConnect; } } - else + catch (Exception ex) { - return isConnect; + return new OperResult($"Upload fail", ex); } } - catch (Exception ex) - { - return new OperResult($"Upload fail", ex); - } } - private async ValueTask TryMqttClientAsync(CancellationToken cancellationToken) + private ValueTask TryMqttClientAsync(CancellationToken cancellationToken) { - if (DisposedValue || _mqttClient == null) return new OperResult("MqttClient is disposed"); + if (DisposedValue || _mqttClient == null) return TouchSocket.Core.EasyValueTask.FromResult(new OperResult("MqttClient is disposed")); if (_mqttClient?.IsConnected == true) - return OperResult.Success; - return await Client().ConfigureAwait(false); + return TouchSocket.Core.EasyValueTask.FromResult(OperResult.Success); + return Client(this, cancellationToken); - async ValueTask Client() + static async PooledValueTask Client(MqttClient @this, CancellationToken cancellationToken) { - if (_mqttClient?.IsConnected == true) + if (@this._mqttClient?.IsConnected == true) return OperResult.Success; try { - await ConnectLock.WaitAsync(cancellationToken).ConfigureAwait(false); + await @this.ConnectLock.WaitAsync(cancellationToken).ConfigureAwait(false); await Task.Delay(100, cancellationToken).ConfigureAwait(false); - if (_mqttClient?.IsConnected == true) + if (@this._mqttClient?.IsConnected == true) return OperResult.Success; - using var timeoutToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(_driverPropertys.ConnectTimeout)); + using var timeoutToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(@this._driverPropertys.ConnectTimeout)); using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutToken.Token); - if (_mqttClient?.IsConnected == true) + if (@this._mqttClient?.IsConnected == true) return OperResult.Success; - if (_mqttClient == null) + if (@this._mqttClient == null) { return new OperResult("mqttClient is null"); } - var result = await _mqttClient.ConnectAsync(_mqttClientOptions, stoppingToken.Token).ConfigureAwait(false); - if (_mqttClient.IsConnected) + var result = await @this._mqttClient.ConnectAsync(@this._mqttClientOptions, stoppingToken.Token).ConfigureAwait(false); + if (@this._mqttClient.IsConnected) { return OperResult.Success; } @@ -570,7 +589,7 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScriptAll } finally { - ConnectLock.Release(); + @this.ConnectLock.Release(); } } } diff --git a/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttCollect/MqttCollect.cs b/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttCollect/MqttCollect.cs index f05215c48..a55808b42 100644 --- a/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttCollect/MqttCollect.cs +++ b/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttCollect/MqttCollect.cs @@ -10,6 +10,9 @@ using MQTTnet; +using PooledAwait; + + #if NET6_0 using MQTTnet.Client; #endif @@ -272,21 +275,29 @@ public partial class MqttCollect : CollectBase return list; } - private async Task CheckAsync(object? state, CancellationToken cancellationToken) + private Task CheckAsync(object? state, CancellationToken cancellationToken) { - var clientResult = await TryMqttClientAsync(cancellationToken).ConfigureAwait(false); - if (!clientResult.IsSuccess) + return CheckAsync(this, cancellationToken); + + + static async PooledTask CheckAsync(MqttCollect @this, CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - return; - if (success != clientResult.IsSuccess) + var clientResult = await @this.TryMqttClientAsync(cancellationToken).ConfigureAwait(false); + if (!clientResult.IsSuccess) { - if (!clientResult.IsSuccess) - LogMessage?.LogWarning(clientResult.Exception, clientResult.ErrorMessage); - success = clientResult.IsSuccess; + if (cancellationToken.IsCancellationRequested) + return; + if (@this.success != clientResult.IsSuccess) + { + if (!clientResult.IsSuccess) + @this.LogMessage?.LogWarning(clientResult.Exception, clientResult.ErrorMessage); + @this.success = clientResult.IsSuccess; + } + await Task.Delay(10000, cancellationToken).ConfigureAwait(false); + //return; } - await Task.Delay(10000, cancellationToken).ConfigureAwait(false); - //return; + + return; } } diff --git a/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttCollect/MqttCollect.other.cs b/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttCollect/MqttCollect.other.cs index c687028de..142e396bc 100644 --- a/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttCollect/MqttCollect.other.cs +++ b/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttCollect/MqttCollect.other.cs @@ -10,6 +10,9 @@ using MQTTnet; +using PooledAwait; + + #if NET6_0 using MQTTnet.Client; #endif @@ -129,32 +132,34 @@ public partial class MqttCollect : CollectBase } } - private async ValueTask TryMqttClientAsync(CancellationToken cancellationToken) + private ValueTask TryMqttClientAsync(CancellationToken cancellationToken) { - if (_mqttClient?.IsConnected == true) - return OperResult.Success; - return await Client().ConfigureAwait(false); + if (DisposedValue || _mqttClient == null) return TouchSocket.Core.EasyValueTask.FromResult(new OperResult("MqttClient is disposed")); - async ValueTask Client() + if (_mqttClient?.IsConnected == true) + return TouchSocket.Core.EasyValueTask.FromResult(OperResult.Success); + return Client(this, cancellationToken); + + static async PooledValueTask Client(MqttCollect @this, CancellationToken cancellationToken) { - if (_mqttClient?.IsConnected == true) + if (@this._mqttClient?.IsConnected == true) return OperResult.Success; try { - await ConnectLock.WaitAsync(cancellationToken).ConfigureAwait(false); + await @this.ConnectLock.WaitAsync(cancellationToken).ConfigureAwait(false); await Task.Delay(100, cancellationToken).ConfigureAwait(false); - if (_mqttClient?.IsConnected == true) + if (@this._mqttClient?.IsConnected == true) return OperResult.Success; - using var timeoutToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(_driverPropertys.ConnectTimeout)); + using var timeoutToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(@this._driverPropertys.ConnectTimeout)); using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutToken.Token); - if (_mqttClient?.IsConnected == true) + if (@this._mqttClient?.IsConnected == true) return OperResult.Success; - if (_mqttClient == null) + if (@this._mqttClient == null) { return new OperResult("mqttClient is null"); } - var result = await _mqttClient.ConnectAsync(_mqttClientOptions, stoppingToken.Token).ConfigureAwait(false); - if (_mqttClient.IsConnected) + var result = await @this._mqttClient.ConnectAsync(@this._mqttClientOptions, stoppingToken.Token).ConfigureAwait(false); + if (@this._mqttClient.IsConnected) { return OperResult.Success; } @@ -172,7 +177,7 @@ public partial class MqttCollect : CollectBase } finally { - ConnectLock.Release(); + @this.ConnectLock.Release(); } } } diff --git a/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttServer/MqttServer.other.cs b/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttServer/MqttServer.other.cs index 23d807c2f..dd77b2d7d 100644 --- a/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttServer/MqttServer.other.cs +++ b/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttServer/MqttServer.other.cs @@ -20,6 +20,8 @@ using MQTTnet.Server; using Newtonsoft.Json.Linq; +using PooledAwait; + using System.Text; using ThingsGateway.Admin.Application; @@ -165,25 +167,30 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScriptAll } #region private - private async ValueTask Update(IEnumerable topicArrayList, CancellationToken cancellationToken) + private ValueTask Update(IEnumerable topicArrayList, CancellationToken cancellationToken) { - foreach (var topicArray in topicArrayList) + return Update(this, topicArrayList, cancellationToken); + + static async PooledValueTask Update(MqttServer @this, IEnumerable topicArrayList, CancellationToken cancellationToken) { - var result = await MqttUpAsync(topicArray, cancellationToken).ConfigureAwait(false); - if (success != result.IsSuccess) + foreach (var topicArray in topicArrayList) { + var result = await @this.MqttUpAsync(topicArray, cancellationToken).ConfigureAwait(false); + if (@this.success != result.IsSuccess) + { + if (!result.IsSuccess) + { + @this.LogMessage?.LogWarning(result.ToString()); + } + @this.success = result.IsSuccess; + } if (!result.IsSuccess) { - LogMessage?.LogWarning(result.ToString()); + return result; } - success = result.IsSuccess; - } - if (!result.IsSuccess) - { - return result; } + return OperResult.Success; } - return OperResult.Success; } private ValueTask UpdateAlarmModel(IEnumerable item, CancellationToken cancellationToken) @@ -207,65 +214,70 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScriptAll #endregion private - private async ValueTask>> GetRpcResult(string clientId, Dictionary> rpcDatas) + private ValueTask>> GetRpcResult(string clientId, Dictionary> rpcDatas) { - var mqttRpcResult = new Dictionary>(); - rpcDatas.ForEach(a => mqttRpcResult.Add(a.Key, new())); - try + return GetRpcResult(this, clientId, rpcDatas); + + static async PooledValueTask>> GetRpcResult(MqttServer @this, string clientId, Dictionary> rpcDatas) { - foreach (var rpcData in rpcDatas) + var mqttRpcResult = new Dictionary>(); + rpcDatas.ForEach(a => mqttRpcResult.Add(a.Key, new())); + try { - if (GlobalData.ReadOnlyDevices.TryGetValue(rpcData.Key, out var device)) + foreach (var rpcData in rpcDatas) { - foreach (var item in rpcData.Value) + if (GlobalData.ReadOnlyDevices.TryGetValue(rpcData.Key, out var device)) { - if (device.ReadOnlyVariableRuntimes.TryGetValue(item.Key, out var variable) && IdVariableRuntimes.TryGetValue(variable.Id, out var tag)) + foreach (var item in rpcData.Value) { - var rpcEnable = tag.GetPropertyValue(DeviceId, nameof(_variablePropertys.VariableRpcEnable))?.ToBoolean(); - if (rpcEnable == false) + if (device.ReadOnlyVariableRuntimes.TryGetValue(item.Key, out var variable) && @this.IdVariableRuntimes.TryGetValue(variable.Id, out var tag)) { - mqttRpcResult[rpcData.Key].Add(item.Key, new OperResult("RPCEnable is False")); + var rpcEnable = tag.GetPropertyValue(@this.DeviceId, nameof(_variablePropertys.VariableRpcEnable))?.ToBoolean(); + if (rpcEnable == false) + { + mqttRpcResult[rpcData.Key].Add(item.Key, new OperResult("RPCEnable is False")); + } + } + else + { + mqttRpcResult[rpcData.Key].Add(item.Key, new OperResult("The variable does not exist")); } } - else + } + } + + Dictionary> writeData = new(); + foreach (var item in rpcDatas) + { + writeData.Add(item.Key, new()); + + foreach (var kv in item.Value) + { + if (!mqttRpcResult[item.Key].ContainsKey(kv.Key)) { - mqttRpcResult[rpcData.Key].Add(item.Key, new OperResult("The variable does not exist")); + writeData[item.Key].Add(kv.Key, kv.Value?.ToString()); } } } - } - Dictionary> writeData = new(); - foreach (var item in rpcDatas) - { - writeData.Add(item.Key, new()); + var result = await GlobalData.RpcService.InvokeDeviceMethodAsync(@this.ToString() + "-" + clientId, + writeData).ConfigureAwait(false); - foreach (var kv in item.Value) + foreach (var dictKv in result) { - if (!mqttRpcResult[item.Key].ContainsKey(kv.Key)) + foreach (var item in dictKv.Value) { - writeData[item.Key].Add(kv.Key, kv.Value?.ToString()); + mqttRpcResult[dictKv.Key].TryAdd(item.Key, item.Value); } } } - - var result = await GlobalData.RpcService.InvokeDeviceMethodAsync(ToString() + "-" + clientId, - writeData).ConfigureAwait(false); - - foreach (var dictKv in result) + catch (Exception ex) { - foreach (var item in dictKv.Value) - { - mqttRpcResult[dictKv.Key].TryAdd(item.Key, item.Value); - } + @this.LogMessage?.LogWarning(ex); } - } - catch (Exception ex) - { - LogMessage?.LogWarning(ex); - } - return mqttRpcResult; + return mqttRpcResult; + } } private List GetRetainedMessages() @@ -328,14 +340,19 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScriptAll return Task.CompletedTask; } - private async Task MqttServer_InterceptingPublishAsync(InterceptingPublishEventArgs args) + private Task MqttServer_InterceptingPublishAsync(InterceptingPublishEventArgs args) { - try + return MqttServer_InterceptingPublishAsync(this, args); + + + static async PooledTask MqttServer_InterceptingPublishAsync(MqttServer @this, InterceptingPublishEventArgs args) { + try + { #if NET8_0_OR_GREATER - var payload = args.ApplicationMessage.Payload; - var payloadCount = payload.Length; + var payload = args.ApplicationMessage.Payload; + var payloadCount = payload.Length; #else var payload = args.ApplicationMessage.PayloadSegment; @@ -343,53 +360,56 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScriptAll #endif - if (args.ApplicationMessage.Topic == _driverPropertys.RpcQuestTopic && payloadCount > 0) - { - var data = GetRetainedMessages(); - - foreach (var item in data) + if (args.ApplicationMessage.Topic == @this._driverPropertys.RpcQuestTopic && payloadCount > 0) { - await _mqttServer.InjectApplicationMessage(new InjectedMqttApplicationMessage(item)).ConfigureAwait(false); - } - return; - } + var data = @this.GetRetainedMessages(); - if (!_driverPropertys.DeviceRpcEnable || string.IsNullOrEmpty(args.ClientId)) - return; - - if (!_driverPropertys.BigTextScriptRpc.IsNullOrEmpty()) - { - var rpcBase = CSharpScriptEngineExtension.Do(_driverPropertys.BigTextScriptRpc); - - await rpcBase.RPCInvokeAsync(LogMessage, args, _driverPropertys, _mqttServer, GetRpcResult, CancellationToken.None).ConfigureAwait(false); - } - else - { - if (_driverPropertys.RpcWriteTopic.IsNullOrWhiteSpace()) return; - - var t = string.Format(null, RpcTopic, _driverPropertys.RpcWriteTopic); - if (MqttTopicFilterComparer.Compare(args.ApplicationMessage.Topic, t) != MqttTopicFilterCompareResult.IsMatch) + foreach (var item in data) + { + await @this._mqttServer.InjectApplicationMessage(new InjectedMqttApplicationMessage(item)).ConfigureAwait(false); + } return; - var rpcDatas = Encoding.UTF8.GetString(payload).FromJsonNetString>>(); - if (rpcDatas == null) - return; - var mqttRpcResult = await GetRpcResult(args.ClientId, rpcDatas).ConfigureAwait(false); - - try - { - var variableMessage = new MqttApplicationMessageBuilder() - .WithTopic($"{args.ApplicationMessage.Topic}/Response") - .WithPayload(mqttRpcResult.ToSystemTextJsonString(_driverPropertys.JsonFormattingIndented)).Build(); - await _mqttServer.InjectApplicationMessage(new InjectedMqttApplicationMessage(variableMessage)).ConfigureAwait(false); } - catch + + if (!@this._driverPropertys.DeviceRpcEnable || string.IsNullOrEmpty(args.ClientId)) + return; + + if (!@this._driverPropertys.BigTextScriptRpc.IsNullOrEmpty()) { + var rpcBase = CSharpScriptEngineExtension.Do(@this._driverPropertys.BigTextScriptRpc); + + await rpcBase.RPCInvokeAsync(@this.LogMessage, args, @this._driverPropertys, @this._mqttServer, @this.GetRpcResult, CancellationToken.None).ConfigureAwait(false); + } + else + { + if (@this._driverPropertys.RpcWriteTopic.IsNullOrWhiteSpace()) return; + + var t = string.Format(null, RpcTopic, @this._driverPropertys.RpcWriteTopic); + if (MqttTopicFilterComparer.Compare(args.ApplicationMessage.Topic, t) != MqttTopicFilterCompareResult.IsMatch) + return; + var rpcDatas = Encoding.UTF8.GetString(payload).FromJsonNetString>>(); + if (rpcDatas == null) + return; + var mqttRpcResult = await @this.GetRpcResult(args.ClientId, rpcDatas).ConfigureAwait(false); + + try + { + var variableMessage = new MqttApplicationMessageBuilder() + .WithTopic($"{args.ApplicationMessage.Topic}/Response") + .WithPayload(mqttRpcResult.ToSystemTextJsonString(@this._driverPropertys.JsonFormattingIndented)).Build(); + await @this._mqttServer.InjectApplicationMessage(new InjectedMqttApplicationMessage(variableMessage)).ConfigureAwait(false); + } + catch + { + } } } - } - catch (Exception ex) - { - LogMessage?.LogWarning(ex, $"MqttServer_InterceptingPublishAsync error"); + catch (Exception ex) + { + @this.LogMessage?.LogWarning(ex, $"MqttServer_InterceptingPublishAsync error"); + } + + return; } } @@ -432,33 +452,38 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScriptAll /// /// 上传mqtt,返回上传结果 /// - public async ValueTask MqttUpAsync(TopicArray topicArray, CancellationToken cancellationToken = default) + public ValueTask MqttUpAsync(TopicArray topicArray, CancellationToken cancellationToken = default) { - try - { - var message = new MqttApplicationMessageBuilder() -.WithTopic(topicArray.Topic).WithQualityOfServiceLevel(_driverPropertys.MqttQualityOfServiceLevel).WithRetainFlag() -.WithPayload(topicArray.Payload).Build(); - await _mqttServer.InjectApplicationMessage( - new InjectedMqttApplicationMessage(message), cancellationToken).ConfigureAwait(false); + return MqttUpAsync(this, topicArray, cancellationToken); - if (_driverPropertys.DetailLog) - { - if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) - LogMessage?.LogTrace(GetDetailLogString(topicArray, _memoryVarModelQueue.Count)); - else if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) - LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count)); - } - else - { - if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) - LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count)); - } - return OperResult.Success; - } - catch (Exception ex) + static async PooledValueTask MqttUpAsync(MqttServer @this, TopicArray topicArray, CancellationToken cancellationToken) { - return new OperResult("Upload fail", ex); + try + { + var message = new MqttApplicationMessageBuilder() + .WithTopic(topicArray.Topic).WithQualityOfServiceLevel(@this._driverPropertys.MqttQualityOfServiceLevel).WithRetainFlag() + .WithPayload(topicArray.Payload).Build(); + await @this._mqttServer.InjectApplicationMessage( + new InjectedMqttApplicationMessage(message), cancellationToken).ConfigureAwait(false); + + if (@this._driverPropertys.DetailLog) + { + if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) + @this.LogMessage?.LogTrace(@this.GetDetailLogString(topicArray, @this._memoryVarModelQueue.Count)); + else if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) + @this.LogMessage?.LogDebug(@this.GetCountLogString(topicArray, @this._memoryVarModelQueue.Count)); + } + else + { + if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) + @this.LogMessage?.LogDebug(@this.GetCountLogString(topicArray, @this._memoryVarModelQueue.Count)); + } + return OperResult.Success; + } + catch (Exception ex) + { + return new OperResult("Upload fail", ex); + } } } diff --git a/src/Plugin/ThingsGateway.Plugin.OpcDa/OpcDaMaster/OpcDaMaster.cs b/src/Plugin/ThingsGateway.Plugin.OpcDa/OpcDaMaster/OpcDaMaster.cs index fb4a5601b..5138f504e 100644 --- a/src/Plugin/ThingsGateway.Plugin.OpcDa/OpcDaMaster/OpcDaMaster.cs +++ b/src/Plugin/ThingsGateway.Plugin.OpcDa/OpcDaMaster/OpcDaMaster.cs @@ -160,7 +160,6 @@ public class OpcDaMaster : CollectBase protected override async ValueTask> WriteValuesAsync(Dictionary writeInfoLists, CancellationToken cancellationToken) { using var writeLock = await ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false); - await ValueTask.CompletedTask.ConfigureAwait(false); var result = _plc.WriteItem(writeInfoLists.ToDictionary(a => a.Key.RegisterAddress!, a => a.Value.GetObjectFromJToken()!)); var results = new NonBlockingDictionary(result.ToDictionary>, string, OperResult>(a => writeInfoLists.Keys.FirstOrDefault(b => b.RegisterAddress == a.Key).Name, a => { diff --git a/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaMaster/OpcUaMaster.cs b/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaMaster/OpcUaMaster.cs index 71a388ee3..eab6043bf 100644 --- a/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaMaster/OpcUaMaster.cs +++ b/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaMaster/OpcUaMaster.cs @@ -13,6 +13,8 @@ using Newtonsoft.Json.Linq; using Opc.Ua; using Opc.Ua.Client; +using PooledAwait; + using System.Collections.Concurrent; using ThingsGateway.Foundation.Extension.Generic; @@ -150,50 +152,58 @@ public class OpcUaMaster : CollectBase } private volatile bool checkLog; - private async Task CheckAsync(object? state, CancellationToken cancellationToken) + private Task CheckAsync(object? state, CancellationToken cancellationToken) { - if (_plc.Session != null) + return CheckAsync(this, cancellationToken); + + + static async PooledTask CheckAsync(OpcUaMaster @this, CancellationToken cancellationToken) { - if (_driverProperties.ActiveSubscribe) + if (@this._plc.Session != null) { - //获取设备连接状态 - if (IsConnected()) + if (@this._driverProperties.ActiveSubscribe) { - //更新设备活动时间 + //获取设备连接状态 + if (@this.IsConnected()) { - //如果是订阅模式,连接时添加订阅组 - if (_plc.OpcUaProperty?.ActiveSubscribe == true && CurrentDevice.VariableSourceReads.Count > 0 && _plc.Session.SubscriptionCount < CurrentDevice.VariableSourceReads.Count) + //更新设备活动时间 { - if (cancellationToken.IsCancellationRequested) return; - foreach (var variableSourceRead in CurrentDevice.VariableSourceReads) + //如果是订阅模式,连接时添加订阅组 + if (@this._plc.OpcUaProperty?.ActiveSubscribe == true && @this.CurrentDevice.VariableSourceReads.Count > 0 && @this._plc.Session.SubscriptionCount < @this.CurrentDevice.VariableSourceReads.Count) { if (cancellationToken.IsCancellationRequested) return; - try + foreach (var variableSourceRead in @this.CurrentDevice.VariableSourceReads) { - if (_plc.Session.Subscriptions.FirstOrDefault(a => a.DisplayName == variableSourceRead.RegisterAddress) == null) + if (cancellationToken.IsCancellationRequested) return; + try { - await _plc.AddSubscriptionAsync(variableSourceRead.RegisterAddress, variableSourceRead.VariableRuntimes.Where(a => !a.RegisterAddress.IsNullOrEmpty()).Select(a => a.RegisterAddress!).ToHashSet().ToArray(), _plc.OpcUaProperty.LoadType, cancellationToken).ConfigureAwait(false); + if (@this._plc.Session.Subscriptions.FirstOrDefault(a => a.DisplayName == variableSourceRead.RegisterAddress) == null) + { + await @this._plc.AddSubscriptionAsync(variableSourceRead.RegisterAddress, variableSourceRead.VariableRuntimes.Where(a => !a.RegisterAddress.IsNullOrEmpty()).Select(a => a.RegisterAddress!).ToHashSet().ToArray(), @this._plc.OpcUaProperty.LoadType, cancellationToken).ConfigureAwait(false); - LogMessage?.LogInformation($"AddSubscription index {CurrentDevice.VariableSourceReads.IndexOf(variableSourceRead)} done"); + @this.LogMessage?.LogInformation($"AddSubscription index {@this.CurrentDevice.VariableSourceReads.IndexOf(variableSourceRead)} done"); + } + + await Task.Delay(100, cancellationToken).ConfigureAwait(false); // allow for subscription to be finished on server? + + @this.checkLog = true; + } + catch (Exception ex) + { + if (!@this.checkLog) + @this.LogMessage?.LogWarning(ex, "AddSubscriptions error"); + @this.checkLog = false; } - await Task.Delay(100, cancellationToken).ConfigureAwait(false); // allow for subscription to be finished on server? - - checkLog = true; } - catch (Exception ex) - { - if (!checkLog) - LogMessage?.LogWarning(ex, "AddSubscriptions error"); - checkLog = false; - } - + @this.LogMessage?.LogInformation("AddSubscriptions done"); } - LogMessage?.LogInformation("AddSubscriptions done"); } } } } + + return; } } @@ -230,51 +240,56 @@ public class OpcUaMaster : CollectBase } /// - protected override async ValueTask>> ReadSourceAsync(VariableSourceRead deviceVariableSourceRead, CancellationToken cancellationToken) + protected override ValueTask>> ReadSourceAsync(VariableSourceRead deviceVariableSourceRead, CancellationToken cancellationToken) { - DateTime time = DateTime.Now; - var addresss = deviceVariableSourceRead.VariableRuntimes.Where(a => !a.RegisterAddress.IsNullOrEmpty()).Select(a => a.RegisterAddress!).ToArray(); - try + return ReadSourceAsync(this, deviceVariableSourceRead, cancellationToken); + + static async PooledValueTask>> ReadSourceAsync(OpcUaMaster @this, VariableSourceRead deviceVariableSourceRead, CancellationToken cancellationToken) { - var result = await _plc.ReadJTokenValueAsync(addresss, cancellationToken).ConfigureAwait(false); - foreach (var data in result) + DateTime time = DateTime.Now; + var addresss = deviceVariableSourceRead.VariableRuntimes.Where(a => !a.RegisterAddress.IsNullOrEmpty()).Select(a => a.RegisterAddress!).ToArray(); + try { - if (!cancellationToken.IsCancellationRequested) + var result = await @this._plc.ReadJTokenValueAsync(addresss, cancellationToken).ConfigureAwait(false); + foreach (var data in result) { - var data1 = deviceVariableSourceRead.VariableRuntimes.Where(a => a.RegisterAddress == data.Item1); - - foreach (var item in data1) + if (!cancellationToken.IsCancellationRequested) { - object value = data.Item3.GetObjectFromJToken(); + var data1 = deviceVariableSourceRead.VariableRuntimes.Where(a => a.RegisterAddress == data.Item1); - var isGood = StatusCode.IsGood(data.Item2.StatusCode); - if (_driverProperties.SourceTimestampEnable) + foreach (var item in data1) { - time = data.Item2.SourceTimestamp.ToLocalTime(); - } - if (isGood) - { - item.SetValue(value, time); - } - else - { - if (item is VariableRuntime variable && (variable.IsOnline || variable.CollectTime == DateTime.UnixEpoch.ToLocalTime())) + object value = data.Item3.GetObjectFromJToken(); + + var isGood = StatusCode.IsGood(data.Item2.StatusCode); + if (@this._driverProperties.SourceTimestampEnable) { - LogMessage?.LogWarning($"OPC quality bad:{Environment.NewLine}{data.Item1}"); + time = data.Item2.SourceTimestamp.ToLocalTime(); + } + if (isGood) + { + item.SetValue(value, time); + } + else + { + if (item is VariableRuntime variable && (variable.IsOnline || variable.CollectTime == DateTime.UnixEpoch.ToLocalTime())) + { + @this.LogMessage?.LogWarning($"OPC quality bad:{Environment.NewLine}{data.Item1}"); + } + item.SetValue(null, time, false); + item.VariableSource.LastErrorMessage = data.Item2.StatusCode.ToString(); } - item.SetValue(null, time, false); - item.VariableSource.LastErrorMessage = data.Item2.StatusCode.ToString(); } + @this.LogMessage?.Trace($"Change:{Environment.NewLine}{data.Item1} : {data.Item3}"); } - LogMessage?.Trace($"Change:{Environment.NewLine}{data.Item1} : {data.Item3}"); } - } - return OperResult.CreateSuccessResult>(null); - } - catch (Exception ex) - { - return new OperResult>($"ReadSourceAsync {addresss.ToSystemTextJsonString()}:{Environment.NewLine}{ex}"); + return OperResult.CreateSuccessResult>(null); + } + catch (Exception ex) + { + return new OperResult>($"ReadSourceAsync {addresss.ToSystemTextJsonString()}:{Environment.NewLine}{ex}"); + } } } diff --git a/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaServer/OpcUaServer.cs b/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaServer/OpcUaServer.cs index 9186be929..c225f194d 100644 --- a/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaServer/OpcUaServer.cs +++ b/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaServer/OpcUaServer.cs @@ -15,6 +15,8 @@ using Opc.Ua; using Opc.Ua.Bindings; using Opc.Ua.Configuration; +using PooledAwait; + using System.Collections.Concurrent; using System.Reflection; @@ -197,56 +199,61 @@ public partial class OpcUaServer : BusinessBase await base.ProtectedStartAsync(cancellationToken).ConfigureAwait(false); } - protected override async Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) + protected override Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) { - try + return ProtectedExecuteAsync(this, cancellationToken); + + static async PooledTask ProtectedExecuteAsync(OpcUaServer @this, CancellationToken cancellationToken) { - if (!IsConnected()) + try { - try + if (!@this.IsConnected()) { - await Task.Delay(2000, cancellationToken).ConfigureAwait(false); - await m_application.CheckApplicationInstanceCertificatesAsync(true, 1200, cancellationToken).ConfigureAwait(false); - await m_application.StartAsync(m_server).ConfigureAwait(false); - connect_success = true; - await Task.Delay(2000, cancellationToken).ConfigureAwait(false); - IdVariableRuntimes.ForEach(a => VariableValueChange(a.Value, a.Value.AdaptVariableBasicData())); - } - catch (Exception ex) - { - if (connect_success) - LogMessage?.LogWarning(ex, "Failed to start service"); - connect_success = false; - await Task.Delay(10000, cancellationToken).ConfigureAwait(false); - } - } - var varList = CollectVariableRuntimes.ToListWithDequeue(); - foreach (var item in varList) - { - try - { - if (!cancellationToken.IsCancellationRequested) + try { - m_server?.NodeManager?.UpVariable(item); + await Task.Delay(2000, cancellationToken).ConfigureAwait(false); + await @this.m_application.CheckApplicationInstanceCertificatesAsync(true, 1200, cancellationToken).ConfigureAwait(false); + await @this.m_application.StartAsync(@this.m_server).ConfigureAwait(false); + @this.connect_success = true; + await Task.Delay(2000, cancellationToken).ConfigureAwait(false); + @this.IdVariableRuntimes.ForEach(a => @this.VariableValueChange(a.Value, a.Value.AdaptVariableBasicData())); } - else + catch (Exception ex) { - break; + if (@this.connect_success) + @this.LogMessage?.LogWarning(ex, "Failed to start service"); + @this.connect_success = false; + await Task.Delay(10000, cancellationToken).ConfigureAwait(false); } } - catch (Exception ex) + var varList = @this.CollectVariableRuntimes.ToListWithDequeue(); + foreach (var item in varList) { - LogMessage?.LogWarning(ex); + try + { + if (!cancellationToken.IsCancellationRequested) + { + @this.m_server?.NodeManager?.UpVariable(item); + } + else + { + break; + } + } + catch (Exception ex) + { + @this.LogMessage?.LogWarning(ex); + } } + @this.success = true; + } + catch (OperationCanceledException) { } + catch (Exception ex) + { + if (@this.success) + @this.LogMessage?.LogWarning(ex); + @this.success = false; } - success = true; - } - catch (OperationCanceledException) { } - catch (Exception ex) - { - if (success) - LogMessage?.LogWarning(ex); - success = false; } } diff --git a/src/Plugin/ThingsGateway.Plugin.RabbitMQ/RabbitMQ/RabbitMQProducer.cs b/src/Plugin/ThingsGateway.Plugin.RabbitMQ/RabbitMQ/RabbitMQProducer.cs index fe074b2fa..1fd9812ee 100644 --- a/src/Plugin/ThingsGateway.Plugin.RabbitMQ/RabbitMQ/RabbitMQProducer.cs +++ b/src/Plugin/ThingsGateway.Plugin.RabbitMQ/RabbitMQ/RabbitMQProducer.cs @@ -8,6 +8,8 @@ // QQ群:605534569 //------------------------------------------------------------------------------ +using PooledAwait; + using RabbitMQ.Client; using ThingsGateway.Foundation; @@ -64,39 +66,44 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScriptAll await base.DisposeAsync(disposing).ConfigureAwait(false); } - protected override async Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) + protected override Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) { - if (_channel == null) + return ProtectedExecuteAsync(this, cancellationToken); + + static async PooledTask ProtectedExecuteAsync(RabbitMQProducer @this, CancellationToken cancellationToken) { - try + if (@this._channel == null) { - // 创建连接 - _connection ??= await _connectionFactory.CreateConnectionAsync(cancellationToken).ConfigureAwait(false); - // 创建通道 - _channel ??= await _connection.CreateChannelAsync(cancellationToken: cancellationToken).ConfigureAwait(false); - // 声明路由队列 - if (_driverPropertys.IsQueueDeclare) + try { - await (_channel?.QueueDeclareAsync(_driverPropertys.VariableTopic, true, false, false, cancellationToken: cancellationToken)).ConfigureAwait(false); - await (_channel?.QueueDeclareAsync(_driverPropertys.DeviceTopic, true, false, false, cancellationToken: cancellationToken)).ConfigureAwait(false); - await (_channel?.QueueDeclareAsync(_driverPropertys.AlarmTopic, true, false, false, cancellationToken: cancellationToken)).ConfigureAwait(false); + // 创建连接 + @this._connection ??= await @this._connectionFactory.CreateConnectionAsync(cancellationToken).ConfigureAwait(false); + // 创建通道 + @this._channel ??= await @this._connection.CreateChannelAsync(cancellationToken: cancellationToken).ConfigureAwait(false); + // 声明路由队列 + if (@this._driverPropertys.IsQueueDeclare) + { + await (@this._channel?.QueueDeclareAsync(@this._driverPropertys.VariableTopic, true, false, false, cancellationToken: cancellationToken)).ConfigureAwait(false); + await (@this._channel?.QueueDeclareAsync(@this._driverPropertys.DeviceTopic, true, false, false, cancellationToken: cancellationToken)).ConfigureAwait(false); + await (@this._channel?.QueueDeclareAsync(@this._driverPropertys.AlarmTopic, true, false, false, cancellationToken: cancellationToken)).ConfigureAwait(false); + } + @this.success = true; + } + catch (Exception ex) + { + if (@this.success) + { + @this.LogMessage?.LogWarning(ex); + @this.success = false; + } + await Task.Delay(10000, cancellationToken).ConfigureAwait(false); } - success = true; } - catch (Exception ex) + else { - if (success) - { - LogMessage?.LogWarning(ex); - success = false; - } - await Task.Delay(10000, cancellationToken).ConfigureAwait(false); + await @this.Update(cancellationToken).ConfigureAwait(false); } } - else - { - await Update(cancellationToken).ConfigureAwait(false); - } } #endif diff --git a/src/Plugin/ThingsGateway.Plugin.RabbitMQ/RabbitMQ/RabbitMQProducer.other.cs b/src/Plugin/ThingsGateway.Plugin.RabbitMQ/RabbitMQ/RabbitMQProducer.other.cs index d346bcb96..a0474d196 100644 --- a/src/Plugin/ThingsGateway.Plugin.RabbitMQ/RabbitMQ/RabbitMQProducer.other.cs +++ b/src/Plugin/ThingsGateway.Plugin.RabbitMQ/RabbitMQ/RabbitMQProducer.other.cs @@ -8,6 +8,8 @@ // QQ群:605534569 //------------------------------------------------------------------------------ +using PooledAwait; + using RabbitMQ.Client; using ThingsGateway.Extension.Generic; @@ -148,26 +150,30 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScriptAll #region private - private async ValueTask Update(IEnumerable topicArrayList, CancellationToken cancellationToken) + private ValueTask Update(IEnumerable topicArrayList, CancellationToken cancellationToken) { - foreach (var topicArray in topicArrayList) + return Update(this, topicArrayList, cancellationToken); + + static async PooledValueTask Update(RabbitMQProducer @this, IEnumerable topicArrayList, CancellationToken cancellationToken) { - var result = await RabbitMQUpAsync(topicArray, cancellationToken).ConfigureAwait(false); - if (success != result.IsSuccess) + foreach (var topicArray in topicArrayList) { + var result = await @this.RabbitMQUpAsync(topicArray, cancellationToken).ConfigureAwait(false); + if (@this.success != result.IsSuccess) + { + if (!result.IsSuccess) + { + @this.LogMessage?.LogWarning(result.ToString()); + } + @this.success = result.IsSuccess; + } if (!result.IsSuccess) { - LogMessage?.LogWarning(result.ToString()); + return result; } - success = result.IsSuccess; - } - if (!result.IsSuccess) - { - return result; } + return OperResult.Success; } - OperResult operResult = OperResult.Success; - return operResult; } private ValueTask UpdateAlarmModel(IEnumerable item, CancellationToken cancellationToken) @@ -226,37 +232,42 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScriptAll /// /// 上传,返回上传结果 /// - public async Task RabbitMQUpAsync(TopicArray topicArray, CancellationToken cancellationToken) + public Task RabbitMQUpAsync(TopicArray topicArray, CancellationToken cancellationToken) { - try - { - if (_channel != null) - { - await _channel.BasicPublishAsync(_driverPropertys.ExchangeName, topicArray.Topic, topicArray.Payload, cancellationToken).ConfigureAwait(false); + return RabbitMQUpAsync(this, topicArray, cancellationToken); - if (_driverPropertys.DetailLog) + static async PooledTask RabbitMQUpAsync(RabbitMQProducer @this, TopicArray topicArray, CancellationToken cancellationToken) + { + try + { + if (@this._channel != null) { - if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) - LogMessage?.LogTrace(GetDetailLogString(topicArray, _memoryVarModelQueue.Count)); - else if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) - LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count)); + await @this._channel.BasicPublishAsync(@this._driverPropertys.ExchangeName, topicArray.Topic, topicArray.Payload, cancellationToken).ConfigureAwait(false); + + if (@this._driverPropertys.DetailLog) + { + if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) + @this.LogMessage?.LogTrace(@this.GetDetailLogString(topicArray, @this._memoryVarModelQueue.Count)); + else if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) + @this.LogMessage?.LogDebug(@this.GetCountLogString(topicArray, @this._memoryVarModelQueue.Count)); + } + else + { + if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) + @this.LogMessage?.LogDebug(@this.GetCountLogString(topicArray, @this._memoryVarModelQueue.Count)); + } + return OperResult.Success; } else { - if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) - LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count)); + return new OperResult("Upload fail"); } - return OperResult.Success; } - else + catch (Exception ex) { - return new OperResult("Upload fail"); + return new OperResult(ex); } } - catch (Exception ex) - { - return new OperResult(ex); - } } #endregion 方法 diff --git a/src/Plugin/ThingsGateway.Plugin.SiemensS7/SiemensS7Master/SiemensS7Master.cs b/src/Plugin/ThingsGateway.Plugin.SiemensS7/SiemensS7Master/SiemensS7Master.cs index 92c4ed9ef..a46c1a787 100644 --- a/src/Plugin/ThingsGateway.Plugin.SiemensS7/SiemensS7Master/SiemensS7Master.cs +++ b/src/Plugin/ThingsGateway.Plugin.SiemensS7/SiemensS7Master/SiemensS7Master.cs @@ -10,6 +10,8 @@ using Newtonsoft.Json.Linq; +using PooledAwait; + using System.Collections.Concurrent; using ThingsGateway.Debug; @@ -119,74 +121,79 @@ public class SiemensS7Master : CollectFoundationBase } } - protected override async ValueTask> WriteValuesAsync(Dictionary writeInfoLists, CancellationToken cancellationToken) + protected override ValueTask> WriteValuesAsync(Dictionary writeInfoLists, CancellationToken cancellationToken) { - using var writeLock = await ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false); + return WriteValuesAsync(this, writeInfoLists, cancellationToken); - // 检查协议是否为空,如果为空则抛出异常 - if (FoundationDevice == null) - throw new NotSupportedException(); - - // 创建用于存储操作结果的并发字典 - NonBlockingDictionary operResults = new(); - - //转换 - Dictionary addresses = new(); - var w1 = writeInfoLists.Where(a => a.Key.DataType != DataTypeEnum.String); - var w2 = writeInfoLists.Where(a => a.Key.DataType == DataTypeEnum.String); - foreach (var item in w1) + static async PooledValueTask> WriteValuesAsync(SiemensS7Master @this, Dictionary writeInfoLists, CancellationToken cancellationToken) { - SiemensS7Address siemensS7Address = SiemensS7Address.ParseFrom(item.Key.RegisterAddress); - siemensS7Address.Data = GetBytes(item.Key.DataType, item.Value); - siemensS7Address.Length = siemensS7Address.Data.Length; - siemensS7Address.BitLength = 1; - siemensS7Address.IsBit = item.Key.DataType == DataTypeEnum.Boolean; - if (item.Key.DataType == DataTypeEnum.Boolean) + using var writeLock = await @this.ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false); + + // 检查协议是否为空,如果为空则抛出异常 + if (@this.FoundationDevice == null) + throw new NotSupportedException(); + + // 创建用于存储操作结果的并发字典 + NonBlockingDictionary operResults = new(); + + //转换 + Dictionary addresses = new(); + var w1 = writeInfoLists.Where(a => a.Key.DataType != DataTypeEnum.String); + var w2 = writeInfoLists.Where(a => a.Key.DataType == DataTypeEnum.String); + foreach (var item in w1) { - if (item.Value is JArray jArray) + SiemensS7Address siemensS7Address = SiemensS7Address.ParseFrom(item.Key.RegisterAddress); + siemensS7Address.Data = @this.GetBytes(item.Key.DataType, item.Value); + siemensS7Address.Length = siemensS7Address.Data.Length; + siemensS7Address.BitLength = 1; + siemensS7Address.IsBit = item.Key.DataType == DataTypeEnum.Boolean; + if (item.Key.DataType == DataTypeEnum.Boolean) { - siemensS7Address.BitLength = jArray.ToObject().Length; + if (item.Value is JArray jArray) + { + siemensS7Address.BitLength = jArray.ToObject().Length; + } } + addresses.Add(item.Key, siemensS7Address); } - addresses.Add(item.Key, siemensS7Address); - } - if (addresses.Count > 0) - { - var result = await _plc.S7WriteAsync(addresses.Select(a => a.Value).ToArray(), cancellationToken).ConfigureAwait(false); - foreach (var writeInfo in addresses) + if (addresses.Count > 0) { - if (result.TryGetValue(writeInfo.Value, out var r1)) + var result = await @this._plc.S7WriteAsync(addresses.Select(a => a.Value).ToArray(), cancellationToken).ConfigureAwait(false); + foreach (var writeInfo in addresses) { - operResults.TryAdd(writeInfo.Key.Name, r1); + if (result.TryGetValue(writeInfo.Value, out var r1)) + { + operResults.TryAdd(writeInfo.Key.Name, r1); + } } + + + } + // 使用并发方式遍历写入信息列表,并进行异步写入操作 + await w2.ForEachAsync(async (writeInfo) => + { + try + { + // 调用协议的写入方法,将写入信息中的数据写入到对应的寄存器地址,并获取操作结果 + var result = await @this.FoundationDevice.WriteJTokenAsync(writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType, cancellationToken).ConfigureAwait(false); + // 将操作结果添加到结果字典中,使用变量名称作为键 + operResults.TryAdd(writeInfo.Key.Name, result); + } + catch (Exception ex) + { + operResults.TryAdd(writeInfo.Key.Name, new(ex)); + } + }).ConfigureAwait(false); + await @this.Check(writeInfoLists, operResults, cancellationToken).ConfigureAwait(false); + if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) + @this.LogMessage?.Debug(string.Format("Write result: {0} - {1}", @this.DeviceName, operResults.Select(a => $"{a.Key} - {a.Key.Length} - {(a.Value.IsSuccess ? "Success" : a.Value.ErrorMessage)}").ToSystemTextJsonString(false))); + // 返回包含操作结果的字典 + return new Dictionary(operResults); } - - // 使用并发方式遍历写入信息列表,并进行异步写入操作 - await w2.ForEachAsync(async (writeInfo) => - { - try - { - // 调用协议的写入方法,将写入信息中的数据写入到对应的寄存器地址,并获取操作结果 - var result = await FoundationDevice.WriteJTokenAsync(writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType, cancellationToken).ConfigureAwait(false); - - // 将操作结果添加到结果字典中,使用变量名称作为键 - operResults.TryAdd(writeInfo.Key.Name, result); - } - catch (Exception ex) - { - operResults.TryAdd(writeInfo.Key.Name, new(ex)); - } - }).ConfigureAwait(false); - - await Check(writeInfoLists, operResults, cancellationToken).ConfigureAwait(false); - if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) - LogMessage?.Debug(string.Format("Write result: {0} - {1}", DeviceName, operResults.Select(a => $"{a.Key} - {a.Key.Length} - {(a.Value.IsSuccess ? "Success" : a.Value.ErrorMessage)}").ToSystemTextJsonString(false))); - // 返回包含操作结果的字典 - return new Dictionary(operResults); } diff --git a/src/ThingsGateway.ScriptDebug/Test/TestCollectPlugin.cs b/src/ThingsGateway.ScriptDebug/Test/TestCollectPlugin.cs index 533d7f7ab..7567d07cb 100644 --- a/src/ThingsGateway.ScriptDebug/Test/TestCollectPlugin.cs +++ b/src/ThingsGateway.ScriptDebug/Test/TestCollectPlugin.cs @@ -162,7 +162,7 @@ public class TestCollectPlugin1 : CollectBase /// /// /// - protected override Task TestOnline(object? state, CancellationToken cancellationToken) + protected override ValueTask TestOnline(object? state, CancellationToken cancellationToken) { return base.TestOnline(state, cancellationToken); }