pref: 异步状态机优化

This commit is contained in:
2248356998 qq.com
2025-10-18 23:14:55 +08:00
parent 2a8c0cbab1
commit 47e442874c
49 changed files with 3222 additions and 2308 deletions

View File

@@ -15,11 +15,11 @@ public partial class AdminTable<TItem> where TItem : class, new()
{ {
/// <inheritdoc cref="Table{TItem}.OnColumnVisibleChanged"/> /// <inheritdoc cref="Table{TItem}.OnColumnVisibleChanged"/>
[Parameter] [Parameter]
public Func<string,bool, Task> OnColumnVisibleChanged { get; set; } public Func<string, bool, Task> OnColumnVisibleChanged { get; set; }
/// <inheritdoc cref="Table{TItem}.OnColumnCreating"/> /// <inheritdoc cref="Table{TItem}.OnColumnCreating"/>
[Parameter] [Parameter]
public Func<List<ITableColumn>,Task> OnColumnCreating { get; set; } public Func<List<ITableColumn>, Task> OnColumnCreating { get; set; }
/// <inheritdoc cref="Table{TItem}.RenderMode"/> /// <inheritdoc cref="Table{TItem}.RenderMode"/>
[Parameter] [Parameter]
public TableRenderMode RenderMode { get; set; } public TableRenderMode RenderMode { get; set; }

View File

@@ -118,7 +118,7 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
if (count == 0) Init(); if (count == 0) Init();
#if DEBUG #if DEBUG
WriteLog("Acquire Create Free={0} Busy={1}", FreeCount, count + 1); WriteLog("Acquire Create Free={0} Busy={1}", FreeCount, count + 1);
#endif #endif
} }
} }
@@ -156,7 +156,7 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
if (!_busy.Remove(value)) if (!_busy.Remove(value))
{ {
#if DEBUG #if DEBUG
WriteLog("Return Error"); WriteLog("Return Error");
#endif #endif
return false; return false;

View File

@@ -8,7 +8,7 @@
// QQ群605534569 // QQ群605534569
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
namespace ThingsGateway.Foundation; namespace ThingsGateway.NewLife;
public class LinkedCancellationTokenSourceCache : IDisposable public class LinkedCancellationTokenSourceCache : IDisposable
{ {
@@ -63,6 +63,7 @@ public class LinkedCancellationTokenSourceCache : IDisposable
_cachedCts?.Dispose(); _cachedCts?.Dispose();
_cachedCts = null!; _cachedCts = null!;
} }
GC.SuppressFinalize(this);
} }
} }

View File

@@ -10,13 +10,18 @@
// 感谢您的下载和使用 // 感谢您的下载和使用
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
namespace ThingsGateway.Foundation; namespace ThingsGateway.NewLife;
using System; using System;
using System.Threading; using System.Threading;
public sealed class ReusableCancellationTokenSource : IDisposable public sealed class ReusableCancellationTokenSource : IDisposable
{ {
~ReusableCancellationTokenSource()
{
Dispose();
}
private readonly Timer _timer; private readonly Timer _timer;
private CancellationTokenSource? _cts; private CancellationTokenSource? _cts;
@@ -47,7 +52,7 @@ public sealed class ReusableCancellationTokenSource : IDisposable
/// <summary> /// <summary>
/// 获取一个 CTS并启动超时 /// 获取一个 CTS并启动超时
/// </summary> /// </summary>
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; TimeoutStatus = false;
@@ -57,7 +62,7 @@ public sealed class ReusableCancellationTokenSource : IDisposable
// 启动 Timer // 启动 Timer
_timer.Change(timeout, Timeout.Infinite); _timer.Change(timeout, Timeout.Infinite);
return _cts; return _cts.Token;
} }
@@ -71,15 +76,16 @@ public sealed class ReusableCancellationTokenSource : IDisposable
/// </summary> /// </summary>
public void Cancel() public void Cancel()
{ {
_cts?.SafeCancel(); try { _cts?.Cancel(); } catch { }
} }
public void Dispose() public void Dispose()
{ {
_cts?.SafeCancel(); try { _cts?.Cancel(); } catch { }
_cts?.SafeDispose(); try { _cts?.Dispose(); } catch { }
_linkedCtsCache.SafeDispose(); try { _linkedCtsCache?.Dispose(); } catch { }
_timer.SafeDispose(); try { _timer?.Dispose(); } catch { }
GC.SuppressFinalize(this);
} }
} }

View File

@@ -8,6 +8,8 @@
// QQ群605534569 // QQ群605534569
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using ThingsGateway.NewLife.Collections;
namespace ThingsGateway.NewLife; namespace ThingsGateway.NewLife;
/// <summary> /// <summary>
@@ -93,15 +95,20 @@ public sealed class WaitLock : IDisposable
#if NET6_0_OR_GREATER #if NET6_0_OR_GREATER
if (cancellationToken.CanBeCanceled) if (cancellationToken.CanBeCanceled)
return WaitUntilCountOrTimeoutAsync(_waiterLock.WaitAsync(Timeout.Infinite, CancellationToken.None), Timeout.Infinite, cancellationToken); return WaitUntilCountOrTimeoutAsync(_waiterLock.WaitAsync(Timeout.Infinite, CancellationToken.None), Timeout.Infinite, cancellationToken);
//return WaitUntilAsync2(_waiterLock.WaitAsync(Timeout.Infinite, CancellationToken.None), Timeout.Infinite, cancellationToken);
else else
return _waiterLock.WaitAsync(Timeout.Infinite, CancellationToken.None); return _waiterLock.WaitAsync(Timeout.Infinite, CancellationToken.None);
#else #else
return _waiterLock.WaitAsync(Timeout.Infinite,cancellationToken); return _waiterLock.WaitAsync(Timeout.Infinite, cancellationToken);
#endif #endif
} }
#if NET6_0_OR_GREATER #if NET6_0_OR_GREATER
//private ObjectPoolLock<ReusableCancellationTokenSource> _reusableTimeouts = new();
/// <summary>Performs the asynchronous wait.</summary> /// <summary>Performs the asynchronous wait.</summary>
/// <param name="asyncWaiter">The asynchronous waiter.</param> /// <param name="asyncWaiter">The asynchronous waiter.</param>
/// <param name="millisecondsTimeout">The timeout.</param> /// <param name="millisecondsTimeout">The timeout.</param>
@@ -118,6 +125,61 @@ public sealed class WaitLock : IDisposable
return (asyncWaiter.WaitAsync(TimeSpan.FromMilliseconds(millisecondsTimeout), cancellationToken)); 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<object?>(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<object?>, 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<object?>, CancellationTokenRegistration, ObjectPoolLock<ReusableCancellationTokenSource>, 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 #endif
/// <summary> /// <summary>
@@ -132,7 +194,7 @@ public sealed class WaitLock : IDisposable
return _waiterLock.WaitAsync(Timeout.Infinite, CancellationToken.None); return _waiterLock.WaitAsync(Timeout.Infinite, CancellationToken.None);
#else #else
return _waiterLock.WaitAsync(millisecondsTimeout,cancellationToken); return _waiterLock.WaitAsync(millisecondsTimeout, cancellationToken);
#endif #endif
} }
@@ -140,6 +202,9 @@ public sealed class WaitLock : IDisposable
public void Dispose() public void Dispose()
{ {
DisposedValue = true; DisposedValue = true;
#if NET6_0_OR_GREATER
//_reusableTimeouts?.TryDispose();
#endif
_waiterLock?.TryDispose(); _waiterLock?.TryDispose();
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }

View File

@@ -56,7 +56,8 @@ namespace PooledAwait.Internal
/// <summary>Whether the current operation has completed.</summary> /// <summary>Whether the current operation has completed.</summary>
private bool _completed; private bool _completed;
/// <summary>The result with which the operation succeeded, or the default value if it hasn't yet completed or failed.</summary> /// <summary>The result with which the operation succeeded, or the default value if it hasn't yet completed or failed.</summary>
/* [AllowNull, MaybeNull] */ private TResult _result; /* [AllowNull, MaybeNull] */
private TResult _result;
/// <summary>The exception with which the operation failed, or null if it hasn't yet completed or completed successfully.</summary> /// <summary>The exception with which the operation failed, or null if it hasn't yet completed or completed successfully.</summary>
private ExceptionDispatchInfo? _error; private ExceptionDispatchInfo? _error;
/// <summary>The current version of this value, used to help prevent misuse.</summary> /// <summary>The current version of this value, used to help prevent misuse.</summary>

View File

@@ -1,6 +1,5 @@
using System.Data; using System.Data;
using System.Data.Common; using System.Data.Common;
using System.Numerics;
using ThingsGateway.NewLife.Reflection; using ThingsGateway.NewLife.Reflection;
namespace ThingsGateway.SqlSugar namespace ThingsGateway.SqlSugar

View File

@@ -1,9 +1,9 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<PluginVersion>10.11.111</PluginVersion> <PluginVersion>10.11.114</PluginVersion>
<ProPluginVersion>10.11.111</ProPluginVersion> <ProPluginVersion>10.11.114</ProPluginVersion>
<DefaultVersion>10.11.111</DefaultVersion> <DefaultVersion>10.11.114</DefaultVersion>
<AuthenticationVersion>10.11.6</AuthenticationVersion> <AuthenticationVersion>10.11.6</AuthenticationVersion>
<SourceGeneratorVersion>10.11.6</SourceGeneratorVersion> <SourceGeneratorVersion>10.11.6</SourceGeneratorVersion>
<NET8Version>8.0.21</NET8Version> <NET8Version>8.0.21</NET8Version>
@@ -12,7 +12,7 @@
<IsTrimmable>false</IsTrimmable> <IsTrimmable>false</IsTrimmable>
<ManagementProPluginVersion>10.11.87</ManagementProPluginVersion> <ManagementProPluginVersion>10.11.87</ManagementProPluginVersion>
<ManagementPluginVersion>10.11.87</ManagementPluginVersion> <ManagementPluginVersion>10.11.87</ManagementPluginVersion>
<TSVersion>4.0.0-beta.135</TSVersion> <TSVersion>4.0.0-beta.140</TSVersion>
</PropertyGroup> </PropertyGroup>

View File

@@ -11,6 +11,8 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using PooledAwait;
using System.Linq.Expressions; using System.Linq.Expressions;
using ThingsGateway.Gateway.Application.Extensions; using ThingsGateway.Gateway.Application.Extensions;
@@ -139,18 +141,27 @@ public abstract class VariableObject
/// <summary> /// <summary>
/// <see cref="VariableRuntimeAttribute"/>特性连读,反射赋值到继承类中的属性 /// <see cref="VariableRuntimeAttribute"/>特性连读,反射赋值到继承类中的属性
/// </summary> /// </summary>
public virtual async ValueTask<OperResult> MultiReadAsync(CancellationToken cancellationToken = default) public virtual ValueTask<OperResult> MultiReadAsync(CancellationToken cancellationToken = default)
{ {
try try
{ {
GetVariableSources(); GetVariableSources();
//连读 //连读
foreach (var item in DeviceVariableSourceReads) return MultiReadAsync(this, cancellationToken);
}
catch (Exception ex)
{
return EasyValueTask.FromResult(new OperResult(ex));
}
static async PooledValueTask<OperResult> 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) 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) if (!result1.IsSuccess)
{ {
item.LastErrorMessage = result1.ErrorMessage; item.LastErrorMessage = result1.ErrorMessage;
@@ -168,13 +179,9 @@ public abstract class VariableObject
} }
} }
SetValue(); @this.SetValue();
return OperResult.Success; return OperResult.Success;
} }
catch (Exception ex)
{
return new OperResult(ex);
}
} }
/// <summary> /// <summary>
@@ -205,30 +212,31 @@ public abstract class VariableObject
/// <param name="propertyName">属性名称,必须使用<see cref="VariableRuntimeAttribute"/>特性</param> /// <param name="propertyName">属性名称,必须使用<see cref="VariableRuntimeAttribute"/>特性</param>
/// <param name="value">写入值</param> /// <param name="value">写入值</param>
/// <param name="cancellationToken">取消令箭</param> /// <param name="cancellationToken">取消令箭</param>
public virtual async ValueTask<OperResult> WriteValueAsync(string propertyName, object value, CancellationToken cancellationToken = default) public virtual ValueTask<OperResult> WriteValueAsync(string propertyName, object value, CancellationToken cancellationToken = default)
{ {
try try
{ {
GetVariableSources(); GetVariableSources();
if (string.IsNullOrEmpty(propertyName)) 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)) 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); JToken jToken = GetExpressionsValue(value, variableRuntimeProperty);
var result = await Device.WriteJTokenAsync(variableRuntimeProperty.VariableClass.RegisterAddress, jToken, variableRuntimeProperty.VariableClass.DataType, cancellationToken).ConfigureAwait(false); return Device.WriteJTokenAsync(variableRuntimeProperty.VariableClass.RegisterAddress, jToken, variableRuntimeProperty.VariableClass.DataType, cancellationToken);
return result;
} }
catch (Exception ex) catch (Exception ex)
{ {
return new OperResult(ex); return EasyValueTask.FromResult(new OperResult(ex));
} }
} }
/// <summary> /// <summary>

View File

@@ -8,6 +8,8 @@
// QQ群605534569 // QQ群605534569
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using PooledAwait;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using TouchSocket.Resources; using TouchSocket.Resources;
@@ -138,82 +140,86 @@ public class DDPTcpSessionClientChannel : TcpSessionClientChannel
private DeviceSingleStreamDataHandleAdapter<DDPTcpMessage> DDPAdapter = new(); private DeviceSingleStreamDataHandleAdapter<DDPTcpMessage> DDPAdapter = new();
protected override async ValueTask<bool> OnTcpReceiving(IBytesReader byteBlock) protected override ValueTask<bool> OnTcpReceiving(IBytesReader byteBlock)
{ {
if (DDPAdapter.TryParseRequest(ref byteBlock, out var message)) if (DDPAdapter.TryParseRequest(ref byteBlock, out var message))
{ {
return true; return EasyValueTask.FromResult(true);
} }
return OnTcpReceiving(this, message);
if (message != null) static async PooledValueTask<bool> OnTcpReceiving(DDPTcpSessionClientChannel @this, DDPTcpMessage message)
{ {
if (message.IsSuccess) if (message != null)
{ {
var id = $"ID={message.Id}"; if (message.IsSuccess)
if (message.Type == 0x09)
{ {
var reader = new ClassBytesReader(message.Content); var id = $"ID={message.Id}";
if (message.Type == 0x09)
if (this.DataHandlingAdapter == null)
{ {
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 else
{ {
await this.DataHandlingAdapter.ReceivedInputAsync(reader).ConfigureAwait(false); if (message.Type == 0x01)
}
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)
{ {
Logger?.Debug($"Old socket connections with the same ID {id} will be closed"); bool log = false;
try if (id != @this.Id) log = true;
{
//await oldClient.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false); //注册ID
await oldClient.CloseAsync().ConfigureAwait(false); if (@this.Service is ITcpServiceChannel tcpService && tcpService.TryGetClient(id, out var oldClient) && oldClient != @this)
}
catch
{
}
try
{
oldClient.Dispose();
}
catch
{ {
@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<byte>.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<byte>.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<byte>.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<byte>.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 #region Throw

View File

@@ -8,6 +8,8 @@
// QQ群605534569 // QQ群605534569
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using PooledAwait;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Net; using System.Net;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -80,73 +82,78 @@ public class DDPUdpSessionChannel : UdpSessionChannel, IClientChannel, IDtuUdpSe
} }
protected override async ValueTask<bool> OnUdpReceiving(UdpReceiveingEventArgs e) protected override ValueTask<bool> OnUdpReceiving(UdpReceiveingEventArgs e)
{ {
var byteBlock = e.Memory; var byteBlock = e.Memory;
var endPoint = e.EndPoint; var endPoint = e.EndPoint;
if (!DDPAdapter.TryParseRequest(endPoint, byteBlock, out var message)) 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<bool> OnUdpReceiving(DDPUdpSessionChannel @this, EndPoint endPoint, DDPUdpMessage message)
{ {
if (message.IsSuccess) if (message != null)
{ {
var id = $"ID={message.Id}"; if (message.IsSuccess)
if (message.Type == 0x09)
{ {
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 else
{ {
await this.DataHandlingAdapter.ReceivedInputAsync(endPoint, message.Content).ConfigureAwait(false); if (message.Type == 0x01)
} {
bool log = false;
return true; //注册ID
} if (!@this.IdDict.TryAdd(endPoint, id))
else {
{ @this.IdDict[endPoint] = id;
if (message.Type == 0x01) }
{ else
bool log = false; {
log = true;
}
if (!@this.EndPointDcit.TryAdd(id, endPoint))
{
@this.EndPointDcit[id] = endPoint;
}
else
{
log = true;
}
//注册ID //发送成功
if (!IdDict.TryAdd(endPoint, id)) await @this.DDPAdapter.SendInputAsync(endPoint, new DDPSend(ReadOnlyMemory<byte>.Empty, id, false, 0x81), @this.ClosedToken).ConfigureAwait(false);
{ if (log)
IdDict[endPoint] = id; @this.Logger?.Info(string.Format(AppResource.DtuConnected, id));
} }
else else if (message.Type == 0x02)
{ {
log = true; await @this.DDPAdapter.SendInputAsync(endPoint, new DDPSend(ReadOnlyMemory<byte>.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<byte>.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<byte>.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 #region Throw

View File

@@ -8,6 +8,8 @@
// QQ群605534569 // QQ群605534569
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using PooledAwait;
using System.Text; using System.Text;
using ThingsGateway.Foundation.Extension.String; using ThingsGateway.Foundation.Extension.String;
@@ -59,64 +61,71 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
public bool DtuIdHex { get; set; } public bool DtuIdHex { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public async Task OnTcpReceiving(ITcpSession client, BytesReaderEventArgs e) public Task OnTcpReceiving(ITcpSession client, BytesReaderEventArgs e)
{ {
var len = HeartbeatByte.Length; return OnTcpReceiving(this, client, e);
if (client is TcpSessionClientChannel socket && socket.Service is ITcpServiceChannel tcpServiceChannel)
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 (!socket.Id.StartsWith("ID="))
if (tcpServiceChannel.TryGetClient(id, out var oldClient)) {
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 try
{ {
await oldClient.CloseAsync().ConfigureAwait(false); await socket.CloseAsync().ConfigureAwait(false);
oldClient.Dispose(); socket.Dispose();
} }
catch 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)) await e.InvokeNext().ConfigureAwait(false);//如果本插件无法处理当前数据,请将数据转至下一个插件。
{ return;
try
{
await socket.CloseAsync().ConfigureAwait(false);
socket.Dispose();
}
catch
{
} }
await e.InvokeNext().ConfigureAwait(false);//如果本插件无法处理当前数据,请将数据转至下一个插件。 if (len > 0)
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 (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);//如果本插件无法处理当前数据,请将数据转至下一个插件。
} }

View File

@@ -167,14 +167,14 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
await e.InvokeNext().ConfigureAwait(false); await e.InvokeNext().ConfigureAwait(false);
} }
public async Task OnTcpReceiving(ITcpSession client, BytesReaderEventArgs e) public Task OnTcpReceiving(ITcpSession client, BytesReaderEventArgs e)
{ {
if (client is ITcpSessionClient) if (client is ITcpSessionClient)
{ {
return;//此处可判断,如果为服务器,则不用使用心跳。 return Task.CompletedTask;//此处可判断,如果为服务器,则不用使用心跳。
} }
if (DtuId.IsNullOrWhiteSpace()) return; if (DtuId.IsNullOrWhiteSpace()) return Task.CompletedTask;
if (client is ITcpClient tcpClient) if (client is ITcpClient tcpClient)
{ {
@@ -187,8 +187,9 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
e.Handled = true; e.Handled = true;
} }
} }
await e.InvokeNext().ConfigureAwait(false);//如果本插件无法处理当前数据,请将数据转至下一个插件。 return e.InvokeNext();//如果本插件无法处理当前数据,请将数据转至下一个插件。
} }
return Task.CompletedTask;
} }

View File

@@ -331,21 +331,25 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
} }
public bool AutoConnect { get; protected set; } = true; public bool AutoConnect { get; protected set; } = true;
/// <inheritdoc/> /// <inheritdoc/>
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) static async PooledTask SendAsync(DeviceBase @this, ISendMessage sendMessage, IClientChannel channel, CancellationToken token)
await Task.Delay(SendDelayTime, token).ConfigureAwait(false);
if (channel is IDtuUdpSessionChannel udpSession)
{ {
EndPoint? endPoint = GetUdpEndpoint(); if (@this.SendDelayTime != 0)
await udpSession.SendAsync(endPoint, sendMessage, token).ConfigureAwait(false); await Task.Delay(@this.SendDelayTime, token).ConfigureAwait(false);
} if (channel is IDtuUdpSessionChannel udpSession)
else {
{ EndPoint? endPoint = @this.GetUdpEndpoint();
await channel.SendAsync(sendMessage, token).ConfigureAwait(false); 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)); 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); try
if (AutoConnect && Channel != null && Channel?.Online != true)
{ {
if (Channel.PluginManager == null) await @this.connectWaitLock.WaitAsync(token).ConfigureAwait(false);
await Channel.SetupAsync(Channel.Config.Clone()).ConfigureAwait(false); if (@this.AutoConnect && @this.Channel != null && @this.Channel?.Online != true)
await Channel.CloseAsync().ConfigureAwait(false); {
using var ctsTime = new CancellationTokenSource(Channel.ChannelOptions.ConnectTimeout); if (@this.Channel.PluginManager == null)
using var cts = CancellationTokenSource.CreateLinkedTokenSource(ctsTime.Token, token); await @this.Channel.SetupAsync(@this.Channel.Config.Clone()).ConfigureAwait(false);
await Channel.ConnectAsync(cts.Token).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();
} }
} }
} }
/// <inheritdoc/> /// <inheritdoc/>
public virtual async ValueTask<OperResult> SendAsync(ISendMessage sendMessage, CancellationToken cancellationToken) public virtual ValueTask<OperResult> SendAsync(ISendMessage sendMessage, CancellationToken cancellationToken)
{ {
try return SendAsync(this, sendMessage, cancellationToken);
{
var channelResult = GetChannel();
if (!channelResult.IsSuccess) return new OperResult<byte[]>(channelResult);
WaitLock? waitLock = GetWaitLock(channelResult.Content);
static async PooledValueTask<OperResult> SendAsync(DeviceBase @this, ISendMessage sendMessage, CancellationToken cancellationToken)
{
try try
{ {
await BeforeSendAsync(channelResult.Content, cancellationToken).ConfigureAwait(false); var channelResult = @this.GetChannel();
if (!channelResult.IsSuccess) return new OperResult<byte[]>(channelResult);
WaitLock? waitLock = @this.GetWaitLock(channelResult.Content);
await waitLock.WaitAsync(cancellationToken).ConfigureAwait(false); try
channelResult.Content.SetDataHandlingAdapterLogger(Logger); {
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); await @this.SendAsync(sendMessage, channelResult.Content, cancellationToken).ConfigureAwait(false);
return OperResult.Success; return OperResult.Success;
}
finally
{
waitLock.Release();
}
} }
finally catch (Exception ex)
{ {
waitLock.Release(); return new(ex);
} }
} }
catch (Exception ex)
{
return new(ex);
}
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -531,7 +545,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
} }
private ObjectPoolLock<ReusableCancellationTokenSource> _reusableTimeouts = new(); private ObjectPoolLock<ReusableCancellationTokenSource> _reusableTimeouts = new();
/// <summary> /// <summary>
/// 发送并等待数据 /// 发送并等待数据
/// </summary> /// </summary>
@@ -568,8 +582,8 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
try try
{ {
var cts = reusableTimeout.GetTokenSource(timeout, cancellationToken, @this.Channel.ClosedToken); var ctsToken = reusableTimeout.GetTokenSource(timeout, cancellationToken, @this.Channel.ClosedToken);
await waitData.WaitAsync(cts.Token).ConfigureAwait(false); await waitData.WaitAsync(ctsToken).ConfigureAwait(false);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
@@ -659,54 +673,59 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
} }
/// <inheritdoc/> /// <inheritdoc/>
public virtual async ValueTask<OperResult> WriteJTokenAsync(string address, JToken value, DataTypeEnum dataType, CancellationToken cancellationToken = default) public virtual ValueTask<OperResult> WriteJTokenAsync(string address, JToken value, DataTypeEnum dataType, CancellationToken cancellationToken = default)
{ {
try return WriteJTokenAsync(this, address, value, dataType, cancellationToken);
static async PooledValueTask<OperResult> WriteJTokenAsync(DeviceBase @this, string address, JToken value, DataTypeEnum dataType, CancellationToken cancellationToken)
{ {
var bitConverter = ThingsGatewayBitConverter.GetTransByAddress(address); try
if (value is JArray jArray)
{ {
return dataType switch var bitConverter = @this.ThingsGatewayBitConverter.GetTransByAddress(address);
if (value is JArray jArray)
{ {
DataTypeEnum.String => await WriteAsync(address, jArray.ToObject<String[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), return dataType switch
DataTypeEnum.Boolean => await WriteAsync(address, jArray.ToObject<Boolean[]>().AsMemory(), cancellationToken).ConfigureAwait(false), {
DataTypeEnum.Byte => await WriteAsync(address, jArray.ToObject<Byte[]>().AsMemory(), dataType, cancellationToken).ConfigureAwait(false), DataTypeEnum.String => await @this.WriteAsync(address, jArray.ToObject<String[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
DataTypeEnum.Int16 => await WriteAsync(address, jArray.ToObject<Int16[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), DataTypeEnum.Boolean => await @this.WriteAsync(address, jArray.ToObject<Boolean[]>().AsMemory(), cancellationToken).ConfigureAwait(false),
DataTypeEnum.UInt16 => await WriteAsync(address, jArray.ToObject<UInt16[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), DataTypeEnum.Byte => await @this.WriteAsync(address, jArray.ToObject<Byte[]>().AsMemory(), dataType, cancellationToken).ConfigureAwait(false),
DataTypeEnum.Int32 => await WriteAsync(address, jArray.ToObject<Int32[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), DataTypeEnum.Int16 => await @this.WriteAsync(address, jArray.ToObject<Int16[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
DataTypeEnum.UInt32 => await WriteAsync(address, jArray.ToObject<UInt32[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), DataTypeEnum.UInt16 => await @this.WriteAsync(address, jArray.ToObject<UInt16[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
DataTypeEnum.Int64 => await WriteAsync(address, jArray.ToObject<Int64[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), DataTypeEnum.Int32 => await @this.WriteAsync(address, jArray.ToObject<Int32[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
DataTypeEnum.UInt64 => await WriteAsync(address, jArray.ToObject<UInt64[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), DataTypeEnum.UInt32 => await @this.WriteAsync(address, jArray.ToObject<UInt32[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
DataTypeEnum.Float => await WriteAsync(address, jArray.ToObject<Single[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), DataTypeEnum.Int64 => await @this.WriteAsync(address, jArray.ToObject<Int64[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
DataTypeEnum.Double => await WriteAsync(address, jArray.ToObject<Double[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), DataTypeEnum.UInt64 => await @this.WriteAsync(address, jArray.ToObject<UInt64[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
DataTypeEnum.Decimal => await WriteAsync(address, jArray.ToObject<Decimal[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), DataTypeEnum.Float => await @this.WriteAsync(address, jArray.ToObject<Single[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
_ => new OperResult(string.Format(AppResource.DataTypeNotSupported, dataType)), DataTypeEnum.Double => await @this.WriteAsync(address, jArray.ToObject<Double[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
}; DataTypeEnum.Decimal => await @this.WriteAsync(address, jArray.ToObject<Decimal[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
_ => new OperResult(string.Format(AppResource.DataTypeNotSupported, dataType)),
};
}
else
{
return dataType switch
{
DataTypeEnum.String => await @this.WriteAsync(address, value.ToObject<String>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.Boolean => await @this.WriteAsync(address, value.ToObject<Boolean>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.Byte => await @this.WriteAsync(address, value.ToObject<Byte>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.Int16 => await @this.WriteAsync(address, value.ToObject<Int16>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.UInt16 => await @this.WriteAsync(address, value.ToObject<UInt16>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.Int32 => await @this.WriteAsync(address, value.ToObject<Int32>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.UInt32 => await @this.WriteAsync(address, value.ToObject<UInt32>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.Int64 => await @this.WriteAsync(address, value.ToObject<Int64>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.UInt64 => await @this.WriteAsync(address, value.ToObject<UInt64>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.Float => await @this.WriteAsync(address, value.ToObject<Single>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.Double => await @this.WriteAsync(address, value.ToObject<Double>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.Decimal => await @this.WriteAsync(address, value.ToObject<Decimal>(), bitConverter, cancellationToken).ConfigureAwait(false),
_ => new OperResult(string.Format(AppResource.DataTypeNotSupported, dataType)),
};
}
} }
else catch (Exception ex)
{ {
return dataType switch return new OperResult(ex);
{
DataTypeEnum.String => await WriteAsync(address, value.ToObject<String>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.Boolean => await WriteAsync(address, value.ToObject<Boolean>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.Byte => await WriteAsync(address, value.ToObject<Byte>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.Int16 => await WriteAsync(address, value.ToObject<Int16>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.UInt16 => await WriteAsync(address, value.ToObject<UInt16>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.Int32 => await WriteAsync(address, value.ToObject<Int32>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.UInt32 => await WriteAsync(address, value.ToObject<UInt32>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.Int64 => await WriteAsync(address, value.ToObject<Int64>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.UInt64 => await WriteAsync(address, value.ToObject<UInt64>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.Float => await WriteAsync(address, value.ToObject<Single>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.Double => await WriteAsync(address, value.ToObject<Double>(), bitConverter, cancellationToken).ConfigureAwait(false),
DataTypeEnum.Decimal => await WriteAsync(address, value.ToObject<Decimal>(), bitConverter, cancellationToken).ConfigureAwait(false),
_ => new OperResult(string.Format(AppResource.DataTypeNotSupported, dataType)),
};
} }
} }
catch (Exception ex)
{
return new OperResult(ex);
}
} }
#endregion #endregion

View File

@@ -8,6 +8,8 @@
// QQ群605534569 // QQ群605534569
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using PooledAwait;
using TouchSocket.Core; using TouchSocket.Core;
namespace ThingsGateway.Gateway.Application; namespace ThingsGateway.Gateway.Application;
@@ -28,22 +30,26 @@ public class AsyncReadWriteLock : IAsyncDisposable
/// <summary> /// <summary>
/// 获取读锁,支持多个线程并发读取,但写入时会阻止所有读取。 /// 获取读锁,支持多个线程并发读取,但写入时会阻止所有读取。
/// </summary> /// </summary>
public async ValueTask<CancellationToken> ReaderLockAsync(CancellationToken cancellationToken) public ValueTask<CancellationToken> ReaderLockAsync(CancellationToken cancellationToken)
{ {
return ReaderLockAsync(this, cancellationToken);
if (Interlocked.Read(ref _writerCount) > 0) static async PooledValueTask<CancellationToken> 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; public bool WriteWaited => _writerCount > 0;
@@ -51,21 +57,25 @@ public class AsyncReadWriteLock : IAsyncDisposable
/// <summary> /// <summary>
/// 获取写锁,阻止所有读取。 /// 获取写锁,阻止所有读取。
/// </summary> /// </summary>
public async ValueTask<IDisposable> WriterLockAsync(CancellationToken cancellationToken) public ValueTask<IDisposable> WriterLockAsync(CancellationToken cancellationToken)
{ {
return WriterLockAsync(this);
if (Interlocked.Increment(ref _writerCount) == 1) static async PooledValueTask<IDisposable> WriterLockAsync(AsyncReadWriteLock @this)
{ {
if (_writePriority) if (Interlocked.Increment(ref @this._writerCount) == 1)
{ {
var cancellationTokenSource = _cancellationTokenSource; if (@this._writePriority)
_cancellationTokenSource = new(); {
await cancellationTokenSource.SafeCancelAsync().ConfigureAwait(false); // 取消读取 var cancellationTokenSource = @this._cancellationTokenSource;
cancellationTokenSource.SafeDispose(); @this._cancellationTokenSource = new();
await cancellationTokenSource.SafeCancelAsync().ConfigureAwait(false); // 取消读取
cancellationTokenSource.SafeDispose();
}
} }
}
return new Writer(this); return new Writer(@this);
}
} }
private object lockObject = new(); private object lockObject = new();
private void ReleaseWriter() private void ReleaseWriter()

View File

@@ -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
//{
// /// <summary>
// /// 取消令牌
// /// </summary>
// private CancellationTokenSource? _cancelTokenSource;
// private object? _state;
// public DoTask(Func<object?, CancellationToken, Task> doWork, ILog logger, object? state = null, string taskName = null)
// {
// DoWork = doWork; Logger = logger; TaskName = taskName; _state = state;
// }
// /// <summary>
// /// 执行任务方法
// /// </summary>
// public Func<object?, CancellationToken, Task> DoWork { get; }
// private ILog Logger { get; }
// private Task PrivateTask { get; set; }
// private string TaskName { get; }
// /// <summary>
// /// 开始
// /// </summary>
// /// <param name="cancellationToken">调度取消令牌</param>
// 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();
// /// <summary>
// /// 停止操作
// /// </summary>
// 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();
// }
// }
//}

View File

@@ -20,6 +20,8 @@ using ThingsGateway.Common.Extension;
using ThingsGateway.Extension.Generic; using ThingsGateway.Extension.Generic;
#if !Management #if !Management
using ThingsGateway.Gateway.Application.Extensions; using ThingsGateway.Gateway.Application.Extensions;
using ThingsGateway.NewLife;
#endif #endif
using ThingsGateway.NewLife.Json.Extension; using ThingsGateway.NewLife.Json.Extension;
using ThingsGateway.NewLife.Threading; using ThingsGateway.NewLife.Threading;
@@ -292,69 +294,75 @@ public abstract partial class CollectBase : DriverBase
#region #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) if (Pause)
return; return ValueTask.CompletedTask;
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
return; return ValueTask.CompletedTask;
return ReadVariableMed(this, readVariableMethods, cancellationToken);
var readErrorCount = 0; static async PooledValueTask ReadVariableMed(CollectBase @this, VariableMethod readVariableMethods, CancellationToken cancellationToken)
//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)
{ {
if (Pause)
return;
if (cancellationToken.IsCancellationRequested)
return;
readErrorCount++; var readErrorCount = 0;
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
LogMessage?.Trace(string.Format("{0} - Execute method [{1}] - failed - {2}", DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage));
//if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) //if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
// LogMessage?.Trace(string.Format("{0} - Executing method [{1}]", DeviceName, readVariableMethods.MethodInfo.Name)); // 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) // 方法调用失败时重试一定次数
{ while (!readResult.IsSuccess && readErrorCount < @this.CollectProperties.RetryCount)
// 方法调用成功时记录日志并增加成功计数器
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)
{ {
if (!cancellationToken.IsCancellationRequested) if (@this.Pause)
LogMessage?.LogWarning(readResult.Exception, string.Format(AppResource.MethodFail, DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage)); 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 else
{ {
if (!cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
return;
// 方法调用失败时记录日志并增加失败计数器,更新错误信息
if (readVariableMethods.LastErrorMessage != readResult.ErrorMessage)
{ {
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) if (!cancellationToken.IsCancellationRequested)
LogMessage?.Trace(string.Format("{0} - Execute method [{1}] - failed - {2}", DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage)); @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; return;
CurrentDevice.SetDeviceStatus(TimerX.Now, null);
} }
return;
} }
#endregion #endregion
@@ -674,9 +682,9 @@ public abstract partial class CollectBase : DriverBase
#endregion #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) protected void ScriptVariableRun(object? state, CancellationToken cancellationToken)
@@ -727,38 +735,43 @@ public abstract partial class CollectBase : DriverBase
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
protected async Task Check(Dictionary<VariableRuntime, JToken> writeInfoLists, NonBlockingDictionary<string, OperResult> operResults, CancellationToken cancellationToken) protected Task Check(Dictionary<VariableRuntime, JToken> writeInfoLists, NonBlockingDictionary<string, OperResult> operResults, CancellationToken cancellationToken)
{ {
if (VariableSourceReadsEnable) return Check(this, writeInfoLists, operResults, cancellationToken);
static async PooledTask Check(CollectBase @this, Dictionary<VariableRuntime, JToken> writeInfoLists, NonBlockingDictionary<string, OperResult> operResults, CancellationToken cancellationToken)
{ {
// 如果成功,每个变量都读取一次最新值,再次比较写入值 if (@this.VariableSourceReadsEnable)
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) =>
{ {
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)
{ {
// 如果写入值与读取值不同,则更新操作结果为失败 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}"); {
// 如果写入值与读取值不同,则更新操作结果为失败
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
else
{
foreach (var item in varRead)
{ {
// 如果写入值与读取值不同,则更新操作结果为失败 foreach (var item in varRead)
operResults[item.Name] = new OperResult($"Reading and rechecking resulted in an error: {result.ErrorMessage}", result.Exception); {
// 如果写入值与读取值不同,则更新操作结果为失败
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
/// <param name="writeInfoLists">要写入的变量及其对应的数据</param> /// <param name="writeInfoLists">要写入的变量及其对应的数据</param>
/// <param name="cancellationToken">取消操作的通知</param> /// <param name="cancellationToken">取消操作的通知</param>
/// <returns>写入操作的结果字典</returns> /// <returns>写入操作的结果字典</returns>
public async ValueTask<Dictionary<string, Dictionary<string, IOperResult>>> InvokeMethodAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken) public ValueTask<Dictionary<string, Dictionary<string, IOperResult>>> InvokeMethodAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
{ {
// 初始化结果字典 return InvokeMethodAsync(this, writeInfoLists, cancellationToken);
Dictionary<string, OperResult<object>> results = new Dictionary<string, OperResult<object>>();
// 遍历写入信息列表 static async PooledValueTask<Dictionary<string, Dictionary<string, IOperResult>>> InvokeMethodAsync(CollectBase @this, Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
foreach (var (deviceVariable, jToken) in writeInfoLists)
{ {
// 检查是否有写入表达式 // 初始化结果字典
if (!string.IsNullOrEmpty(deviceVariable.WriteExpressions)) Dictionary<string, OperResult<object>> results = new Dictionary<string, OperResult<object>>();
// 遍历写入信息列表
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<object>(string.Format(AppResource.WriteExpressionsError, deviceVariable.Name, deviceVariable.WriteExpressions, ex.Message), ex));
}
}
}
NonBlockingDictionary<string, OperResult<object>> 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 try
{ {
// 根据写入表达式转换数据 // 调用协议的写入方法,将写入信息中的数据写入到对应的寄存器地址,并获取操作结果
object data = deviceVariable.WriteExpressions.GetExpressionsResult(rawdata, LogMessage); var result = await @this.InvokeMethodAsync(writeInfo.Key.VariableMethod, writeInfo.Value?.ToString(), false, cancellationToken).ConfigureAwait(false);
// 将转换后的数据重新赋值给写入信息列表
writeInfoLists[deviceVariable] = JTokenUtil.GetJTokenFromObj(data); // 将操作结果添加到结果字典中,使用变量名称作为键
operResults.TryAdd(writeInfo.Key.Name, result);
} }
catch (Exception ex) catch (Exception ex)
{ {
// 如果转换失败,则记录错误信息 operResults.TryAdd(writeInfo.Key.Name, new(ex));
results.Add(deviceVariable.Name, new OperResult<object>(string.Format(AppResource.WriteExpressionsError, deviceVariable.Name, deviceVariable.WriteExpressions, ex.Message), ex));
} }
} }, @this.CollectProperties.MaxConcurrentCount, cancellationToken).ConfigureAwait(false);
}
NonBlockingDictionary<string, OperResult<object>> operResults = new(); // 将转换失败的变量和写入成功的变量的操作结果合并到结果字典中
return new Dictionary<string, Dictionary<string, IOperResult>>()
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<string, Dictionary<string, IOperResult>>()
{ {
{ {
DeviceName , @this.DeviceName ,
results.Concat(operResults).ToDictionary(a => a.Key, a => (IOperResult)a.Value) results.Concat(operResults).ToDictionary(a => a.Key, a => (IOperResult)a.Value)
} }
}; };
}
} }
/// <summary> /// <summary>
@@ -838,58 +856,63 @@ public abstract partial class CollectBase : DriverBase
/// <param name="writeInfoLists">要写入的变量及其对应的数据</param> /// <param name="writeInfoLists">要写入的变量及其对应的数据</param>
/// <param name="cancellationToken">取消操作的通知</param> /// <param name="cancellationToken">取消操作的通知</param>
/// <returns>写入操作的结果字典</returns> /// <returns>写入操作的结果字典</returns>
public async ValueTask<Dictionary<string, Dictionary<string, IOperResult>>> InVokeWriteAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken) public ValueTask<Dictionary<string, Dictionary<string, IOperResult>>> InVokeWriteAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
{ {
// 初始化结果字典 return InVokeWriteAsync(this, writeInfoLists, cancellationToken);
Dictionary<string, OperResult> results = new Dictionary<string, OperResult>();
// 遍历写入信息列表 static async PooledValueTask<Dictionary<string, Dictionary<string, IOperResult>>> InVokeWriteAsync(CollectBase @this, Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
foreach (var (deviceVariable, jToken) in writeInfoLists)
{ {
// 检查是否有写入表达式 // 初始化结果字典
if (!string.IsNullOrEmpty(deviceVariable.WriteExpressions)) Dictionary<string, OperResult> results = new Dictionary<string, OperResult>();
// 遍历写入信息列表
foreach (var (deviceVariable, jToken) in writeInfoLists)
{ {
// 提取原始数据 // 检查是否有写入表达式
object rawdata = jToken.GetObjectFromJToken(); if (!string.IsNullOrEmpty(deviceVariable.WriteExpressions))
try
{ {
// 根据写入表达式转换数据 // 提取原始数据
object data = deviceVariable.WriteExpressions.GetExpressionsResult(rawdata, LogMessage); object rawdata = jToken.GetObjectFromJToken();
// 将转换后的数据重新赋值给写入信息列表 try
writeInfoLists[deviceVariable] = JTokenUtil.GetJTokenFromObj(data); {
} // 根据写入表达式转换数据
catch (Exception ex) object data = deviceVariable.WriteExpressions.GetExpressionsResult(rawdata, @this.LogMessage);
{ // 将转换后的数据重新赋值给写入信息列表
// 如果转换失败,则记录错误信息 writeInfoLists[deviceVariable] = JTokenUtil.GetJTokenFromObj(data);
results.Add(deviceVariable.Name, new OperResult(string.Format(AppResource.WriteExpressionsError, deviceVariable.Name, deviceVariable.WriteExpressions, ex.Message), ex)); }
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 writePList = writeInfoLists.Where(a => !@this.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 writeSList = writeInfoLists.Where(a => @this.CurrentDevice.VariableScriptReads.Select(a => a.VariableRuntime).Any(b => a.Key.Name == b.Name));
DateTime now = DateTime.Now; DateTime now = DateTime.Now;
foreach (var item in writeSList) foreach (var item in writeSList)
{ {
results.TryAdd(item.Key.Name, item.Key.SetValue(item.Value, now)); results.TryAdd(item.Key.Name, item.Key.SetValue(item.Value, now));
} }
// 过滤掉转换失败的变量,只保留写入成功的变量进行写入操作 // 过滤掉转换失败的变量,只保留写入成功的变量进行写入操作
var results1 = await WriteValuesAsync(writePList var results1 = await @this.WriteValuesAsync(writePList
.Where(a => !results.Any(b => b.Key == a.Key.Name)) .Where(a => !results.Any(b => b.Key == a.Key.Name))
.ToDictionary(item => item.Key, item => item.Value), .ToDictionary(item => item.Key, item => item.Value),
cancellationToken).ConfigureAwait(false); cancellationToken).ConfigureAwait(false);
// 将转换失败的变量和写入成功的变量的操作结果合并到结果字典中 // 将转换失败的变量和写入成功的变量的操作结果合并到结果字典中
return new Dictionary<string, Dictionary<string, IOperResult>>() return new Dictionary<string, Dictionary<string, IOperResult>>()
{ {
{ {
DeviceName , @this. DeviceName ,
results.Concat(results1).ToDictionary(a => a.Key, a => (IOperResult)a.Value) results.Concat(results1).ToDictionary(a => a.Key, a => (IOperResult)a.Value)
} }
}; };
}
} }
/// <summary> /// <summary>
@@ -900,56 +923,61 @@ public abstract partial class CollectBase : DriverBase
/// <param name="isRead">指示是否为读取操作</param> /// <param name="isRead">指示是否为读取操作</param>
/// <param name="cancellationToken">取消操作的通知</param> /// <param name="cancellationToken">取消操作的通知</param>
/// <returns>操作结果,包含执行方法的结果</returns> /// <returns>操作结果,包含执行方法的结果</returns>
protected virtual async ValueTask<OperResult<object>> InvokeMethodAsync(VariableMethod variableMethod, string? value = null, bool isRead = true, CancellationToken cancellationToken = default) protected virtual ValueTask<OperResult<object>> InvokeMethodAsync(VariableMethod variableMethod, string? value = null, bool isRead = true, CancellationToken cancellationToken = default)
{ {
try return InvokeMethodAsync(this, variableMethod, value, isRead, cancellationToken);
static async PooledValueTask<OperResult<object>> InvokeMethodAsync(CollectBase @this, VariableMethod variableMethod, string? value, bool isRead, CancellationToken cancellationToken)
{ {
// 初始化操作结果 try
OperResult<object> result = new OperResult<object>();
// 获取要执行的方法
var method = variableMethod.MethodInfo;
// 如果方法未找到,则返回错误结果
if (method == null)
{ {
result.OperCode = 999; // 初始化操作结果
result.ErrorMessage = string.Format(AppResource.MethodNotNull, variableMethod.Variable.Name, variableMethod.Variable.OtherMethod); OperResult<object> result = new OperResult<object>();
return result;
}
else
{
// 调用方法并获取结果
var data = await variableMethod.InvokeMethodAsync(this, value, cancellationToken).ConfigureAwait(false);
result = data.GetOperResult(); // 获取要执行的方法
var method = variableMethod.MethodInfo;
// 如果方法有返回值,并且是读取操作 // 如果方法未找到,则返回错误结果
if (method.HasReturn && isRead) if (method == null)
{ {
var time = DateTime.Now; result.OperCode = 999;
if (result.IsSuccess == true) result.ErrorMessage = string.Format(AppResource.MethodNotNull, variableMethod.Variable.Name, variableMethod.Variable.OtherMethod);
{ return result;
// 将结果序列化并设置到变量中 }
var variableResult = variableMethod.Variable.SetValue(result.Content, time); else
if (!variableResult.IsSuccess) {
variableMethod.LastErrorMessage = result.ErrorMessage; // 调用方法并获取结果
} var data = await variableMethod.InvokeMethodAsync(@this, value, cancellationToken).ConfigureAwait(false);
else
{ result = data.GetOperResult();
// 如果读取操作失败,则将变量标记为离线
var variableResult = variableMethod.Variable.SetValue(null, time, isOnline: false); // 如果方法有返回值,并且是读取操作
if (!variableResult.IsSuccess) if (method.HasReturn && isRead)
variableMethod.LastErrorMessage = result.ErrorMessage; {
} 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)
catch (Exception ex) {
{ // 捕获异常并返回错误结果
// 捕获异常并返回错误结果 return new OperResult<object>(ex);
return new OperResult<object>(ex); }
} }
} }

View File

@@ -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; if (!@this.FoundationDevice.OnLine)
Exception exception = null;
try
{ {
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; if (!cancellationToken.IsCancellationRequested)
await FoundationDevice.ConnectAsync(cancellationToken).ConfigureAwait(false);
if (CurrentDevice.DeviceStatusChangeTime < TimerX.Now.AddMinutes(-1))
{ {
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 (OperationCanceledException)
{
}
catch (Exception ex)
{
exception = ex;
}
if (cancellationToken.IsCancellationRequested)
{
return;
}
if (FoundationDevice.OnLine == false && exception != null)
{
foreach (var item in CurrentDevice.VariableSourceReads)
{ {
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) exception = ex;
{
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);
} }
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
/// 批量写入变量值,需返回变量名称/结果,注意非通用设备需重写 /// 批量写入变量值,需返回变量名称/结果,注意非通用设备需重写
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
protected override async ValueTask<Dictionary<string, OperResult>> WriteValuesAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken) protected override ValueTask<Dictionary<string, OperResult>> WriteValuesAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
{ {
using var writeLock = await ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false); return WriteValuesAsync(this, writeInfoLists, cancellationToken);
// 检查协议是否为空,如果为空则抛出异常
if (FoundationDevice == null)
throw new NotSupportedException();
// 创建用于存储操作结果的并发字典 static async PooledValueTask<Dictionary<string, OperResult>> WriteValuesAsync(CollectFoundationBase @this, Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
NonBlockingDictionary<string, OperResult> operResults = new();
// 使用并发方式遍历写入信息列表,并进行异步写入操作
await writeInfoLists.ParallelForEachAsync(async (writeInfo, cancellationToken) =>
{ {
try using var writeLock = await @this.ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false);
// 检查协议是否为空,如果为空则抛出异常
if (@this.FoundationDevice == null)
throw new NotSupportedException();
// 创建用于存储操作结果的并发字典
NonBlockingDictionary<string, OperResult> operResults = new();
// 使用并发方式遍历写入信息列表,并进行异步写入操作
await writeInfoLists.ParallelForEachAsync(async (writeInfo, cancellationToken) =>
{ {
try
// 调用协议的写入方法,将写入信息中的数据写入到对应的寄存器地址,并获取操作结果
var result = await FoundationDevice.WriteJTokenAsync(writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType, cancellationToken).ConfigureAwait(false);
if (result.IsSuccess)
{ {
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));
} }
// 将操作结果添加到结果字典中,使用变量名称作为键 }, @this.CollectProperties.MaxConcurrentCount, cancellationToken).ConfigureAwait(false);
operResults.TryAdd(writeInfo.Key.Name, result);
}
catch (Exception ex)
{
operResults.TryAdd(writeInfo.Key.Name, new(ex));
}
}, CollectProperties.MaxConcurrentCount, cancellationToken).ConfigureAwait(false);
await Check(writeInfoLists, operResults, cancellationToken).ConfigureAwait(false); await @this.Check(writeInfoLists, operResults, cancellationToken).ConfigureAwait(false);
// 返回包含操作结果的字典 // 返回包含操作结果的字典
return new Dictionary<string, OperResult>(operResults); return new Dictionary<string, OperResult>(operResults);
}
} }

View File

@@ -10,6 +10,8 @@
using BootstrapBlazor.Components; using BootstrapBlazor.Components;
using PooledAwait;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using ThingsGateway.Extension.Generic; using ThingsGateway.Extension.Generic;
@@ -89,38 +91,63 @@ public static class GlobalData
public static event PluginEventHandler? PluginEventHandler; public static event PluginEventHandler? PluginEventHandler;
public static async Task<IEnumerable<ChannelRuntime>> GetCurrentUserChannels() public static Task<IEnumerable<ChannelRuntime>> GetCurrentUserChannels()
{ {
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false); return GetCurrentUserChannels();
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); static async PooledTask<IEnumerable<ChannelRuntime>> 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<IEnumerable<DeviceRuntime>> GetCurrentUserDevices() public static Task<IEnumerable<DeviceRuntime>> GetCurrentUserDevices()
{ {
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false); return GetCurrentUserDevices();
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); static async PooledTask<IEnumerable<DeviceRuntime>> 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<IEnumerable<VariableRuntime>> GetCurrentUserIdVariables() public static Task<IEnumerable<VariableRuntime>> GetCurrentUserIdVariables()
{ {
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false); return GetCurrentUserIdVariables();
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); static async PooledTask<IEnumerable<VariableRuntime>> 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<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariablesAsync() public static Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariablesAsync()
{ {
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false); return GetCurrentUserRealAlarmVariablesAsync();
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); static async PooledTask<IEnumerable<AlarmVariable>> 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<IEnumerable<VariableRuntime>> GetCurrentUserAlarmEnableVariables() public static Task<IEnumerable<VariableRuntime>> GetCurrentUserAlarmEnableVariables()
{ {
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false); return GetCurrentUserAlarmEnableVariables();
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); static async PooledTask<IEnumerable<VariableRuntime>> 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) public static bool ContainsVariable(long businessDeviceId, VariableRuntime a)

View File

@@ -8,6 +8,8 @@
// QQ群605534569 // QQ群605534569
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using PooledAwait;
using TouchSocket.Core; using TouchSocket.Core;
namespace ThingsGateway.Gateway.Application; namespace ThingsGateway.Gateway.Application;
@@ -51,42 +53,47 @@ public class VariableMethod
/// <param name="value">以,逗号分割的参数</param> /// <param name="value">以,逗号分割的参数</param>
/// <param name="cancellationToken">取消令箭</param> /// <param name="cancellationToken">取消令箭</param>
/// <returns></returns> /// <returns></returns>
public async ValueTask<IOperResult> InvokeMethodAsync(object driverBase, string? value = null, CancellationToken cancellationToken = default) public ValueTask<IOperResult> InvokeMethodAsync(object driverBase, string? value = null, CancellationToken cancellationToken = default)
{ {
try return InvokeMethodAsync(this, driverBase, value, cancellationToken);
static async PooledValueTask<IOperResult> InvokeMethodAsync(VariableMethod @this, object driverBase, string? value, CancellationToken cancellationToken)
{ {
object?[]? os = null; try
if (value == null && OS == null)
{ {
//默认的参数 object?[]? os = null;
var addresss = Variable.RegisterAddress.SplitOS(); if (value == null && @this.OS == null)
//通过逗号分割,并且合并参数 {
var strs = addresss; //默认的参数
var addresss = @this.Variable.RegisterAddress.SplitOS();
//通过逗号分割,并且合并参数
var strs = addresss;
OS = GetOS(strs, cancellationToken); @this.OS = @this.GetOS(strs, cancellationToken);
os = OS; 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(); return new OperResult(ex);
var values = value.SplitOS();
//通过分号分割,并且合并参数
var strs = addresss.Concat(values).ToList();
os = GetOS(strs, cancellationToken);
} }
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);
} }
} }

View File

@@ -12,6 +12,8 @@ using BootstrapBlazor.Components;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using PooledAwait;
using Riok.Mapperly.Abstractions; using Riok.Mapperly.Abstractions;
@@ -448,13 +450,18 @@ public partial class VariableRuntime : Variable
} }
/// <inheritdoc/> /// <inheritdoc/>
public virtual async ValueTask<IOperResult> RpcAsync(string value, string executive = "brower", CancellationToken cancellationToken = default) public virtual ValueTask<IOperResult> RpcAsync(string value, string executive = "brower", CancellationToken cancellationToken = default)
{ {
var data = await GlobalData.RpcService.InvokeDeviceMethodAsync(executive, new Dictionary<string, Dictionary<string, string>>() return RpcAsync(DeviceName, Name, value, executive, cancellationToken);
static async PooledValueTask<IOperResult> RpcAsync(string deviceName, string name, string value, string executive, CancellationToken cancellationToken)
{ {
{ DeviceName, new Dictionary<string, string>() { { Name,value} } } var data = await GlobalData.RpcService.InvokeDeviceMethodAsync(executive, new Dictionary<string, Dictionary<string, string>>()
{
{ deviceName, new Dictionary<string, string>() { { name,value} } }
}, cancellationToken).ConfigureAwait(false); }, cancellationToken).ConfigureAwait(false);
return data.FirstOrDefault().Value.FirstOrDefault().Value; return data.FirstOrDefault().Value.FirstOrDefault().Value;
}
} }
public void SetErrorMessage(string lastErrorMessage) public void SetErrorMessage(string lastErrorMessage)

View File

@@ -13,6 +13,8 @@ using BootstrapBlazor.Components;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using PooledAwait;
using ThingsGateway.Blazor.Diagrams.Core; using ThingsGateway.Blazor.Diagrams.Core;
using ThingsGateway.Blazor.Diagrams.Core.Models; using ThingsGateway.Blazor.Diagrams.Core.Models;
using ThingsGateway.NewLife; 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) return Analysis(targetNode, input, rulesLog, cancellationToken);
{
node.Logger = rulesLog.Log;
node.RulesEngineName = rulesLog.Rules.Name;
}
try
static async PooledTask Analysis(NodeModel targetNode, NodeInput input, RulesLog rulesLog, CancellationToken cancellationToken)
{ {
if (targetNode == null) if (targetNode is INode node)
return;
if (targetNode is IConditionNode conditionNode)
{ {
var next = await conditionNode.ExecuteAsync(input, cancellationToken).ConfigureAwait(false); node.Logger = rulesLog.Log;
if (next) 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)
else if (targetNode is IExpressionNode expressionNode)
{
var nodeOutput = await expressionNode.ExecuteAsync(input, cancellationToken).ConfigureAwait(false);
if (nodeOutput.IsSuccess)
{ {
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)
else if (targetNode is IActuatorNode actuatorNode)
{
var nodeOutput = await actuatorNode.ExecuteAsync(input, cancellationToken).ConfigureAwait(false);
if (nodeOutput.IsSuccess)
{ {
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)
else if (targetNode is ITriggerNode triggerNode)
{
Func<NodeOutput, CancellationToken, Task> func = (async (a, token) =>
{ {
foreach (var link in targetNode.PortLinks.Where(a => ((a.Target.Model as PortModel)?.Parent) != targetNode)) Func<NodeOutput, CancellationToken, Task> func = (async (a, token) =>
{ {
await Analysis((link.Target.Model as PortModel)?.Parent, new NodeInput() { Value = a.Value }, rulesLog, token).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); }
});
await triggerNode.StartAsync(func, cancellationToken).ConfigureAwait(false);
}
} }
} catch (TaskCanceledException) { }
catch (TaskCanceledException) { } catch (OperationCanceledException) { }
catch (OperationCanceledException) { } catch (Exception ex)
catch (Exception ex) {
{ rulesLog.Log?.LogWarning(ex);
rulesLog.Log?.LogWarning(ex); }
return;
} }
} }

View File

@@ -321,6 +321,33 @@ public partial class ChannelDeviceTree
return "enable--text"; 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] [Inject]
DialogService DialogService { get; set; } DialogService DialogService { get; set; }
@@ -1617,7 +1644,7 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
List<string> ret = new(jsstring.Count); List<string> ret = new(jsstring.Count);
foreach (var str in jsstring) foreach (var str in jsstring)
{ {
var item = ChannelDeviceTreeItem.FromJSString(str); var item = ChannelDeviceTreeItemStruct.FromJSString(str);
ret.Add(GetClass(item)); ret.Add(GetClass(item));
} }
return ret; return ret;

View File

@@ -149,7 +149,75 @@ public class ChannelDeviceTreeItem : IEqualityComparer<ChannelDeviceTreeItem>
return $"{channelDeviceTreeItem.ChannelDevicePluginType}.{channelDeviceTreeItem.DeviceRuntimeId}.{channelDeviceTreeItem.ChannelRuntimeId}.{channelDeviceTreeItem.PluginName}.{channelDeviceTreeItem.PluginType}"; 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)) if (string.IsNullOrWhiteSpace(jsString))
throw new ArgumentNullException(nameof(jsString)); throw new ArgumentNullException(nameof(jsString));
@@ -204,7 +272,7 @@ public class ChannelDeviceTreeItem : IEqualityComparer<ChannelDeviceTreeItem>
parsedPluginType = tmp; parsedPluginType = tmp;
} }
return new ChannelDeviceTreeItem return new ChannelDeviceTreeItemStruct
{ {
ChannelDevicePluginType = pluginType, ChannelDevicePluginType = pluginType,
DeviceRuntimeId = deviceRuntimeId, DeviceRuntimeId = deviceRuntimeId,
@@ -215,3 +283,4 @@ public class ChannelDeviceTreeItem : IEqualityComparer<ChannelDeviceTreeItem>
} }
} }

View File

@@ -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<MyWaitData>();
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<MyWaitData>();
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<MyWaitData>();
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<MyWaitData>();
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<T>
where T : class, IWaitHandle
{
private readonly int m_maxSign;
private readonly int m_minSign;
private readonly ConcurrentDictionary<int, AsyncWaitData2<T>> m_waitDic = new();
private readonly Action<int> _remove;
private int m_currentSign;
/// <summary>
/// 初始化<see cref="WaitHandlePool{T}"/>类的新实例。
/// </summary>
/// <param name="minSign">签名的最小值默认为1。</param>
/// <param name="maxSign">签名的最大值,默认为<see cref="int.MaxValue"/>。</param>
/// <remarks>
/// 签名范围用于控制自动生成的唯一标识符的取值范围。
/// 当签名达到最大值时,会自动重置到最小值重新开始分配。
/// </remarks>
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;
}
/// <summary>
/// 取消池中所有等待操作。
/// </summary>
/// <remarks>
/// 此方法会遍历池中所有的等待数据,并调用其<see cref="AsyncWaitData{T}.Cancel"/>方法来取消等待。
/// 取消后的等待数据会从池中移除。适用于应用程序关闭或需要批量取消所有等待操作的场景。
/// </remarks>
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();
}
}
}
/// <summary>
/// 获取与指定结果关联的异步等待数据。
/// </summary>
/// <param name="result">要关联的结果对象。</param>
/// <param name="autoSign">指示是否自动为结果对象分配签名,默认为<see langword="true"/>。</param>
/// <returns>创建的<see cref="AsyncWaitData{T}"/>实例。</returns>
/// <exception cref="InvalidOperationException">当指定的签名已被使用时抛出。</exception>
/// <remarks>
/// 如果<paramref name="autoSign"/>为<see langword="true"/>,方法会自动为结果对象生成唯一签名。
/// 创建的等待数据会被添加到池中,直到被设置结果或取消时才会移除。
/// </remarks>
public AsyncWaitData2<T> GetWaitDataAsync(T result, bool autoSign = true)
{
if (autoSign)
{
result.Sign = this.GetSign();
}
var waitDataAsyncSlim = new AsyncWaitData2<T>(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;
}
/// <summary>
/// 获取具有自动生成签名的异步等待数据。
/// </summary>
/// <param name="sign">输出参数,返回自动生成的签名值。</param>
/// <returns>创建的<see cref="AsyncWaitData{T}"/>实例。</returns>
/// <exception cref="InvalidOperationException">当生成的签名已被使用时抛出。</exception>
/// <remarks>
/// 此方法会自动生成唯一签名,并创建不包含挂起数据的等待对象。
/// 适用于只需要等待通知而不关心具体数据内容的场景。
/// </remarks>
public AsyncWaitData2<T> GetWaitDataAsync(out int sign)
{
sign = this.GetSign();
var waitDataAsyncSlim = new AsyncWaitData2<T>(sign, this._remove, default);
if (!this.m_waitDic.TryAdd(sign, waitDataAsyncSlim))
{
return default;
}
return waitDataAsyncSlim;
}
/// <summary>
/// 使用指定结果设置对应签名的等待操作。
/// </summary>
/// <param name="result">包含签名和结果数据的对象。</param>
/// <returns>如果成功设置等待操作则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
/// <remarks>
/// 此方法根据结果对象的签名查找对应的等待数据,并设置其结果。
/// 设置成功后,等待数据会从池中移除,正在等待的任务会被完成。
/// 如果找不到对应签名的等待数据,则返回<see langword="false"/>。
/// </remarks>
public bool Set(T result)
{
if (this.m_waitDic.TryRemove(result.Sign, out var waitDataAsync))
{
waitDataAsync.Set(result);
return true;
}
return false;
}
/// <summary>
/// 尝试获取指定签名的异步等待数据。
/// </summary>
/// <param name="sign">要查找的签名。</param>
/// <param name="waitDataAsync">输出参数,如果找到则返回对应的等待数据;否则为<see langword="null"/>。</param>
/// <returns>如果找到指定签名的等待数据则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
/// <remarks>
/// 此方法允许查询池中是否存在特定签名的等待数据,而不会修改池的状态。
/// 适用于需要检查等待状态或获取等待数据进行进一步操作的场景。
/// </remarks>
public bool TryGetDataAsync(int sign, out AsyncWaitData2<T> waitDataAsync)
{
return this.m_waitDic.TryGetValue(sign, out waitDataAsync);
}
/// <summary>
/// 生成下一个可用的唯一签名。
/// </summary>
/// <returns>生成的唯一签名值。</returns>
/// <remarks>
/// 使用原子递增操作确保签名的唯一性和线程安全性。
/// 当签名达到最大值时,会重新开始分配以避免溢出。
/// </remarks>
[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失败继续重试
}
}
/// <summary>
/// 从池中移除指定签名的等待数据。
/// </summary>
/// <param name="sign">要移除的签名。</param>
/// <remarks>
/// 此方法由等待数据在释放时自动调用,确保池中不会保留已完成或已取消的等待对象。
/// </remarks>
private void Remove(int sign)
{
this.m_waitDic.TryRemove(sign, out _);
}
}
public sealed class AsyncWaitData2<T> : DisposableObject, IValueTaskSource<WaitDataStatus>
{
// ManualResetValueTaskSourceCore 是一个结构体,避免了额外托管对象分配,但需要配合 token 使用。
private ManualResetValueTaskSourceCore<T> _core; // 核心结构体,不会分配额外对象
// 缓存的移除回调,由 WaitHandlePool 构造时传入,避免每次分配委托。
private readonly Action<int> _remove;
// 挂起时的临时数据
private readonly T _pendingData;
// 完成时的数据
private T _completedData;
// 当前等待状态(成功/取消/未完成等)
private WaitDataStatus _status;
private CancellationTokenRegistration Registration;
/// <summary>
/// 使用指定签名和移除回调初始化一个新的 <see cref="AsyncWaitData{T}"/> 实例。
/// </summary>
/// <param name="sign">此等待项对应的签名(用于在池中查找)。</param>
/// <param name="remove">完成或释放时调用的回调,用于将此实例从等待池中移除。</param>
/// <param name="pendingData">可选的挂起数据,当创建时可以携带一个初始占位数据。</param>
public AsyncWaitData2(int sign, Action<int> remove, T pendingData)
{
this.Sign = sign;
this._remove = remove;
this._pendingData = pendingData;
this._core.RunContinuationsAsynchronously = true; // 确保续体异步执行,避免潜在的栈内联执行问题
}
/// <summary>
/// 获取此等待项的签名标识。
/// </summary>
public int Sign { get; }
/// <summary>
/// 获取挂起时的原始数据(如果在创建时传入)。
/// </summary>
public T PendingData => this._pendingData;
/// <summary>
/// 获取已完成时的返回数据。
/// </summary>
public T CompletedData => this._completedData;
/// <summary>
/// 获取当前等待状态例如Success、Canceled 等)。
/// </summary>
public WaitDataStatus Status => this._status;
/// <summary>
/// 取消当前等待标记为已取消并触发等待任务的异常OperationCanceledException
/// </summary>
public void Cancel()
{
this.Set(WaitDataStatus.Canceled, default!);
}
/// <summary>
/// 将等待项设置为成功并携带结果数据。
/// </summary>
/// <param name="result">要设置的完成数据。</param>
public void Set(T result)
{
this.Set(WaitDataStatus.Success, result);
}
/// <summary>
/// 设置等待项的状态和数据,并完成对应的 ValueTask。
/// </summary>
/// <param name="status">要设置的状态。</param>
/// <param name="result">要设置的完成数据。</param>
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);
}
/// <summary>
/// 异步等待此项完成,返回一个 <see cref="ValueTask{WaitDataStatus}"/>,可传入取消令牌以取消等待。
/// </summary>
/// <param name="cancellationToken">可选的取消令牌。若触发则会调用 <see cref="Cancel"/>。</param>
/// <returns>表示等待状态的 ValueTask。</returns>
public ValueTask<WaitDataStatus> WaitAsync(CancellationToken cancellationToken = default)
{
if (cancellationToken.CanBeCanceled)
{
this.Registration = cancellationToken.Register(this.Cancel);
}
return new ValueTask<WaitDataStatus>(this, this._core.Version);
}
/// <summary>
/// 从核心获取结果(显式接口实现)。
/// 注意:此方法由 ValueTask 基础设施调用,不应直接在用户代码中调用。
/// </summary>
WaitDataStatus IValueTaskSource<WaitDataStatus>.GetResult(short token)
{
this._core.GetResult(token);
return this._status;
}
/// <summary>
/// 获取当前 ValueTask 源的状态(显式接口实现)。
/// </summary>
ValueTaskSourceStatus IValueTaskSource<WaitDataStatus>.GetStatus(short token)
=> this._core.GetStatus(token);
/// <summary>
/// 注册续体(显式接口实现)。
/// 注意flags 可以控制是否捕获上下文等行为。
/// </summary>
void IValueTaskSource<WaitDataStatus>.OnCompleted(Action<object?> continuation, object? state,
short token, ValueTaskSourceOnCompletedFlags flags)
=> this._core.OnCompleted(continuation, state, token, flags);
/// <summary>
/// 释放托管资源时调用,会触发传入的移除回调,从所在的等待池中移除此等待项。
/// </summary>
/// <param name="disposing">是否为显式释放。</param>
protected override void Dispose(bool disposing)
{
if (disposing)
{
this.Registration.Dispose();
this._remove(this.Sign);
}
base.Dispose(disposing);
}
}
}

View File

@@ -14,8 +14,6 @@
using BenchmarkDotNet.Configs; using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running; using BenchmarkDotNet.Running;
using ThingsGateway.Foundation;
namespace BenchmarkConsoleApp namespace BenchmarkConsoleApp
{ {
internal class Program internal class Program
@@ -47,15 +45,18 @@ namespace BenchmarkConsoleApp
//ManualConfig.Create(DefaultConfig.Instance) //ManualConfig.Create(DefaultConfig.Instance)
//.WithOptions(ConfigOptions.DisableOptimizationsValidator) //.WithOptions(ConfigOptions.DisableOptimizationsValidator)
//); //);
BenchmarkRunner.Run<BenchmarkAsyncWaitData>(
BenchmarkRunner.Run<SemaphoreBenchmark>( ManualConfig.Create(DefaultConfig.Instance)
ManualConfig.Create(DefaultConfig.Instance) .WithOptions(ConfigOptions.DisableOptimizationsValidator)
.WithOptions(ConfigOptions.DisableOptimizationsValidator) );
); // BenchmarkRunner.Run<SemaphoreBenchmark>(
// BenchmarkRunner.Run<ModbusBenchmark>( // ManualConfig.Create(DefaultConfig.Instance)
//ManualConfig.Create(DefaultConfig.Instance) // .WithOptions(ConfigOptions.DisableOptimizationsValidator)
//.WithOptions(ConfigOptions.DisableOptimizationsValidator) //);
//); // BenchmarkRunner.Run<ModbusBenchmark>(
//ManualConfig.Create(DefaultConfig.Instance)
//.WithOptions(ConfigOptions.DisableOptimizationsValidator)
//);
// BenchmarkRunner.Run<S7Benchmark>( // BenchmarkRunner.Run<S7Benchmark>(
//ManualConfig.Create(DefaultConfig.Instance) //ManualConfig.Create(DefaultConfig.Instance)
//.WithOptions(ConfigOptions.DisableOptimizationsValidator) //.WithOptions(ConfigOptions.DisableOptimizationsValidator)

View File

@@ -8,6 +8,8 @@
// QQ群605534569 // QQ群605534569
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using PooledAwait;
using System.Buffers; using System.Buffers;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -157,7 +159,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress
{ {
return PackHelper.LoadSourceRead<T>(this, deviceVariables, maxPack, defaultIntervalTime); return PackHelper.LoadSourceRead<T>(this, deviceVariables, maxPack, defaultIntervalTime);
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override Task DisposeAsync(bool disposing) protected override Task DisposeAsync(bool disposing)
{ {
@@ -352,10 +354,10 @@ public class ModbusSlave : DeviceBase, IModbusAddress
Init(mAddress); Init(mAddress);
} }
ModbusServer01ByteBlocks.TryGetValue(mAddress.Station,out var ModbusServer01ByteBlock); ModbusServer01ByteBlocks.TryGetValue(mAddress.Station, out var ModbusServer01ByteBlock);
ModbusServer02ByteBlocks.TryGetValue(mAddress.Station,out var ModbusServer02ByteBlock); ModbusServer02ByteBlocks.TryGetValue(mAddress.Station, out var ModbusServer02ByteBlock);
ModbusServer03ByteBlocks.TryGetValue(mAddress.Station,out var ModbusServer03ByteBlock); ModbusServer03ByteBlocks.TryGetValue(mAddress.Station, out var ModbusServer03ByteBlock);
ModbusServer04ByteBlocks.TryGetValue(mAddress.Station,out var ModbusServer04ByteBlock); ModbusServer04ByteBlocks.TryGetValue(mAddress.Station, out var ModbusServer04ByteBlock);
if (read) if (read)
{ {
using (new ReadLock(_lockSlim)) using (new ReadLock(_lockSlim))
@@ -475,33 +477,31 @@ public class ModbusSlave : DeviceBase, IModbusAddress
return mAddress; return mAddress;
} }
/// <inheritdoc/> /// <inheritdoc/>
public override async ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<byte> value, DataTypeEnum dataType, CancellationToken cancellationToken = default) public override ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<byte> value, DataTypeEnum dataType, CancellationToken cancellationToken = default)
{ {
try try
{ {
await EasyValueTask.CompletedTask.ConfigureAwait(false);
var mAddress = GetModbusAddress(address, Station); var mAddress = GetModbusAddress(address, Station);
mAddress.SlaveWriteDatas = new(value); mAddress.SlaveWriteDatas = new(value);
return ModbusRequest(mAddress, false, cancellationToken); return EasyValueTask.FromResult<OperResult>(ModbusRequest(mAddress, false, cancellationToken));
} }
catch (Exception ex) catch (Exception ex)
{ {
return new OperResult(ex); return EasyValueTask.FromResult(new OperResult(ex));
} }
} }
/// <inheritdoc/> /// <inheritdoc/>
public override async ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<bool> value, CancellationToken cancellationToken = default) public override ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<bool> value, CancellationToken cancellationToken = default)
{ {
try try
{ {
await EasyValueTask.CompletedTask.ConfigureAwait(false);
var mAddress = GetModbusAddress(address, Station); var mAddress = GetModbusAddress(address, Station);
if (mAddress.IsBitFunction) if (mAddress.IsBitFunction)
{ {
mAddress.SlaveWriteDatas = new(value.Span.BoolToByte()); mAddress.SlaveWriteDatas = new(value.Span.BoolToByte());
ModbusRequest(mAddress, false, cancellationToken); ModbusRequest(mAddress, false, cancellationToken);
return OperResult.Success; return EasyValueTask.FromResult(OperResult.Success);
} }
else else
{ {
@@ -509,7 +509,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress
{ {
mAddress.Length = 2; mAddress.Length = 2;
var readData = ModbusRequest(mAddress, true, cancellationToken); var readData = ModbusRequest(mAddress, true, cancellationToken);
if (!readData.IsSuccess) return readData; if (!readData.IsSuccess) return EasyValueTask.FromResult<OperResult>(readData);
var writeData = TouchSocketBitConverter.BigEndian.To<ushort>(readData.Content.Span); var writeData = TouchSocketBitConverter.BigEndian.To<ushort>(readData.Content.Span);
var span = value.Span; var span = value.Span;
for (int i = 0; i < value.Length; i++) for (int i = 0; i < value.Length; i++)
@@ -518,17 +518,17 @@ public class ModbusSlave : DeviceBase, IModbusAddress
} }
mAddress.SlaveWriteDatas = new(ThingsGatewayBitConverter.GetBytes(writeData)); mAddress.SlaveWriteDatas = new(ThingsGatewayBitConverter.GetBytes(writeData));
ModbusRequest(mAddress, false, cancellationToken); ModbusRequest(mAddress, false, cancellationToken);
return OperResult.Success; return EasyValueTask.FromResult<OperResult>(OperResult.Success);
} }
else else
{ {
return new OperResult(string.Format(AppResource.ValueOverlimit, nameof(mAddress.BitIndex), 16)); return EasyValueTask.FromResult<OperResult>(new OperResult(string.Format(AppResource.ValueOverlimit, nameof(mAddress.BitIndex), 16)));
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
return new OperResult(ex); return EasyValueTask.FromResult<OperResult>(new OperResult(ex));
} }
} }
protected override ValueTask ChannelReceived(IClientChannel client, ReceivedDataEventArgs e, bool last) protected override ValueTask ChannelReceived(IClientChannel client, ReceivedDataEventArgs e, bool last)
@@ -536,20 +536,26 @@ public class ModbusSlave : DeviceBase, IModbusAddress
return HandleChannelReceivedAsync(client, e, last); 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; 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<byte> sequences, out bool modbusRtu) private static bool TryParseRequest(object requestInfo, out ModbusRequest modbusRequest, out ReadOnlySequence<byte> sequences, out bool modbusRtu)
@@ -580,7 +586,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress
private static byte NormalizeFunctionCode(byte funcCode) private static byte NormalizeFunctionCode(byte funcCode)
=> funcCode > 0x30 ? (byte)(funcCode - 0x30) : funcCode; => funcCode > 0x30 ? (byte)(funcCode - 0x30) : funcCode;
private async Task HandleReadRequestAsync( private Task HandleReadRequestAsync(
IClientChannel client, IClientChannel client,
ReceivedDataEventArgs e, ReceivedDataEventArgs e,
ModbusRequest modbusRequest, ModbusRequest modbusRequest,
@@ -590,27 +596,31 @@ public class ModbusSlave : DeviceBase, IModbusAddress
var data = ModbusRequest(modbusRequest, true); var data = ModbusRequest(modbusRequest, true);
if (!data.IsSuccess) if (!data.IsSuccess)
{ {
await WriteError(modbusRtu, client, sequences, e).ConfigureAwait(false); return WriteError(modbusRtu, client, sequences, e);
return;
} }
ValueByteBlock byteBlock = new(1024); return Write(this, client, e, modbusRequest, sequences, modbusRtu, data);
try
static async PooledTask Write(ModbusSlave @this, IClientChannel client, ReceivedDataEventArgs e, ModbusRequest modbusRequest, ReadOnlySequence<byte> sequences, bool modbusRtu, OperResult<ReadOnlyMemory<byte>> data)
{ {
WriteReadResponse(modbusRequest, sequences, data.Content, ref byteBlock, modbusRtu); ValueByteBlock byteBlock = new(1024);
await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false); try
} {
catch WriteReadResponse(modbusRequest, sequences, data.Content, ref byteBlock, modbusRtu);
{ await @this.ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false);
await WriteError(modbusRtu, client, sequences, e).ConfigureAwait(false); }
} catch
finally {
{ await @this.WriteError(modbusRtu, client, sequences, e).ConfigureAwait(false);
byteBlock.SafeDispose(); }
finally
{
byteBlock.SafeDispose();
}
} }
} }
private async Task HandleWriteRequestAsync( private Task HandleWriteRequestAsync(
IClientChannel client, IClientChannel client,
ReceivedDataEventArgs e, ReceivedDataEventArgs e,
ModbusRequest modbusRequest, ModbusRequest modbusRequest,
@@ -618,50 +628,62 @@ public class ModbusSlave : DeviceBase, IModbusAddress
bool modbusRtu, bool modbusRtu,
byte f) byte f)
{ {
var modbusAddress = new ModbusAddress(modbusRequest); return HandleWriteRequestAsync(this, client, e, modbusRequest, sequences, modbusRtu, f);
bool isSuccess;
switch (f)
static async PooledTask HandleWriteRequestAsync(ModbusSlave @this, IClientChannel client, ReceivedDataEventArgs e, ModbusRequest modbusRequest, ReadOnlySequence<byte> sequences, bool modbusRtu, byte f)
{ {
case 5: var modbusAddress = new ModbusAddress(modbusRequest);
case 15: bool isSuccess;
modbusAddress.WriteFunctionCode = modbusRequest.FunctionCode;
modbusAddress.FunctionCode = 1;
isSuccess = await HandleWriteCoreAsync(modbusAddress, client, modbusRequest).ConfigureAwait(false);
break;
case 6: switch (f)
case 16: {
modbusAddress.WriteFunctionCode = modbusRequest.FunctionCode; case 5:
modbusAddress.FunctionCode = 3; case 15:
isSuccess = await HandleWriteCoreAsync(modbusAddress, client, modbusRequest).ConfigureAwait(false); modbusAddress.WriteFunctionCode = modbusRequest.FunctionCode;
break; modbusAddress.FunctionCode = 1;
isSuccess = await @this.HandleWriteCoreAsync(modbusAddress, client, modbusRequest).ConfigureAwait(false);
break;
default: case 6:
return; 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<bool> HandleWriteCoreAsync(ModbusAddress address, IClientChannel client, ModbusRequest modbusRequest) private Task<bool> HandleWriteCoreAsync(ModbusAddress address, IClientChannel client, ModbusRequest modbusRequest)
{ {
if (WriteData != null) return HandleWriteCoreAsync(this, address, client, modbusRequest);
{
var result = await WriteData(address, ThingsGatewayBitConverter, client).ConfigureAwait(false);
if (!result.IsSuccess) return false;
}
if (IsWriteMemory) static async PooledTask<bool> HandleWriteCoreAsync(ModbusSlave @this, ModbusAddress address, IClientChannel client, ModbusRequest modbusRequest)
{ {
var memResult = ModbusRequest(modbusRequest, false); if (@this.WriteData != null)
return memResult.IsSuccess; {
} 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( private static void WriteReadResponse(
@@ -696,63 +718,78 @@ public class ModbusSlave : DeviceBase, IModbusAddress
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), EndianType.Big, 5); ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), EndianType.Big, 5);
} }
private async Task ReturnData(IClientChannel client, ReadOnlyMemory<byte> sendData, ReceivedDataEventArgs e) private Task ReturnData(IClientChannel client, ReadOnlyMemory<byte> sendData, ReceivedDataEventArgs e)
{ {
if (SendDelayTime > 0) return ReturnData(SendDelayTime, client, sendData, e);
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);
}
private async Task WriteError(bool modbusRtu, IClientChannel client, ReadOnlySequence<byte> bytes, ReceivedDataEventArgs e) static async PooledTask ReturnData(int deley, IClientChannel client, ReadOnlyMemory<byte> sendData, ReceivedDataEventArgs e)
{
ValueByteBlock byteBlock = new(20);
try
{ {
if (modbusRtu) if (deley > 0)
{ await Task.Delay(deley).ConfigureAwait(false);
ByteBlockExtension.Write(ref byteBlock, bytes.Slice(0, 2)); if (client is IUdpClientSender udpClientSender)
WriterExtension.WriteValue(ref byteBlock, (byte)1); await udpClientSender.SendAsync(((UdpReceivedDataEventArgs)e).EndPoint, sendData).ConfigureAwait(false);
byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Span));
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Span[1] + 128), EndianType.Big, 1);
}
else else
{ await client.SendAsync(sendData, client.ClosedToken).ConfigureAwait(false);
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();
} }
} }
private async Task WriteSuccess(bool modbusRtu, IClientChannel client, ReadOnlySequence<byte> bytes, ReceivedDataEventArgs e) private Task WriteError(bool modbusRtu, IClientChannel client, ReadOnlySequence<byte> bytes, ReceivedDataEventArgs e)
{ {
ValueByteBlock byteBlock = new(20); return WriteError(this, modbusRtu, client, bytes, e);
try
static async PooledTask WriteError(ModbusSlave @this, bool modbusRtu, IClientChannel client, ReadOnlySequence<byte> bytes, ReceivedDataEventArgs e)
{ {
if (modbusRtu) ValueByteBlock byteBlock = new(20);
try
{ {
ByteBlockExtension.Write(ref byteBlock, bytes.Slice(0, 6)); if (modbusRtu)
byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Span)); {
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)); byteBlock.SafeDispose();
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), EndianType.Big, 5);
} }
await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false);
} }
finally }
private Task WriteSuccess(bool modbusRtu, IClientChannel client, ReadOnlySequence<byte> bytes, ReceivedDataEventArgs e)
{
return WriteSuccess(this, modbusRtu, client, bytes, e);
static async PooledTask WriteSuccess(ModbusSlave @this, bool modbusRtu, IClientChannel client, ReadOnlySequence<byte> 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();
}
} }
} }

View File

@@ -395,18 +395,17 @@ public class OpcUaMaster : IAsyncDisposable
/// <summary> /// <summary>
/// 连接到服务器 /// 连接到服务器
/// </summary> /// </summary>
public async Task ConnectAsync(CancellationToken cancellationToken) public Task ConnectAsync(CancellationToken cancellationToken)
{ {
await ConnectAsync(OpcUaProperty.OpcUrl, cancellationToken).ConfigureAwait(false); return ConnectAsync(OpcUaProperty.OpcUrl, cancellationToken);
} }
/// <summary> /// <summary>
/// 断开连接。 /// 断开连接。
/// </summary> /// </summary>
public async Task DisconnectAsync() public Task DisconnectAsync()
{ {
await PrivateDisconnectAsync().ConfigureAwait(false); return PrivateDisconnectAsync();
} }
public async ValueTask DisposeAsync() public async ValueTask DisposeAsync()

View File

@@ -8,6 +8,8 @@
// QQ群605534569 // QQ群605534569
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using PooledAwait;
using ThingsGateway.Foundation.Extension.String; using ThingsGateway.Foundation.Extension.String;
using ThingsGateway.NewLife; using ThingsGateway.NewLife;
using ThingsGateway.NewLife.Extension; using ThingsGateway.NewLife.Extension;
@@ -144,71 +146,76 @@ public partial class SiemensS7Master : DeviceBase
/// <summary> /// <summary>
/// 此方法并不会智能分组以最大化效率减少传输次数因为返回值是byte[],所以一切都按地址数组的顺序执行,最后合并数组 /// 此方法并不会智能分组以最大化效率减少传输次数因为返回值是byte[],所以一切都按地址数组的顺序执行,最后合并数组
/// </summary> /// </summary>
public async ValueTask<OperResult<ReadOnlyMemory<byte>>> S7ReadAsync( public ValueTask<OperResult<ReadOnlyMemory<byte>>> S7ReadAsync(
SiemensS7Address[] addresses, SiemensS7Address[] addresses,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var byteBuffer = new ValueByteBlock(512); return S7ReadAsync(this, addresses, cancellationToken);
try static async PooledValueTask<OperResult<ReadOnlyMemory<byte>>> S7ReadAsync(SiemensS7Master @this, SiemensS7Address[] addresses, CancellationToken cancellationToken)
{ {
foreach (var address in addresses) var byteBuffer = new ValueByteBlock(512);
try
{ {
int readCount = 0; foreach (var address in addresses)
int totalLength = address.Length == 0 ? 1 : address.Length;
int originalStart = address.AddressStart;
try
{ {
while (readCount < totalLength) int readCount = 0;
int totalLength = address.Length == 0 ? 1 : address.Length;
int originalStart = address.AddressStart;
try
{ {
// 每次读取的 PDU 长度,循环直到读取完整 while (readCount < totalLength)
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)
{ {
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; 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; finally
{
// 更新地址起点 address.AddressStart = originalStart;
if (address.DataCode == S7Area.TM || address.DataCode == S7Area.CT)
address.AddressStart += chunkLength / 2;
else
address.AddressStart += chunkLength * 8;
} }
} }
finally
{
address.AddressStart = originalStart;
}
}
return new OperResult<ReadOnlyMemory<byte>> { Content = byteBuffer.ToArray() }; return new OperResult<ReadOnlyMemory<byte>> { Content = byteBuffer.ToArray() };
} }
catch (Exception ex) catch (Exception ex)
{ {
return new OperResult<ReadOnlyMemory<byte>>(ex); return new OperResult<ReadOnlyMemory<byte>>(ex);
} }
finally finally
{ {
byteBuffer.SafeDispose(); byteBuffer.SafeDispose();
}
} }
} }
@@ -216,79 +223,65 @@ public partial class SiemensS7Master : DeviceBase
/// <summary> /// <summary>
/// 此方法并不会智能分组以最大化效率减少传输次数因为返回值是byte[],所以一切都按地址数组的顺序执行,最后合并数组 /// 此方法并不会智能分组以最大化效率减少传输次数因为返回值是byte[],所以一切都按地址数组的顺序执行,最后合并数组
/// </summary> /// </summary>
public async ValueTask<Dictionary<SiemensS7Address, OperResult>> S7WriteAsync( public ValueTask<Dictionary<SiemensS7Address, OperResult>> S7WriteAsync(
SiemensS7Address[] addresses, SiemensS7Address[] addresses,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var dictOperResult = new Dictionary<SiemensS7Address, OperResult>(); return S7WriteAsync(this, addresses, cancellationToken);
void SetFailOperResult(OperResult operResult) static async PooledValueTask<Dictionary<SiemensS7Address, OperResult>> S7WriteAsync(SiemensS7Master @this, SiemensS7Address[] addresses, CancellationToken cancellationToken)
{ {
foreach (var address in addresses) var dictOperResult = new Dictionary<SiemensS7Address, OperResult>();
{
dictOperResult.TryAdd(address, operResult);
}
}
var firstAddress = addresses[0]; void SetFailOperResult(OperResult operResult)
// 单位写入(位写入)
if (addresses.Length <= 1 && firstAddress.IsBit)
{
var byteBuffer = new ValueByteBlock(512);
try
{ {
var writeResult = await SendThenReturnAsync( foreach (var address in addresses)
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<List<SiemensS7Address>>();
ushort dataLength = 0;
ushort itemCount = 1;
var currentChunk = new List<SiemensS7Address>();
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)
{ {
currentChunk.Add(address); dictOperResult.TryAdd(address, operResult);
itemCount++;
if (i == addresses.Length - 1)
addressChunks.Add(currentChunk);
} }
else }
var firstAddress = addresses[0];
// 单位写入(位写入)
if (addresses.Length <= 1 && firstAddress.IsBit)
{
var byteBuffer = new ValueByteBlock(512);
try
{ {
addressChunks.Add(currentChunk); var writeResult = await @this.SendThenReturnAsync(
currentChunk = new List<SiemensS7Address>(); new S7Send([firstAddress], false),
dataLength = 0; cancellationToken: cancellationToken
itemCount = 1; ).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<List<SiemensS7Address>>();
ushort dataLength = 0;
ushort itemCount = 1;
var currentChunk = new List<SiemensS7Address>();
for (int i = 0; i < addresses.Length; i++)
{
var address = addresses[i];
dataLength += (ushort)(address.Data.Length + 4); 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); currentChunk.Add(address);
itemCount++; itemCount++;
@@ -298,34 +291,53 @@ public partial class SiemensS7Master : DeviceBase
} }
else else
{ {
SetFailOperResult(new OperResult("Write length exceeds limit")); addressChunks.Add(currentChunk);
currentChunk = new List<SiemensS7Address>();
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;
} }
} }
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;
} }
} }

View File

@@ -8,6 +8,8 @@
// QQ群605534569 // QQ群605534569
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using PooledAwait;
using System.Diagnostics; using System.Diagnostics;
using ThingsGateway.Extension.Generic; using ThingsGateway.Extension.Generic;
@@ -81,91 +83,101 @@ public partial class QuestDBProducer : BusinessBaseWithCacheIntervalVariable
AddQueueVarModel(new CacheDBItem<VariableBasicData>(variable)); AddQueueVarModel(new CacheDBItem<VariableBasicData>(variable));
} }
} }
private async ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken) private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
{ {
var result = await InserableAsync(item.WhereIf(_driverPropertys.OnlineFilter, a => a.IsOnline == true).ToList(), cancellationToken).ConfigureAwait(false); return UpdateVarModel(this, item, cancellationToken);
if (success != result.IsSuccess)
{
if (!result.IsSuccess)
LogMessage?.LogWarning(result.ToString());
success = result.IsSuccess;
}
return result; static async PooledValueTask<OperResult> UpdateVarModel(QuestDBProducer @this, IEnumerable<VariableBasicData> 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 #region
private async ValueTask<OperResult> InserableAsync(List<VariableBasicData> dbInserts, CancellationToken cancellationToken) private ValueTask<OperResult> InserableAsync(List<VariableBasicData> dbInserts, CancellationToken cancellationToken)
{ {
try return InserableAsync(this, dbInserts, cancellationToken);
static async PooledValueTask<OperResult> InserableAsync(QuestDBProducer @this, List<VariableBasicData> dbInserts, CancellationToken cancellationToken)
{ {
_db.Ado.CancellationToken = cancellationToken; try
if (!_driverPropertys.BigTextScriptHistoryTable.IsNullOrEmpty())
{ {
var getDeviceModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(_driverPropertys.BigTextScriptHistoryTable); @this._db.Ado.CancellationToken = cancellationToken;
getDeviceModel.Logger = LogMessage; if (!@this._driverPropertys.BigTextScriptHistoryTable.IsNullOrEmpty())
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())
{ {
Stopwatch stopwatch = new(); var getDeviceModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(@this._driverPropertys.BigTextScriptHistoryTable);
stopwatch.Start(); getDeviceModel.Logger = @this.LogMessage;
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 result = await db.Insertable(dbInserts).SplitTable().ExecuteCommandAsync().ConfigureAwait(false); await getDeviceModel.DBInsertable(@this._db, dbInserts, cancellationToken).ConfigureAwait(false);
if (result > 0) }
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()) return OperResult.Success;
{ }
Stopwatch stopwatch = new(); catch (Exception ex)
stopwatch.Start(); {
var data = stringData.AdaptListQuestDBHistoryValue(); return new OperResult(ex);
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);
} }
} }

View File

@@ -10,6 +10,8 @@
using BootstrapBlazor.Components; using BootstrapBlazor.Components;
using PooledAwait;
using ThingsGateway.Common; using ThingsGateway.Common;
using ThingsGateway.DB; using ThingsGateway.DB;
using ThingsGateway.Debug; using ThingsGateway.Debug;
@@ -239,39 +241,44 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariable
await base.ProtectedStartAsync(cancellationToken).ConfigureAwait(false); 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(); if (@this._driverPropertys.IsReadDB)
try
{ {
var varLists = list.Batch(_driverPropertys.SplitSize); var list = @this.RealTimeVariables.ToListWithDequeue();
foreach (var varList in varLists) try
{ {
var result = await UpdateAsync(varList, cancellationToken).ConfigureAwait(false); var varLists = list.Batch(@this._driverPropertys.SplitSize);
if (success != result.IsSuccess) foreach (var varList in varLists)
{ {
if (!result.IsSuccess) var result = await @this.UpdateAsync(varList, cancellationToken).ConfigureAwait(false);
LogMessage?.LogWarning(result.ToString()); if (@this.success != result.IsSuccess)
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) await @this.Update(cancellationToken).ConfigureAwait(false);
LogMessage?.LogWarning(ex);
success = false;
list.ForEach(variable => RealTimeVariables.AddOrUpdate(variable.Id, variable, (key, oldValue) => variable));
} }
} }
if (_driverPropertys.IsHistoryDB)
{
await Update(cancellationToken).ConfigureAwait(false);
}
} }
private ISugarQueryable<SQLNumberHistoryValue> Query(DBHistoryValuePageInput input) private ISugarQueryable<SQLNumberHistoryValue> Query(DBHistoryValuePageInput input)

View File

@@ -8,6 +8,8 @@
// QQ群605534569 // QQ群605534569
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using PooledAwait;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics; using System.Diagnostics;
@@ -97,131 +99,146 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariable
} }
} }
private async ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken) private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
{ {
var result = await InserableAsync(item.WhereIf(_driverPropertys.OnlineFilter, a => a.IsOnline == true).ToList(), cancellationToken).ConfigureAwait(false); return UpdateVarModel(this, item, cancellationToken);
if (success != result.IsSuccess)
{
if (!result.IsSuccess)
LogMessage?.LogWarning(result.ToString());
success = result.IsSuccess;
}
return result; static async PooledValueTask<OperResult> UpdateVarModel(SqlDBProducer @this, IEnumerable<VariableBasicData> 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 #region
private async ValueTask<OperResult> InserableAsync(List<VariableBasicData> dbInserts, CancellationToken cancellationToken) private ValueTask<OperResult> InserableAsync(List<VariableBasicData> dbInserts, CancellationToken cancellationToken)
{ {
try return InserableAsync(this, dbInserts, cancellationToken);
static async PooledValueTask<OperResult> InserableAsync(SqlDBProducer @this, List<VariableBasicData> dbInserts, CancellationToken cancellationToken)
{ {
_db.Ado.CancellationToken = cancellationToken; try
if (!_driverPropertys.BigTextScriptHistoryTable.IsNullOrEmpty())
{ {
var getDeviceModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(_driverPropertys.BigTextScriptHistoryTable); @this._db.Ado.CancellationToken = cancellationToken;
if (!@this._driverPropertys.BigTextScriptHistoryTable.IsNullOrEmpty())
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())
{ {
var data = numberData.AdaptEnumerableSQLNumberHistoryValue(); var getDeviceModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(@this._driverPropertys.BigTextScriptHistoryTable);
Stopwatch stopwatch = new();
stopwatch.Start();
var result = await _db.Fastest<SQLNumberHistoryValue>().SplitTable().BulkCopyAsync(data).ConfigureAwait(false);
stopwatch.Stop();
//var result = await db.Insertable(dbInserts).SplitTable().ExecuteCommandAsync().ConfigureAwait(false); getDeviceModel.Logger = @this.LogMessage;
if (result > 0)
{ await getDeviceModel.DBInsertable(@this._db, dbInserts, cancellationToken).ConfigureAwait(false);
LogMessage?.Trace($"TableName{_driverPropertys.NumberTableName}Count{result}watchTime: {stopwatch.ElapsedMilliseconds} ms");
}
} }
else
if (stringData.Any())
{ {
Stopwatch stopwatch = new(); var stringData = dbInserts.Where(a => (!a.IsNumber && a.Value is not bool));
stopwatch.Start(); var numberData = dbInserts.Where(a => (a.IsNumber || a.Value is bool));
var data = stringData.AdaptEnumerableSQLHistoryValue();
var result = await _db.Fastest<SQLHistoryValue>().SplitTable().BulkCopyAsync(data).ConfigureAwait(false);
stopwatch.Stop();
//var result = await db.Insertable(dbInserts).SplitTable().ExecuteCommandAsync().ConfigureAwait(false); if (numberData.Any())
if (result > 0)
{ {
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<SQLNumberHistoryValue>().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; if (stringData.Any())
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
private async ValueTask<OperResult> UpdateAsync(List<VariableBasicData> datas, CancellationToken cancellationToken)
{
try
{
_db.Ado.CancellationToken = cancellationToken;
if (!_driverPropertys.BigTextScriptRealTable.IsNullOrEmpty())
{
var getDeviceModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(_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<SQLRealValue>().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<SQLRealValue>().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)
{ {
Stopwatch stopwatch = new(); Stopwatch stopwatch = new();
stopwatch.Start(); stopwatch.Start();
var data = stringData.AdaptEnumerableSQLHistoryValue();
var result = await @this._db.Fastest<SQLHistoryValue>().SplitTable().BulkCopyAsync(data).ConfigureAwait(false);
stopwatch.Stop();
var data = datas.AdaptEnumerableSQLRealValue(); //var result = await db.Insertable(dbInserts).SplitTable().ExecuteCommandAsync().ConfigureAwait(false);
var result = await _db.Fastest<SQLRealValue>().AS(_driverPropertys.ReadDBTableName).BulkUpdateAsync(data).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<OperResult> UpdateAsync(List<VariableBasicData> datas, CancellationToken cancellationToken)
{
return UpdateAsync(this, datas, cancellationToken);
static async PooledValueTask<OperResult> UpdateAsync(SqlDBProducer @this, List<VariableBasicData> datas, CancellationToken cancellationToken)
{
try
{
@this._db.Ado.CancellationToken = cancellationToken;
if (!@this._driverPropertys.BigTextScriptRealTable.IsNullOrEmpty())
{
var getDeviceModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(@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<SQLRealValue>().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<SQLRealValue>().AS(@this._driverPropertys.ReadDBTableName).BulkCopyAsync(InsertData).ConfigureAwait(false);
@this._initRealData = true;
stopwatch.Stop(); stopwatch.Stop();
if (result > 0) 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<SQLRealValue>().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;
} }
return OperResult.Success;
} }
} }
} catch (Exception ex)
catch (Exception ex) {
{ return new OperResult(ex);
return new OperResult(ex); }
} }
} }

View File

@@ -8,6 +8,8 @@
// QQ群605534569 // QQ群605534569
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using PooledAwait;
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
@@ -85,66 +87,80 @@ public partial class TDengineDBProducer : BusinessBaseWithCacheIntervalVariable
AddQueueVarModel(new CacheDBItem<VariableBasicData>(variable)); AddQueueVarModel(new CacheDBItem<VariableBasicData>(variable));
} }
} }
private async ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken) private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
{ {
var result = await InserableAsync(item.WhereIf(_driverPropertys.OnlineFilter, a => a.IsOnline == true).ToList(), cancellationToken).ConfigureAwait(false); return UpdateVarModel(this, item, cancellationToken);
if (success != result.IsSuccess)
{
if (!result.IsSuccess)
LogMessage?.LogWarning(result.ToString());
success = result.IsSuccess;
}
return result; static async PooledValueTask<OperResult> UpdateVarModel(TDengineDBProducer @this, IEnumerable<VariableBasicData> 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 #region
private async ValueTask<OperResult> InserableAsync(List<VariableBasicData> dbInserts, CancellationToken cancellationToken) private ValueTask<OperResult> InserableAsync(List<VariableBasicData> dbInserts, CancellationToken cancellationToken)
{ {
try return InserableAsync(this, dbInserts, cancellationToken);
static async PooledValueTask<OperResult> InserableAsync(TDengineDBProducer @this, List<VariableBasicData> dbInserts, CancellationToken cancellationToken)
{ {
_db.Ado.CancellationToken = cancellationToken; try
if (!_driverPropertys.BigTextScriptHistoryTable.IsNullOrEmpty())
{ {
var getDeviceModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(_driverPropertys.BigTextScriptHistoryTable); @this._db.Ado.CancellationToken = cancellationToken;
getDeviceModel.Logger = LogMessage;
await getDeviceModel.DBInsertable(_db, dbInserts, cancellationToken).ConfigureAwait(false); if (!@this._driverPropertys.BigTextScriptHistoryTable.IsNullOrEmpty())
{
var getDeviceModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(@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)); return new OperResult(ex);
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 OperResult.Success;
}
catch (Exception ex)
{
return new OperResult(ex);
} }
} }
private async Task InserableAsync(IEnumerable<VariableBasicData> dbInserts, string tableName, CancellationToken cancellationToken) private Task InserableAsync(IEnumerable<VariableBasicData> dbInserts, string tableName, CancellationToken cancellationToken)
{ {
return InserableAsync(this, dbInserts, tableName, cancellationToken);
Stopwatch stopwatch = new();
stopwatch.Start();
StringBuilder stringBuilder = new(); static async PooledTask InserableAsync(TDengineDBProducer @this, IEnumerable<VariableBasicData> dbInserts, string tableName, CancellationToken cancellationToken)
stringBuilder.Append($"INSERT INTO");
bool any = false;
//(`id`,`createtime`,`collecttime`,`isonline`,`value`)
foreach (var deviceGroup in dbInserts.GroupBy(a => a.DeviceName))
{ {
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; foreach (var variableGroup in deviceGroup.GroupBy(a => a.Name))
stringBuilder.Append($""" {
any = true;
stringBuilder.Append($"""
`{tableName}_{deviceGroup.Key}_{variableGroup.Key}` `{tableName}_{deviceGroup.Key}_{variableGroup.Key}`
USING `{tableName}` TAGS ("{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) 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.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.Append(';');
stringBuilder.AppendLine(); 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(); stopwatch.Stop();
//if (result > 0) //if (result > 0)
{ {
LogMessage?.Trace($"TableName{tableName}Count{result}watchTime: {stopwatch.ElapsedMilliseconds} ms"); @this.LogMessage?.Trace($"TableName{tableName}Count{result}watchTime: {stopwatch.ElapsedMilliseconds} ms");
}
return;
} }
} }
#endregion #endregion

View File

@@ -10,6 +10,8 @@
using BootstrapBlazor.Components; using BootstrapBlazor.Components;
using PooledAwait;
using ThingsGateway.Extension.Generic; using ThingsGateway.Extension.Generic;
using ThingsGateway.Foundation; using ThingsGateway.Foundation;
@@ -148,70 +150,80 @@ public partial class Webhook : BusinessBaseWithCacheIntervalScriptAll
private readonly HttpClient client = new HttpClient(); private readonly HttpClient client = new HttpClient();
private async Task<OperResult> WebhookUpAsync(TopicArray topicArray, CancellationToken cancellationToken) private Task<OperResult> WebhookUpAsync(TopicArray topicArray, CancellationToken cancellationToken)
{ {
// 设置请求内容 return WebhookUpAsync(this, topicArray, 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");
try static async PooledTask<OperResult> 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");
// 检查响应状态 try
if (response.IsSuccessStatusCode)
{ {
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) if (@this._driverPropertys.DetailLog)
LogMessage?.LogTrace(GetDetailLogString(topicArray, _memoryVarModelQueue.Count)); {
else if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count)); @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 else
{ {
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) return new($"Failed to trigger webhook. Status code: {response.StatusCode}");
LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count));
} }
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 #region private
private async ValueTask<OperResult> Update(IEnumerable<TopicArray> topicArrayList, CancellationToken cancellationToken) private ValueTask<OperResult> Update(IEnumerable<TopicArray> topicArrayList, CancellationToken cancellationToken)
{ {
foreach (var topicArray in topicArrayList) return Update(this, topicArrayList, cancellationToken);
{
var result = await WebhookUpAsync(topicArray, cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested) static async PooledValueTask<OperResult> Update(Webhook @this, IEnumerable<TopicArray> topicArrayList, CancellationToken cancellationToken)
return result; {
if (success != result.IsSuccess) 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) if (!result.IsSuccess)
{ {
LogMessage?.LogWarning(result.ToString()); return result;
} }
success = result.IsSuccess;
}
if (!result.IsSuccess)
{
return result;
} }
return OperResult.Success;
} }
return OperResult.Success;
} }

View File

@@ -10,6 +10,8 @@
using Confluent.Kafka; using Confluent.Kafka;
using PooledAwait;
using ThingsGateway.Extension.Generic; using ThingsGateway.Extension.Generic;
using ThingsGateway.Foundation; using ThingsGateway.Foundation;
using ThingsGateway.Foundation.Extension.Generic; using ThingsGateway.Foundation.Extension.Generic;
@@ -145,117 +147,128 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScriptAll
#region private #region private
private async ValueTask<OperResult> Update(IEnumerable<TopicArray> topicArrayList, CancellationToken cancellationToken) private ValueTask<OperResult> Update(IEnumerable<TopicArray> topicArrayList, CancellationToken cancellationToken)
{ {
foreach (var topicArray in topicArrayList) return Update(this, topicArrayList, cancellationToken);
static async PooledValueTask<OperResult> Update(KafkaProducer @this, IEnumerable<TopicArray> topicArrayList, CancellationToken cancellationToken)
{ {
var result = await KafKaUpAsync(topicArray, cancellationToken).ConfigureAwait(false); foreach (var topicArray in topicArrayList)
if (success != result.IsSuccess)
{ {
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) 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<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken) private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken)
{ {
var topicArrayList = GetAlarmTopicArrays(item); var topicArrayList = GetAlarmTopicArrays(item);
return await Update(topicArrayList, cancellationToken).ConfigureAwait(false); return Update(topicArrayList, cancellationToken);
} }
private async ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken) private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken)
{ {
var topicArrayList = GetDeviceTopicArray(item); var topicArrayList = GetDeviceTopicArray(item);
return await Update(topicArrayList, cancellationToken).ConfigureAwait(false); return Update(topicArrayList, cancellationToken);
} }
private async ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken) private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
{ {
var topicArrayList = GetVariableBasicDataTopicArray(item.WhereIf(_driverPropertys.OnlineFilter, a => a.IsOnline == true)); var topicArrayList = GetVariableBasicDataTopicArray(item.WhereIf(_driverPropertys.OnlineFilter, a => a.IsOnline == true));
return await Update(topicArrayList, cancellationToken).ConfigureAwait(false); return Update(topicArrayList, cancellationToken);
} }
#endregion private #endregion private
#region #region
private async Task AllPublishAsync(CancellationToken cancellationToken) private async Task AllPublishAsync(CancellationToken cancellationToken)
{ {
//保留消息 //保留消息
//分解List避免超出字节大小限制 //分解List避免超出字节大小限制
var varData = IdVariableRuntimes.Select(a => a.Value).AdaptIEnumerableVariableBasicData().ChunkBetter(_driverPropertys.SplitSize); var varData = this.IdVariableRuntimes.Select(a => a.Value).AdaptIEnumerableVariableBasicData().ChunkBetter(this._driverPropertys.SplitSize);
var devData = CollectDevices?.Select(a => a.Value).AdaptIEnumerableDeviceBasicData().ChunkBetter(_driverPropertys.SplitSize); var devData = this.CollectDevices?.Select(a => a.Value).AdaptIEnumerableDeviceBasicData().ChunkBetter(this._driverPropertys.SplitSize);
var alramData = GlobalData.ReadOnlyRealAlarmIdVariables.Select(a => a.Value).ChunkBetter(_driverPropertys.SplitSize); var alramData = GlobalData.ReadOnlyRealAlarmIdVariables.Select(a => a.Value).ChunkBetter(this._driverPropertys.SplitSize);
foreach (var item in varData) foreach (var item in varData)
{ {
if (!success) if (!this.success)
break; break;
await UpdateVarModel(item, cancellationToken).ConfigureAwait(false); await this.UpdateVarModel(item, cancellationToken).ConfigureAwait(false);
} }
if (devData != null) if (devData != null)
{ {
foreach (var item in devData) foreach (var item in devData)
{ {
if (!success) if (!this.success)
break; break;
await UpdateDevModel(item, cancellationToken).ConfigureAwait(false); await this.UpdateDevModel(item, cancellationToken).ConfigureAwait(false);
} }
} }
foreach (var item in alramData) foreach (var item in alramData)
{ {
if (!success) if (!this.success)
break; break;
await UpdateAlarmModel(item, cancellationToken).ConfigureAwait(false); await this.UpdateAlarmModel(item, cancellationToken).ConfigureAwait(false);
} }
} }
/// <summary> /// <summary>
/// kafka上传返回上传结果 /// kafka上传返回上传结果
/// </summary> /// </summary>
public async ValueTask<OperResult> KafKaUpAsync(TopicArray topicArray, CancellationToken cancellationToken) public ValueTask<OperResult> KafKaUpAsync(TopicArray topicArray, CancellationToken cancellationToken)
{ {
try return KafKaUpAsync(this, topicArray, cancellationToken);
static async PooledValueTask<OperResult> KafKaUpAsync(KafkaProducer @this, TopicArray topicArray, CancellationToken cancellationToken)
{ {
using CancellationTokenSource cancellationTokenSource = new(_driverPropertys.Timeout); try
using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, cancellationToken);
var result = await _producer.ProduceAsync(topicArray.Topic, new Message<Null, byte[]> { Value = topicArray.Payload }, stoppingToken.Token).ConfigureAwait(false);
if (result.Status != PersistenceStatus.Persisted)
{ {
return new OperResult("Upload fail"); using CancellationTokenSource cancellationTokenSource = new(@this._driverPropertys.Timeout);
} using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, cancellationToken);
else var result = await @this._producer.ProduceAsync(topicArray.Topic, new Message<Null, byte[]> { Value = topicArray.Payload }, stoppingToken.Token).ConfigureAwait(false);
{ if (result.Status != PersistenceStatus.Persisted)
if (_driverPropertys.DetailLog)
{ {
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) return new OperResult("Upload fail");
LogMessage?.LogTrace(GetDetailLogString(topicArray, _memoryVarModelQueue.Count));
else if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug)
LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count));
} }
else else
{ {
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) if (@this._driverPropertys.DetailLog)
LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count)); {
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)
catch (OperationCanceledException) {
{ return new OperResult("Timeout");
return new OperResult("Timeout"); }
} catch (Exception ex)
catch (Exception ex) {
{ return new OperResult(ex);
return new OperResult(ex); }
} }
} }

View File

@@ -10,6 +10,8 @@
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using PooledAwait;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using ThingsGateway.Foundation.Modbus; using ThingsGateway.Foundation.Modbus;
@@ -151,95 +153,108 @@ public class ModbusSlave : BusinessBase
await base.DisposeAsync(disposing).ConfigureAwait(false); await base.DisposeAsync(disposing).ConfigureAwait(false);
} }
protected override async Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) protected override Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken)
{ {
//获取设备连接状态 return ProtectedExecuteAsync(this, cancellationToken);
if (!IsConnected())
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) if (cancellationToken.IsCancellationRequested)
return; break;
await _plc.ConnectAsync(cancellationToken).ConfigureAwait(false); if (!@this.IdVariableRuntimes.TryGetValue(item.Value, out var variableRuntime))
success = true; break;
}
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;
var type = variableRuntime.GetPropertyValue(CurrentDevice.Id, nameof(ModbusSlaveVariableProperty.DataType)); var type = variableRuntime.GetPropertyValue(@this.CurrentDevice.Id, nameof(ModbusSlaveVariableProperty.DataType));
if (Enum.TryParse(type, out DataTypeEnum result)) if (Enum.TryParse(type, out DataTypeEnum result))
{ {
await _plc.WriteJTokenAsync(item.Key, (variableRuntime.Value).GetJTokenFromObj(), result, cancellationToken).ConfigureAwait(false); await @this._plc.WriteJTokenAsync(item.Key, (variableRuntime.Value).GetJTokenFromObj(), result, cancellationToken).ConfigureAwait(false);
} }
else else
{ {
await _plc.WriteJTokenAsync(item.Key, (variableRuntime.Value).GetJTokenFromObj(), variableRuntime.DataType, cancellationToken).ConfigureAwait(false); await @this._plc.WriteJTokenAsync(item.Key, (variableRuntime.Value).GetJTokenFromObj(), variableRuntime.DataType, cancellationToken).ConfigureAwait(false);
}
} }
return;
} }
} }
/// <summary> /// <summary>
/// RPC写入 /// RPC写入
/// </summary> /// </summary>
private async ValueTask<IOperResult> OnWriteData(ModbusRequest modbusRequest, IThingsGatewayBitConverter bitConverter, IChannel channel) private ValueTask<IOperResult> OnWriteData(ModbusRequest modbusRequest, IThingsGatewayBitConverter bitConverter, IChannel channel)
{ {
try return OnWriteData(this, modbusRequest, bitConverter, channel);
static async PooledValueTask<IOperResult> 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(); try
if (tag.Length == 0) return OperResult.Success;
foreach (var item in tag)
{ {
if (!(item.Value.GetPropertyValue(DeviceId, nameof(_variablePropertys.VariableRpcEnable)).ToBoolean(false) && _driverPropertys.DeviceRpcEnable)) var tag = @this.ModbusVariables.Where(a => a.Key?.StartAddress == modbusRequest.StartAddress && a.Key?.Station == modbusRequest.Station && a.Key?.FunctionCode == modbusRequest.FunctionCode).ToArray();
return new OperResult("Not Permitted to Write"); 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); foreach (var item in tag)
var bitIndex = _plc.GetBitOffset(addressStr);
if (modbusRequest.FunctionCode == 0x03 && dType == DataTypeEnum.Boolean && bitIndex != null)
{ {
var int16Data = thingsGatewayBitConverter.ToUInt16(modbusRequest.MasterWriteDatas.Span, 0); if (!(item.Value.GetPropertyValue(@this.DeviceId, nameof(_variablePropertys.VariableRpcEnable)).ToBoolean(false) && @this._driverPropertys.DeviceRpcEnable))
var wData = BitHelper.GetBit(int16Data, bitIndex.Value); 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) var thingsGatewayBitConverter = bitConverter.GetTransByAddress(addressStr);
return result;
} var bitIndex = @this._plc.GetBitOffset(addressStr);
else if (modbusRequest.FunctionCode == 0x03 && dType == DataTypeEnum.Boolean && bitIndex != null)
{ {
_ = thingsGatewayBitConverter.GetChangedDataFormBytes(_plc, addressStr, modbusRequest.MasterWriteDatas.Span, 0, dType, item.Value.ArrayLength ?? 1, null, out var data); var int16Data = thingsGatewayBitConverter.ToUInt16(modbusRequest.MasterWriteDatas.Span, 0);
var wData = BitHelper.GetBit(int16Data, bitIndex.Value);
var result = await item.Value.RpcAsync(data.ToSystemTextJsonString(), $"{nameof(ModbusSlave)}-{CurrentDevice.Name}-{$"{channel}"}").ConfigureAwait(false);
var result = await item.Value.RpcAsync(wData.ToSystemTextJsonString(), $"{nameof(ModbusSlave)}-{@this.CurrentDevice.Name}-{$"{channel}"}").ConfigureAwait(false);
if (!result.IsSuccess)
return result; 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);
} }
} }

View File

@@ -10,6 +10,9 @@
using MQTTnet; 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); return ProtectedExecuteAsync(this, cancellationToken);
if (!clientResult.IsSuccess)
static async PooledTask ProtectedExecuteAsync(MqttClient @this, CancellationToken cancellationToken)
{ {
if (cancellationToken.IsCancellationRequested) var clientResult = await @this.TryMqttClientAsync(cancellationToken).ConfigureAwait(false);
return; if (!clientResult.IsSuccess)
if (success != clientResult.IsSuccess)
{ {
if (!clientResult.IsSuccess) if (cancellationToken.IsCancellationRequested)
LogMessage?.LogWarning(clientResult.Exception, clientResult.ErrorMessage); return;
success = clientResult.IsSuccess; 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); //TD设备上线
//return;
}
//TD设备上线
var data = ThingsBoardDeviceConnectQueue.ToListWithDequeue(); var data = @this.ThingsBoardDeviceConnectQueue.ToListWithDequeue();
foreach (var item in data) foreach (var item in data)
{ {
await UpdateThingsBoardDeviceConnect(item).ConfigureAwait(false); await @this.UpdateThingsBoardDeviceConnect(item).ConfigureAwait(false);
} }
await Update(cancellationToken).ConfigureAwait(false); await @this.Update(cancellationToken).ConfigureAwait(false);
return;
}
} }

View File

@@ -20,6 +20,8 @@ using MQTTnet.Client;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using PooledAwait;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Text; using System.Text;
@@ -30,6 +32,7 @@ using ThingsGateway.NewLife;
using ThingsGateway.NewLife.Extension; using ThingsGateway.NewLife.Extension;
using ThingsGateway.NewLife.Json.Extension; using ThingsGateway.NewLife.Json.Extension;
namespace ThingsGateway.Plugin.Mqtt; namespace ThingsGateway.Plugin.Mqtt;
/// <summary> /// <summary>
@@ -222,27 +225,32 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScriptAll
#region private #region private
private async ValueTask<OperResult> Update(IEnumerable<TopicArray> topicArrayList, CancellationToken cancellationToken) private ValueTask<OperResult> Update(IEnumerable<TopicArray> topicArrayList, CancellationToken cancellationToken)
{ {
foreach (TopicArray topicArray in topicArrayList) return Update(this, topicArrayList, cancellationToken);
static async PooledValueTask<OperResult> Update(MqttClient @this, IEnumerable<TopicArray> topicArrayList, CancellationToken cancellationToken)
{ {
var result = await MqttUpAsync(topicArray, cancellationToken).ConfigureAwait(false); foreach (TopicArray topicArray in topicArrayList)
if (cancellationToken.IsCancellationRequested)
return result;
if (success != result.IsSuccess)
{ {
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) 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<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken) private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken)
@@ -265,96 +273,102 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScriptAll
#endregion private #endregion private
private async ValueTask AllPublishAsync(CancellationToken cancellationToken) private async Task AllPublishAsync(CancellationToken cancellationToken)
{ {
//保留消息 //保留消息
//分解List避免超出mqtt字节大小限制 //分解List避免超出mqtt字节大小限制
var varData = IdVariableRuntimes.Select(a => a.Value).AdaptIEnumerableVariableBasicData().ChunkBetter(_driverPropertys.SplitSize); var varData = this.IdVariableRuntimes.Select(a => a.Value).AdaptIEnumerableVariableBasicData().ChunkBetter(this._driverPropertys.SplitSize);
var devData = CollectDevices?.Select(a => a.Value).AdaptIEnumerableDeviceBasicData().ChunkBetter(_driverPropertys.SplitSize); var devData = this.CollectDevices?.Select(a => a.Value).AdaptIEnumerableDeviceBasicData().ChunkBetter(this._driverPropertys.SplitSize);
var alramData = GlobalData.ReadOnlyRealAlarmIdVariables.Select(a => a.Value).ChunkBetter(_driverPropertys.SplitSize); var alramData = GlobalData.ReadOnlyRealAlarmIdVariables.Select(a => a.Value).ChunkBetter(this._driverPropertys.SplitSize);
foreach (var item in varData) foreach (var item in varData)
{ {
if (!success) if (!this.success)
break; break;
await UpdateVarModel(item, cancellationToken).ConfigureAwait(false); await this.UpdateVarModel(item, cancellationToken).ConfigureAwait(false);
} }
if (devData != null) if (devData != null)
{ {
foreach (var item in devData) foreach (var item in devData)
{ {
if (!success) if (!this.success)
break; break;
await UpdateDevModel(item, cancellationToken).ConfigureAwait(false); await this.UpdateDevModel(item, cancellationToken).ConfigureAwait(false);
} }
} }
foreach (var item in alramData) foreach (var item in alramData)
{ {
if (!success) if (!this.success)
break; break;
await UpdateAlarmModel(item, cancellationToken).ConfigureAwait(false); await this.UpdateAlarmModel(item, cancellationToken).ConfigureAwait(false);
} }
} }
private async ValueTask<Dictionary<string, Dictionary<string, IOperResult>>> GetRpcResult(string clientId, Dictionary<string, Dictionary<string, JToken>> rpcDatas) private ValueTask<Dictionary<string, Dictionary<string, IOperResult>>> GetRpcResult(string clientId, Dictionary<string, Dictionary<string, JToken>> rpcDatas)
{ {
var mqttRpcResult = new Dictionary<string, Dictionary<string, IOperResult>>(); return GetRpcResult(this, clientId, rpcDatas);
rpcDatas.ForEach(a => mqttRpcResult.Add(a.Key, new()));
try static async PooledValueTask<Dictionary<string, Dictionary<string, IOperResult>>> GetRpcResult(MqttClient @this, string clientId, Dictionary<string, Dictionary<string, JToken>> rpcDatas)
{ {
foreach (var rpcData in rpcDatas) var mqttRpcResult = new Dictionary<string, Dictionary<string, IOperResult>>();
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 (device.ReadOnlyVariableRuntimes.TryGetValue(item.Key, out var variable) && @this.IdVariableRuntimes.TryGetValue(variable.Id, out var tag))
if (rpcEnable == false)
{ {
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<string, Dictionary<string, string>> 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<string, Dictionary<string, string>> writeData = new(); var result = await GlobalData.RpcService.InvokeDeviceMethodAsync(@this.ToString() + "-" + clientId,
foreach (var item in rpcDatas) writeData).ConfigureAwait(false);
{
writeData.Add(item.Key, new());
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);
} }
} }
} }
catch (Exception ex)
var result = await GlobalData.RpcService.InvokeDeviceMethodAsync(ToString() + "-" + clientId,
writeData).ConfigureAwait(false);
foreach (var dictKv in result)
{ {
foreach (var item in dictKv.Value) @this.LogMessage?.LogWarning(ex);
{
mqttRpcResult[dictKv.Key].TryAdd(item.Key, item.Value);
}
} }
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex);
}
return mqttRpcResult; return mqttRpcResult;
}
} }
private async Task MqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs args) private async Task MqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs args)
@@ -482,77 +496,82 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScriptAll
/// <summary> /// <summary>
/// 上传mqtt返回上传结果 /// 上传mqtt返回上传结果
/// </summary> /// </summary>
public async ValueTask<OperResult> MqttUpAsync(TopicArray topicArray, CancellationToken cancellationToken = default) public ValueTask<OperResult> MqttUpAsync(TopicArray topicArray, CancellationToken cancellationToken = default)
{ {
try return MqttUpAsync(this, topicArray, cancellationToken);
static async PooledValueTask<OperResult> MqttUpAsync(MqttClient @this, TopicArray topicArray, CancellationToken cancellationToken)
{ {
var isConnect = await TryMqttClientAsync(cancellationToken).ConfigureAwait(false); try
if (isConnect.IsSuccess)
{ {
var variableMessage = new MqttApplicationMessageBuilder() var isConnect = await @this.TryMqttClientAsync(cancellationToken).ConfigureAwait(false);
.WithTopic(topicArray.Topic).WithQualityOfServiceLevel(_driverPropertys.MqttQualityOfServiceLevel).WithRetainFlag() if (isConnect.IsSuccess)
.WithPayload(topicArray.Payload).Build();
var result = await _mqttClient.PublishAsync(variableMessage, cancellationToken).ConfigureAwait(false);
if (result.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) if (@this._driverPropertys.DetailLog)
LogMessage?.LogTrace(GetDetailLogString(topicArray, _memoryVarModelQueue.Count)); {
else if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count)); @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 else
{ {
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) return new OperResult($"Upload fail{result.ReasonString}");
LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count));
} }
return OperResult.Success;
} }
else 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<OperResult> TryMqttClientAsync(CancellationToken cancellationToken) private ValueTask<OperResult> 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) if (_mqttClient?.IsConnected == true)
return OperResult.Success; return TouchSocket.Core.EasyValueTask.FromResult(OperResult.Success);
return await Client().ConfigureAwait(false); return Client(this, cancellationToken);
async ValueTask<OperResult> Client() static async PooledValueTask<OperResult> Client(MqttClient @this, CancellationToken cancellationToken)
{ {
if (_mqttClient?.IsConnected == true) if (@this._mqttClient?.IsConnected == true)
return OperResult.Success; return OperResult.Success;
try try
{ {
await ConnectLock.WaitAsync(cancellationToken).ConfigureAwait(false); await @this.ConnectLock.WaitAsync(cancellationToken).ConfigureAwait(false);
await Task.Delay(100, cancellationToken).ConfigureAwait(false); await Task.Delay(100, cancellationToken).ConfigureAwait(false);
if (_mqttClient?.IsConnected == true) if (@this._mqttClient?.IsConnected == true)
return OperResult.Success; 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); using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutToken.Token);
if (_mqttClient?.IsConnected == true) if (@this._mqttClient?.IsConnected == true)
return OperResult.Success; return OperResult.Success;
if (_mqttClient == null) if (@this._mqttClient == null)
{ {
return new OperResult("mqttClient is null"); return new OperResult("mqttClient is null");
} }
var result = await _mqttClient.ConnectAsync(_mqttClientOptions, stoppingToken.Token).ConfigureAwait(false); var result = await @this._mqttClient.ConnectAsync(@this._mqttClientOptions, stoppingToken.Token).ConfigureAwait(false);
if (_mqttClient.IsConnected) if (@this._mqttClient.IsConnected)
{ {
return OperResult.Success; return OperResult.Success;
} }
@@ -570,7 +589,7 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScriptAll
} }
finally finally
{ {
ConnectLock.Release(); @this.ConnectLock.Release();
} }
} }
} }

View File

@@ -10,6 +10,9 @@
using MQTTnet; using MQTTnet;
using PooledAwait;
#if NET6_0 #if NET6_0
using MQTTnet.Client; using MQTTnet.Client;
#endif #endif
@@ -272,21 +275,29 @@ public partial class MqttCollect : CollectBase
return list; return list;
} }
private async Task CheckAsync(object? state, CancellationToken cancellationToken) private Task CheckAsync(object? state, CancellationToken cancellationToken)
{ {
var clientResult = await TryMqttClientAsync(cancellationToken).ConfigureAwait(false); return CheckAsync(this, cancellationToken);
if (!clientResult.IsSuccess)
static async PooledTask CheckAsync(MqttCollect @this, CancellationToken cancellationToken)
{ {
if (cancellationToken.IsCancellationRequested) var clientResult = await @this.TryMqttClientAsync(cancellationToken).ConfigureAwait(false);
return; if (!clientResult.IsSuccess)
if (success != clientResult.IsSuccess)
{ {
if (!clientResult.IsSuccess) if (cancellationToken.IsCancellationRequested)
LogMessage?.LogWarning(clientResult.Exception, clientResult.ErrorMessage); return;
success = clientResult.IsSuccess; 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;
} }
} }

View File

@@ -10,6 +10,9 @@
using MQTTnet; using MQTTnet;
using PooledAwait;
#if NET6_0 #if NET6_0
using MQTTnet.Client; using MQTTnet.Client;
#endif #endif
@@ -129,32 +132,34 @@ public partial class MqttCollect : CollectBase
} }
} }
private async ValueTask<OperResult> TryMqttClientAsync(CancellationToken cancellationToken) private ValueTask<OperResult> TryMqttClientAsync(CancellationToken cancellationToken)
{ {
if (_mqttClient?.IsConnected == true) if (DisposedValue || _mqttClient == null) return TouchSocket.Core.EasyValueTask.FromResult(new OperResult("MqttClient is disposed"));
return OperResult.Success;
return await Client().ConfigureAwait(false);
async ValueTask<OperResult> Client() if (_mqttClient?.IsConnected == true)
return TouchSocket.Core.EasyValueTask.FromResult(OperResult.Success);
return Client(this, cancellationToken);
static async PooledValueTask<OperResult> Client(MqttCollect @this, CancellationToken cancellationToken)
{ {
if (_mqttClient?.IsConnected == true) if (@this._mqttClient?.IsConnected == true)
return OperResult.Success; return OperResult.Success;
try try
{ {
await ConnectLock.WaitAsync(cancellationToken).ConfigureAwait(false); await @this.ConnectLock.WaitAsync(cancellationToken).ConfigureAwait(false);
await Task.Delay(100, cancellationToken).ConfigureAwait(false); await Task.Delay(100, cancellationToken).ConfigureAwait(false);
if (_mqttClient?.IsConnected == true) if (@this._mqttClient?.IsConnected == true)
return OperResult.Success; 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); using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutToken.Token);
if (_mqttClient?.IsConnected == true) if (@this._mqttClient?.IsConnected == true)
return OperResult.Success; return OperResult.Success;
if (_mqttClient == null) if (@this._mqttClient == null)
{ {
return new OperResult("mqttClient is null"); return new OperResult("mqttClient is null");
} }
var result = await _mqttClient.ConnectAsync(_mqttClientOptions, stoppingToken.Token).ConfigureAwait(false); var result = await @this._mqttClient.ConnectAsync(@this._mqttClientOptions, stoppingToken.Token).ConfigureAwait(false);
if (_mqttClient.IsConnected) if (@this._mqttClient.IsConnected)
{ {
return OperResult.Success; return OperResult.Success;
} }
@@ -172,7 +177,7 @@ public partial class MqttCollect : CollectBase
} }
finally finally
{ {
ConnectLock.Release(); @this.ConnectLock.Release();
} }
} }
} }

View File

@@ -20,6 +20,8 @@ using MQTTnet.Server;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using PooledAwait;
using System.Text; using System.Text;
using ThingsGateway.Admin.Application; using ThingsGateway.Admin.Application;
@@ -165,25 +167,30 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScriptAll
} }
#region private #region private
private async ValueTask<OperResult> Update(IEnumerable<TopicArray> topicArrayList, CancellationToken cancellationToken) private ValueTask<OperResult> Update(IEnumerable<TopicArray> topicArrayList, CancellationToken cancellationToken)
{ {
foreach (var topicArray in topicArrayList) return Update(this, topicArrayList, cancellationToken);
static async PooledValueTask<OperResult> Update(MqttServer @this, IEnumerable<TopicArray> topicArrayList, CancellationToken cancellationToken)
{ {
var result = await MqttUpAsync(topicArray, cancellationToken).ConfigureAwait(false); foreach (var topicArray in topicArrayList)
if (success != result.IsSuccess)
{ {
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) 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<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken) private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken)
@@ -207,65 +214,70 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScriptAll
#endregion private #endregion private
private async ValueTask<Dictionary<string, Dictionary<string, IOperResult>>> GetRpcResult(string clientId, Dictionary<string, Dictionary<string, JToken>> rpcDatas) private ValueTask<Dictionary<string, Dictionary<string, IOperResult>>> GetRpcResult(string clientId, Dictionary<string, Dictionary<string, JToken>> rpcDatas)
{ {
var mqttRpcResult = new Dictionary<string, Dictionary<string, IOperResult>>(); return GetRpcResult(this, clientId, rpcDatas);
rpcDatas.ForEach(a => mqttRpcResult.Add(a.Key, new()));
try static async PooledValueTask<Dictionary<string, Dictionary<string, IOperResult>>> GetRpcResult(MqttServer @this, string clientId, Dictionary<string, Dictionary<string, JToken>> rpcDatas)
{ {
foreach (var rpcData in rpcDatas) var mqttRpcResult = new Dictionary<string, Dictionary<string, IOperResult>>();
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 (device.ReadOnlyVariableRuntimes.TryGetValue(item.Key, out var variable) && @this.IdVariableRuntimes.TryGetValue(variable.Id, out var tag))
if (rpcEnable == false)
{ {
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<string, Dictionary<string, string>> 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<string, Dictionary<string, string>> writeData = new(); var result = await GlobalData.RpcService.InvokeDeviceMethodAsync(@this.ToString() + "-" + clientId,
foreach (var item in rpcDatas) writeData).ConfigureAwait(false);
{
writeData.Add(item.Key, new());
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);
} }
} }
} }
catch (Exception ex)
var result = await GlobalData.RpcService.InvokeDeviceMethodAsync(ToString() + "-" + clientId,
writeData).ConfigureAwait(false);
foreach (var dictKv in result)
{ {
foreach (var item in dictKv.Value) @this.LogMessage?.LogWarning(ex);
{
mqttRpcResult[dictKv.Key].TryAdd(item.Key, item.Value);
}
} }
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex);
}
return mqttRpcResult; return mqttRpcResult;
}
} }
private List<MqttApplicationMessage> GetRetainedMessages() private List<MqttApplicationMessage> GetRetainedMessages()
@@ -328,14 +340,19 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScriptAll
return Task.CompletedTask; 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 #if NET8_0_OR_GREATER
var payload = args.ApplicationMessage.Payload; var payload = args.ApplicationMessage.Payload;
var payloadCount = payload.Length; var payloadCount = payload.Length;
#else #else
var payload = args.ApplicationMessage.PayloadSegment; var payload = args.ApplicationMessage.PayloadSegment;
@@ -343,53 +360,56 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScriptAll
#endif #endif
if (args.ApplicationMessage.Topic == _driverPropertys.RpcQuestTopic && payloadCount > 0) if (args.ApplicationMessage.Topic == @this._driverPropertys.RpcQuestTopic && payloadCount > 0)
{
var data = GetRetainedMessages();
foreach (var item in data)
{ {
await _mqttServer.InjectApplicationMessage(new InjectedMqttApplicationMessage(item)).ConfigureAwait(false); var data = @this.GetRetainedMessages();
}
return;
}
if (!_driverPropertys.DeviceRpcEnable || string.IsNullOrEmpty(args.ClientId)) foreach (var item in data)
return; {
await @this._mqttServer.InjectApplicationMessage(new InjectedMqttApplicationMessage(item)).ConfigureAwait(false);
if (!_driverPropertys.BigTextScriptRpc.IsNullOrEmpty()) }
{
var rpcBase = CSharpScriptEngineExtension.Do<DynamicMqttServerRpcBase>(_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)
return; return;
var rpcDatas = Encoding.UTF8.GetString(payload).FromJsonNetString<Dictionary<string, Dictionary<string, JToken>>>();
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<DynamicMqttServerRpcBase>(@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<Dictionary<string, Dictionary<string, JToken>>>();
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)
catch (Exception ex) {
{ @this.LogMessage?.LogWarning(ex, $"MqttServer_InterceptingPublishAsync error");
LogMessage?.LogWarning(ex, $"MqttServer_InterceptingPublishAsync error"); }
return;
} }
} }
@@ -432,33 +452,38 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScriptAll
/// <summary> /// <summary>
/// 上传mqtt返回上传结果 /// 上传mqtt返回上传结果
/// </summary> /// </summary>
public async ValueTask<OperResult> MqttUpAsync(TopicArray topicArray, CancellationToken cancellationToken = default) public ValueTask<OperResult> MqttUpAsync(TopicArray topicArray, CancellationToken cancellationToken = default)
{ {
try return MqttUpAsync(this, topicArray, cancellationToken);
{
var message = new MqttApplicationMessageBuilder()
.WithTopic(topicArray.Topic).WithQualityOfServiceLevel(_driverPropertys.MqttQualityOfServiceLevel).WithRetainFlag()
.WithPayload(topicArray.Payload).Build();
await _mqttServer.InjectApplicationMessage(
new InjectedMqttApplicationMessage(message), cancellationToken).ConfigureAwait(false);
if (_driverPropertys.DetailLog) static async PooledValueTask<OperResult> MqttUpAsync(MqttServer @this, TopicArray topicArray, CancellationToken cancellationToken)
{
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)
{ {
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);
}
} }
} }

View File

@@ -160,7 +160,6 @@ public class OpcDaMaster : CollectBase
protected override async ValueTask<Dictionary<string, OperResult>> WriteValuesAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken) protected override async ValueTask<Dictionary<string, OperResult>> WriteValuesAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
{ {
using var writeLock = await ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false); 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 result = _plc.WriteItem(writeInfoLists.ToDictionary(a => a.Key.RegisterAddress!, a => a.Value.GetObjectFromJToken()!));
var results = new NonBlockingDictionary<string, OperResult>(result.ToDictionary<KeyValuePair<string, Tuple<bool, string>>, string, OperResult>(a => writeInfoLists.Keys.FirstOrDefault(b => b.RegisterAddress == a.Key).Name, a => var results = new NonBlockingDictionary<string, OperResult>(result.ToDictionary<KeyValuePair<string, Tuple<bool, string>>, string, OperResult>(a => writeInfoLists.Keys.FirstOrDefault(b => b.RegisterAddress == a.Key).Name, a =>
{ {

View File

@@ -13,6 +13,8 @@ using Newtonsoft.Json.Linq;
using Opc.Ua; using Opc.Ua;
using Opc.Ua.Client; using Opc.Ua.Client;
using PooledAwait;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using ThingsGateway.Foundation.Extension.Generic; using ThingsGateway.Foundation.Extension.Generic;
@@ -150,50 +152,58 @@ public class OpcUaMaster : CollectBase
} }
private volatile bool checkLog; 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 (@this._driverProperties.ActiveSubscribe)
if (IsConnected())
{ {
//更新设备活动时间 //获取设备连接状态
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; 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) @this.LogMessage?.LogInformation("AddSubscriptions done");
{
if (!checkLog)
LogMessage?.LogWarning(ex, "AddSubscriptions error");
checkLog = false;
}
} }
LogMessage?.LogInformation("AddSubscriptions done");
} }
} }
} }
} }
return;
} }
} }
@@ -230,51 +240,56 @@ public class OpcUaMaster : CollectBase
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override async ValueTask<OperResult<ReadOnlyMemory<byte>>> ReadSourceAsync(VariableSourceRead deviceVariableSourceRead, CancellationToken cancellationToken) protected override ValueTask<OperResult<ReadOnlyMemory<byte>>> ReadSourceAsync(VariableSourceRead deviceVariableSourceRead, CancellationToken cancellationToken)
{ {
DateTime time = DateTime.Now; return ReadSourceAsync(this, deviceVariableSourceRead, cancellationToken);
var addresss = deviceVariableSourceRead.VariableRuntimes.Where(a => !a.RegisterAddress.IsNullOrEmpty()).Select(a => a.RegisterAddress!).ToArray();
try static async PooledValueTask<OperResult<ReadOnlyMemory<byte>>> ReadSourceAsync(OpcUaMaster @this, VariableSourceRead deviceVariableSourceRead, CancellationToken cancellationToken)
{ {
var result = await _plc.ReadJTokenValueAsync(addresss, cancellationToken).ConfigureAwait(false); DateTime time = DateTime.Now;
foreach (var data in result) 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); if (!cancellationToken.IsCancellationRequested)
foreach (var item in data1)
{ {
object value = data.Item3.GetObjectFromJToken(); var data1 = deviceVariableSourceRead.VariableRuntimes.Where(a => a.RegisterAddress == data.Item1);
var isGood = StatusCode.IsGood(data.Item2.StatusCode); foreach (var item in data1)
if (_driverProperties.SourceTimestampEnable)
{ {
time = data.Item2.SourceTimestamp.ToLocalTime(); object value = data.Item3.GetObjectFromJToken();
}
if (isGood) var isGood = StatusCode.IsGood(data.Item2.StatusCode);
{ if (@this._driverProperties.SourceTimestampEnable)
item.SetValue(value, time);
}
else
{
if (item is VariableRuntime variable && (variable.IsOnline || variable.CollectTime == DateTime.UnixEpoch.ToLocalTime()))
{ {
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<ReadOnlyMemory<byte>>(null); return OperResult.CreateSuccessResult<ReadOnlyMemory<byte>>(null);
} }
catch (Exception ex) catch (Exception ex)
{ {
return new OperResult<ReadOnlyMemory<byte>>($"ReadSourceAsync {addresss.ToSystemTextJsonString()}{Environment.NewLine}{ex}"); return new OperResult<ReadOnlyMemory<byte>>($"ReadSourceAsync {addresss.ToSystemTextJsonString()}{Environment.NewLine}{ex}");
}
} }
} }

View File

@@ -15,6 +15,8 @@ using Opc.Ua;
using Opc.Ua.Bindings; using Opc.Ua.Bindings;
using Opc.Ua.Configuration; using Opc.Ua.Configuration;
using PooledAwait;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Reflection; using System.Reflection;
@@ -197,56 +199,61 @@ public partial class OpcUaServer : BusinessBase
await base.ProtectedStartAsync(cancellationToken).ConfigureAwait(false); 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); try
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)
{ {
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;
} }
} }

View File

@@ -8,6 +8,8 @@
// QQ群605534569 // QQ群605534569
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using PooledAwait;
using RabbitMQ.Client; using RabbitMQ.Client;
using ThingsGateway.Foundation; using ThingsGateway.Foundation;
@@ -64,39 +66,44 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScriptAll
await base.DisposeAsync(disposing).ConfigureAwait(false); 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)
{ {
// 创建连接 try
_connection ??= await _connectionFactory.CreateConnectionAsync(cancellationToken).ConfigureAwait(false);
// 创建通道
_channel ??= await _connection.CreateChannelAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
// 声明路由队列
if (_driverPropertys.IsQueueDeclare)
{ {
await (_channel?.QueueDeclareAsync(_driverPropertys.VariableTopic, true, false, false, cancellationToken: cancellationToken)).ConfigureAwait(false); // 创建连接
await (_channel?.QueueDeclareAsync(_driverPropertys.DeviceTopic, true, false, false, cancellationToken: cancellationToken)).ConfigureAwait(false); @this._connection ??= await @this._connectionFactory.CreateConnectionAsync(cancellationToken).ConfigureAwait(false);
await (_channel?.QueueDeclareAsync(_driverPropertys.AlarmTopic, true, false, false, cancellationToken: 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) await @this.Update(cancellationToken).ConfigureAwait(false);
{
LogMessage?.LogWarning(ex);
success = false;
}
await Task.Delay(10000, cancellationToken).ConfigureAwait(false);
} }
} }
else
{
await Update(cancellationToken).ConfigureAwait(false);
}
} }
#endif #endif

View File

@@ -8,6 +8,8 @@
// QQ群605534569 // QQ群605534569
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using PooledAwait;
using RabbitMQ.Client; using RabbitMQ.Client;
using ThingsGateway.Extension.Generic; using ThingsGateway.Extension.Generic;
@@ -148,26 +150,30 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScriptAll
#region private #region private
private async ValueTask<OperResult> Update(IEnumerable<TopicArray> topicArrayList, CancellationToken cancellationToken) private ValueTask<OperResult> Update(IEnumerable<TopicArray> topicArrayList, CancellationToken cancellationToken)
{ {
foreach (var topicArray in topicArrayList) return Update(this, topicArrayList, cancellationToken);
static async PooledValueTask<OperResult> Update(RabbitMQProducer @this, IEnumerable<TopicArray> topicArrayList, CancellationToken cancellationToken)
{ {
var result = await RabbitMQUpAsync(topicArray, cancellationToken).ConfigureAwait(false); foreach (var topicArray in topicArrayList)
if (success != result.IsSuccess)
{ {
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) 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<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken) private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken)
@@ -226,37 +232,42 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScriptAll
/// <summary> /// <summary>
/// 上传,返回上传结果 /// 上传,返回上传结果
/// </summary> /// </summary>
public async Task<OperResult> RabbitMQUpAsync(TopicArray topicArray, CancellationToken cancellationToken) public Task<OperResult> RabbitMQUpAsync(TopicArray topicArray, CancellationToken cancellationToken)
{ {
try return RabbitMQUpAsync(this, topicArray, cancellationToken);
{
if (_channel != null)
{
await _channel.BasicPublishAsync(_driverPropertys.ExchangeName, topicArray.Topic, topicArray.Payload, cancellationToken).ConfigureAwait(false);
if (_driverPropertys.DetailLog) static async PooledTask<OperResult> RabbitMQUpAsync(RabbitMQProducer @this, TopicArray topicArray, CancellationToken cancellationToken)
{
try
{
if (@this._channel != null)
{ {
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) await @this._channel.BasicPublishAsync(@this._driverPropertys.ExchangeName, topicArray.Topic, topicArray.Payload, cancellationToken).ConfigureAwait(false);
LogMessage?.LogTrace(GetDetailLogString(topicArray, _memoryVarModelQueue.Count));
else if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) if (@this._driverPropertys.DetailLog)
LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count)); {
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 else
{ {
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug) return new OperResult("Upload fail");
LogMessage?.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count));
} }
return OperResult.Success;
} }
else catch (Exception ex)
{ {
return new OperResult("Upload fail"); return new OperResult(ex);
} }
} }
catch (Exception ex)
{
return new OperResult(ex);
}
} }
#endregion #endregion

View File

@@ -10,6 +10,8 @@
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using PooledAwait;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using ThingsGateway.Debug; using ThingsGateway.Debug;
@@ -119,74 +121,79 @@ public class SiemensS7Master : CollectFoundationBase
} }
} }
protected override async ValueTask<Dictionary<string, OperResult>> WriteValuesAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken) protected override ValueTask<Dictionary<string, OperResult>> WriteValuesAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
{ {
using var writeLock = await ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false); return WriteValuesAsync(this, writeInfoLists, cancellationToken);
// 检查协议是否为空,如果为空则抛出异常 static async PooledValueTask<Dictionary<string, OperResult>> WriteValuesAsync(SiemensS7Master @this, Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
if (FoundationDevice == null)
throw new NotSupportedException();
// 创建用于存储操作结果的并发字典
NonBlockingDictionary<string, OperResult> operResults = new();
//转换
Dictionary<VariableRuntime, SiemensS7Address> 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)
{ {
SiemensS7Address siemensS7Address = SiemensS7Address.ParseFrom(item.Key.RegisterAddress); using var writeLock = await @this.ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false);
siemensS7Address.Data = GetBytes(item.Key.DataType, item.Value);
siemensS7Address.Length = siemensS7Address.Data.Length; // 检查协议是否为空,如果为空则抛出异常
siemensS7Address.BitLength = 1; if (@this.FoundationDevice == null)
siemensS7Address.IsBit = item.Key.DataType == DataTypeEnum.Boolean; throw new NotSupportedException();
if (item.Key.DataType == DataTypeEnum.Boolean)
// 创建用于存储操作结果的并发字典
NonBlockingDictionary<string, OperResult> operResults = new();
//转换
Dictionary<VariableRuntime, SiemensS7Address> 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<Boolean[]>().Length; if (item.Value is JArray jArray)
{
siemensS7Address.BitLength = jArray.ToObject<Boolean[]>().Length;
}
} }
addresses.Add(item.Key, siemensS7Address);
} }
addresses.Add(item.Key, siemensS7Address); if (addresses.Count > 0)
}
if (addresses.Count > 0)
{
var result = await _plc.S7WriteAsync(addresses.Select(a => a.Value).ToArray(), cancellationToken).ConfigureAwait(false);
foreach (var writeInfo in addresses)
{ {
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<string, OperResult>(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<string, OperResult>(operResults);
} }

View File

@@ -162,7 +162,7 @@ public class TestCollectPlugin1 : CollectBase
/// <param name="state"></param> /// <param name="state"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
protected override Task TestOnline(object? state, CancellationToken cancellationToken) protected override ValueTask TestOnline(object? state, CancellationToken cancellationToken)
{ {
return base.TestOnline(state, cancellationToken); return base.TestOnline(state, cancellationToken);
} }