mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-11-01 16:13:59 +08:00
Compare commits
8 Commits
10.11.114.
...
10.12.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21838bf4af | ||
|
|
6090108597 | ||
|
|
b47b9e6f43 | ||
|
|
18d1cffb2d | ||
|
|
516fd7f235 | ||
|
|
2ee16c3533 | ||
|
|
7d22f5c78e | ||
|
|
3e604ee2fd |
@@ -27,7 +27,7 @@ internal class CacheManager
|
||||
{
|
||||
private IMemoryCache Cache { get; set; }
|
||||
|
||||
private IServiceProvider Provider { get; set; }
|
||||
private static IServiceProvider Provider => App.RootServices;
|
||||
|
||||
[NotNull]
|
||||
private static CacheManager? Instance { get; set; }
|
||||
@@ -40,8 +40,7 @@ internal class CacheManager
|
||||
static CacheManager()
|
||||
{
|
||||
Instance = new();
|
||||
Instance.Provider = App.RootServices;
|
||||
Instance.Cache = Instance.Provider.GetRequiredService<IMemoryCache>();
|
||||
Instance.Cache = Provider.GetRequiredService<IMemoryCache>();
|
||||
Options = App.RootServices.GetRequiredService<IOptions<BootstrapBlazorOptions>>().Value;
|
||||
}
|
||||
|
||||
@@ -236,7 +235,7 @@ internal class CacheManager
|
||||
/// <returns></returns>
|
||||
public static IStringLocalizer? CreateLocalizerByType(Type resourceSource) => resourceSource.Assembly.IsDynamic
|
||||
? null
|
||||
: Instance.Provider.GetRequiredService<IStringLocalizerFactory>().Create(resourceSource);
|
||||
: Provider.GetRequiredService<IStringLocalizerFactory>().Create(resourceSource);
|
||||
|
||||
/// <summary>
|
||||
/// 获得 <see cref="JsonLocalizationOptions"/> 值
|
||||
@@ -244,7 +243,7 @@ internal class CacheManager
|
||||
/// <returns></returns>
|
||||
private static JsonLocalizationOptions GetJsonLocalizationOption()
|
||||
{
|
||||
var localizationOptions = Instance.Provider.GetRequiredService<IOptions<JsonLocalizationOptions>>();
|
||||
var localizationOptions = Provider.GetRequiredService<IOptions<JsonLocalizationOptions>>();
|
||||
return localizationOptions.Value;
|
||||
}
|
||||
/// <summary>
|
||||
@@ -253,7 +252,7 @@ internal class CacheManager
|
||||
/// <returns></returns>
|
||||
private static BootstrapBlazorOptions GetBootstrapBlazorOption()
|
||||
{
|
||||
var localizationOptions = Instance.Provider.GetRequiredService<IOptions<BootstrapBlazorOptions>>();
|
||||
var localizationOptions = Provider.GetRequiredService<IOptions<BootstrapBlazorOptions>>();
|
||||
return localizationOptions.Value;
|
||||
}
|
||||
/// <summary>
|
||||
@@ -269,7 +268,7 @@ internal class CacheManager
|
||||
return null;
|
||||
}
|
||||
IStringLocalizer? ret = null;
|
||||
var factories = Instance.Provider.GetServices<IStringLocalizerFactory>();
|
||||
var factories = Provider.GetServices<IStringLocalizerFactory>();
|
||||
var factory = factories.LastOrDefault(a => a is not JsonStringLocalizerFactory);
|
||||
if (factory != null)
|
||||
{
|
||||
@@ -345,7 +344,7 @@ internal class CacheManager
|
||||
/// <param name="typeName"></param>
|
||||
/// <param name="includeParentCultures"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<LocalizedString> GetTypeStringsFromResolve(string typeName, bool includeParentCultures = true) => Instance.Provider.GetRequiredService<ILocalizationResolve>().GetAllStringsByType(typeName, includeParentCultures);
|
||||
public static IEnumerable<LocalizedString> GetTypeStringsFromResolve(string typeName, bool includeParentCultures = true) => Provider.GetRequiredService<ILocalizationResolve>().GetAllStringsByType(typeName, includeParentCultures);
|
||||
#endregion
|
||||
|
||||
#region DisplayName
|
||||
|
||||
@@ -25,17 +25,11 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
|
||||
/// <summary>最大个数。默认0,0表示无上限</summary>
|
||||
public Int32 Max { get; set; } = 0;
|
||||
|
||||
/// <summary>最小个数。默认1</summary>
|
||||
public Int32 Min { get; set; } = 1;
|
||||
|
||||
private readonly object _syncRoot = new();
|
||||
|
||||
/// <summary>基础空闲集合。只保存最小个数,最热部分</summary>
|
||||
private readonly Stack<T> _free = new();
|
||||
|
||||
/// <summary>扩展空闲集合。保存最小个数以外部分</summary>
|
||||
private readonly Queue<T> _free2 = new();
|
||||
|
||||
/// <summary>借出去的放在这</summary>
|
||||
private readonly HashSet<T> _busy = new();
|
||||
|
||||
@@ -79,7 +73,7 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
|
||||
if (_inited) return;
|
||||
_inited = true;
|
||||
|
||||
WriteLog($"Init {typeof(T).FullName} Min={Min} Max={Max}");
|
||||
WriteLog($"Init {typeof(T).FullName} Max={Max}");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
@@ -99,26 +93,20 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
|
||||
pi = _free.Pop();
|
||||
_FreeCount--;
|
||||
}
|
||||
else if (_free2.Count > 0)
|
||||
{
|
||||
pi = _free2.Dequeue();
|
||||
_FreeCount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
var count = BusyCount;
|
||||
if (Max > 0 && count >= Max)
|
||||
if (Max > 0 && BusyCount >= Max)
|
||||
{
|
||||
var msg = $"申请失败,已有 {count:n0} 达到或超过最大值 {Max:n0}";
|
||||
var msg = $"申请失败,已有 {BusyCount:n0} 达到或超过最大值 {Max:n0}";
|
||||
WriteLog("Acquire Max " + msg);
|
||||
throw new Exception(Name + " " + msg);
|
||||
}
|
||||
|
||||
pi = OnCreate();
|
||||
if (count == 0) Init();
|
||||
if (BusyCount == 0) Init();
|
||||
|
||||
#if DEBUG
|
||||
WriteLog("Acquire Create Free={0} Busy={1}", FreeCount, count + 1);
|
||||
WriteLog("Acquire Create Free={0} Busy={1}", FreeCount, BusyCount + 1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -177,10 +165,7 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
|
||||
}
|
||||
lock (_syncRoot)
|
||||
{
|
||||
if (_FreeCount < Min)
|
||||
_free.Push(value);
|
||||
else
|
||||
_free2.Enqueue(value);
|
||||
_free.Push(value);
|
||||
_FreeCount++;
|
||||
}
|
||||
|
||||
@@ -214,12 +199,6 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
|
||||
OnDispose(pi);
|
||||
}
|
||||
|
||||
while (_free2.Count > 0)
|
||||
{
|
||||
var pi = _free2.Dequeue();
|
||||
OnDispose(pi);
|
||||
}
|
||||
|
||||
_FreeCount = 0;
|
||||
|
||||
foreach (var item in _busy)
|
||||
|
||||
@@ -12,18 +12,13 @@ namespace PooledAwait
|
||||
{
|
||||
private static ObjectPoolLock<T> pool = new();
|
||||
|
||||
[ThreadStatic]
|
||||
private static T? ts_local;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance from the pool if possible
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T? TryGet()
|
||||
{
|
||||
var tmp = ts_local;
|
||||
ts_local = null;
|
||||
return tmp ?? pool.Get();
|
||||
return pool.Get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -34,11 +29,6 @@ namespace PooledAwait
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
if (ts_local == null)
|
||||
{
|
||||
ts_local = value;
|
||||
return;
|
||||
}
|
||||
pool.Return(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,7 +580,19 @@ public static class Reflect
|
||||
|
||||
return func;
|
||||
}
|
||||
/// <summary>把一个方法转为泛型委托,便于快速反射调用</summary>
|
||||
/// <typeparam name="TFunc"></typeparam>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
public static void RemoveCache<TFunc>(this MethodInfo method, object? target = null) where TFunc : class
|
||||
{
|
||||
if (method == null) return;
|
||||
|
||||
var key = new DelegateCacheKey(method, typeof(TFunc), target);
|
||||
|
||||
DelegateCache<TFunc>.Cache.TryRemove(key);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
public static class DelegateCache<TFunc>
|
||||
|
||||
@@ -191,6 +191,13 @@ public class TimerScheduler : IDisposable, ILogFeature
|
||||
Count--;
|
||||
}
|
||||
}
|
||||
|
||||
timer.Method.RemoveCache<TimerCallback>(timer.Target.Target);
|
||||
#if NET6_0_OR_GREATER
|
||||
timer.Method.RemoveCache<Func<Object?, ValueTask>>(timer.Target.Target);
|
||||
#endif
|
||||
timer.Method.RemoveCache<Func<Object?, Task>>(timer.Target.Target);
|
||||
|
||||
}
|
||||
|
||||
private AutoResetEvent? _waitForTimer;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<PluginVersion>10.11.114</PluginVersion>
|
||||
<ProPluginVersion>10.11.114</ProPluginVersion>
|
||||
<DefaultVersion>10.11.114</DefaultVersion>
|
||||
<PluginVersion>10.12.1</PluginVersion>
|
||||
<ProPluginVersion>10.12.1</ProPluginVersion>
|
||||
<DefaultVersion>10.12.1</DefaultVersion>
|
||||
<AuthenticationVersion>10.11.6</AuthenticationVersion>
|
||||
<SourceGeneratorVersion>10.11.6</SourceGeneratorVersion>
|
||||
<NET8Version>8.0.21</NET8Version>
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using PooledAwait;
|
||||
|
||||
using ThingsGateway.Foundation.Extension.String;
|
||||
|
||||
using TouchSocket.SerialPorts;
|
||||
@@ -26,65 +28,33 @@ public static class ChannelOptionsExtensions
|
||||
/// <param name="e">接收数据</param>
|
||||
/// <param name="funcs">事件</param>
|
||||
/// <returns></returns>
|
||||
internal static ValueTask OnChannelReceivedEvent(
|
||||
this IClientChannel clientChannel,
|
||||
ReceivedDataEventArgs e,
|
||||
ChannelReceivedEventHandler funcs)
|
||||
internal static ValueTask OnChannelReceivedEvent(this IClientChannel clientChannel, ReceivedDataEventArgs e, ChannelReceivedEventHandler funcs)
|
||||
{
|
||||
clientChannel.ThrowIfNull(nameof(IClientChannel));
|
||||
e.ThrowIfNull(nameof(ReceivedDataEventArgs));
|
||||
funcs.ThrowIfNull(nameof(ChannelReceivedEventHandler));
|
||||
|
||||
if (funcs.Count == 0) return EasyValueTask.CompletedTask;
|
||||
return OnChannelReceivedEvent(clientChannel, e, funcs);
|
||||
|
||||
return InvokeHandlersSequentially(clientChannel, e, funcs);
|
||||
}
|
||||
|
||||
private static ValueTask InvokeHandlersSequentially(
|
||||
IClientChannel clientChannel, ReceivedDataEventArgs e, ChannelReceivedEventHandler funcs)
|
||||
{
|
||||
var enumerator = new HandlerEnumerator(clientChannel, e, funcs);
|
||||
return enumerator.MoveNextAsync();
|
||||
}
|
||||
|
||||
private struct HandlerEnumerator
|
||||
{
|
||||
private readonly IClientChannel _channel;
|
||||
private readonly ReceivedDataEventArgs _e;
|
||||
private readonly ChannelReceivedEventHandler _funcs;
|
||||
private int _index;
|
||||
|
||||
public HandlerEnumerator(IClientChannel channel, ReceivedDataEventArgs e, ChannelReceivedEventHandler funcs)
|
||||
static async PooledValueTask OnChannelReceivedEvent(IClientChannel clientChannel, ReceivedDataEventArgs e, ChannelReceivedEventHandler funcs)
|
||||
{
|
||||
_channel = channel;
|
||||
_e = e;
|
||||
_funcs = funcs;
|
||||
_index = -1;
|
||||
}
|
||||
|
||||
public ValueTask MoveNextAsync()
|
||||
{
|
||||
_index++;
|
||||
if (_index >= _funcs.Count) return default;
|
||||
|
||||
var func = _funcs[_index];
|
||||
if (func == null) return MoveNextAsync();
|
||||
|
||||
bool isLast = _index == _funcs.Count - 1;
|
||||
var vt = func.Invoke(_channel, _e, isLast);
|
||||
if (vt.IsCompletedSuccessfully)
|
||||
if (funcs.Count > 0)
|
||||
{
|
||||
if (_e.Handled) return default;
|
||||
return MoveNextAsync();
|
||||
for (int i = 0; i < funcs.Count; i++)
|
||||
{
|
||||
var func = funcs[i];
|
||||
if (func == null) continue;
|
||||
var taskResult= func.Invoke(clientChannel, e, i == funcs.Count - 1);
|
||||
if(!taskResult.IsCompletedSuccessfully)
|
||||
{
|
||||
await taskResult.ConfigureAwait(false);
|
||||
}
|
||||
if (e.Handled)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Awaited(vt);
|
||||
}
|
||||
|
||||
private async ValueTask Awaited(ValueTask vt)
|
||||
{
|
||||
await vt.ConfigureAwait(false);
|
||||
if (!_e.Handled)
|
||||
await MoveNextAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,16 @@ public struct OperResult<T> : IOperResult<T>
|
||||
Exception = operResult.Exception;
|
||||
ErrorType = operResult.ErrorType;
|
||||
}
|
||||
/// <summary>
|
||||
/// 从另一个操作对象中赋值信息
|
||||
/// </summary>
|
||||
public OperResult(OperResult operResult)
|
||||
{
|
||||
OperCode = operResult.OperCode;
|
||||
ErrorMessage = operResult.ErrorMessage;
|
||||
Exception = operResult.Exception;
|
||||
ErrorType = operResult.ErrorType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 传入错误信息
|
||||
@@ -162,7 +172,13 @@ public struct OperResult<T, T2> : IOperResult<T, T2>
|
||||
Exception = operResult.Exception;
|
||||
ErrorType = operResult.ErrorType;
|
||||
}
|
||||
|
||||
public OperResult(OperResult operResult)
|
||||
{
|
||||
OperCode = operResult.OperCode;
|
||||
ErrorMessage = operResult.ErrorMessage;
|
||||
Exception = operResult.Exception;
|
||||
ErrorType = operResult.ErrorType;
|
||||
}
|
||||
/// <summary>
|
||||
/// 传入错误信息
|
||||
/// </summary>
|
||||
@@ -275,7 +291,13 @@ public struct OperResult<T, T2, T3> : IOperResult<T, T2, T3>
|
||||
Exception = operResult.Exception;
|
||||
ErrorType = operResult.ErrorType;
|
||||
}
|
||||
|
||||
public OperResult(OperResult operResult)
|
||||
{
|
||||
OperCode = operResult.OperCode;
|
||||
ErrorMessage = operResult.ErrorMessage;
|
||||
Exception = operResult.Exception;
|
||||
ErrorType = operResult.ErrorType;
|
||||
}
|
||||
/// <summary>
|
||||
/// 传入错误信息
|
||||
/// </summary>
|
||||
@@ -389,7 +411,13 @@ public struct OperResult : IOperResult
|
||||
Exception = operResult.Exception;
|
||||
ErrorType = operResult.ErrorType;
|
||||
}
|
||||
|
||||
public OperResult(OperResult operResult)
|
||||
{
|
||||
OperCode = operResult.OperCode;
|
||||
ErrorMessage = operResult.ErrorMessage;
|
||||
Exception = operResult.Exception;
|
||||
ErrorType = operResult.ErrorType;
|
||||
}
|
||||
/// <summary>
|
||||
/// 传入错误信息
|
||||
/// </summary>
|
||||
|
||||
@@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using System.ComponentModel;
|
||||
using ThingsGateway.NewLife.DictionaryExtensions;
|
||||
|
||||
using ThingsGateway.FriendlyException;
|
||||
|
||||
@@ -122,16 +123,11 @@ public class ControlController : ControllerBase, IRpcServer
|
||||
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||
public async Task<Dictionary<string, Dictionary<string, OperResult>>> WriteVariablesAsync([FromBody][TouchSocket.WebApi.FromBody] Dictionary<string, Dictionary<string, string>> deviceDatas)
|
||||
{
|
||||
foreach (var deviceData in deviceDatas)
|
||||
{
|
||||
if (GlobalData.Devices.TryGetValue(deviceData.Key, out var device))
|
||||
{
|
||||
var data = device.VariableRuntimes.Where(a => deviceData.Value.ContainsKey(a.Key)).ToList();
|
||||
await GlobalData.SysUserService.CheckApiDataScopeAsync(data.Select(a => a.Value.CreateOrgId), data.Select(a => a.Value.CreateUserId)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
await GlobalData.CheckByDeviceNames(deviceDatas.Select(a => a.Key)).ConfigureAwait(false);
|
||||
|
||||
return (await GlobalData.RpcService.InvokeDeviceMethodAsync($"WebApi-{UserManager.UserAccount}-{App.HttpContext?.GetRemoteIpAddressToIPv4()}", deviceDatas).ConfigureAwait(false)).ToDictionary(a => a.Key, a => a.Value.ToDictionary(b => b.Key, b => (OperResult)b.Value));
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -380,7 +380,7 @@ public abstract partial class CollectBase : DriverBase
|
||||
if (cancellationToken.IsCancellationRequested) return;
|
||||
CancellationToken readToken = default;
|
||||
var readerLockTask = @this.ReadWriteLock.ReaderLockAsync(cancellationToken);
|
||||
if (!readerLockTask.IsCompleted)
|
||||
if (!readerLockTask.IsCompletedSuccessfully)
|
||||
{
|
||||
readToken = await readerLockTask.ConfigureAwait(false);
|
||||
}
|
||||
@@ -403,7 +403,7 @@ public abstract partial class CollectBase : DriverBase
|
||||
|
||||
OperResult<ReadOnlyMemory<byte>> readResult = default;
|
||||
var readTask = @this.ReadSourceAsync(variableSourceRead, allToken);
|
||||
if (!readTask.IsCompleted)
|
||||
if (!readTask.IsCompletedSuccessfully)
|
||||
{
|
||||
readResult = await readTask.ConfigureAwait(false);
|
||||
}
|
||||
@@ -435,7 +435,7 @@ public abstract partial class CollectBase : DriverBase
|
||||
//if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
|
||||
// LogMessage?.Trace(string.Format("{0} - Collecting [{1} - {2}]", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length));
|
||||
var readTask1 = @this.ReadSourceAsync(variableSourceRead, allToken);
|
||||
if (!readTask1.IsCompleted)
|
||||
if (!readTask1.IsCompletedSuccessfully)
|
||||
{
|
||||
readResult = await readTask1.ConfigureAwait(false);
|
||||
}
|
||||
@@ -490,195 +490,6 @@ public abstract partial class CollectBase : DriverBase
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// private ValueTask ReadVariableSource(object? state, CancellationToken cancellationToken)
|
||||
// {
|
||||
// var enumerator = new ReadVariableSourceEnumerator(this, state, cancellationToken);
|
||||
// return enumerator.MoveNextAsync();
|
||||
// }
|
||||
|
||||
// private struct ReadVariableSourceEnumerator
|
||||
// {
|
||||
// private readonly CollectBase _owner;
|
||||
// private readonly object? _state;
|
||||
// private readonly CancellationToken _cancellationToken;
|
||||
|
||||
// private VariableSourceRead _variableSourceRead;
|
||||
// private CancellationToken _readToken;
|
||||
// private CancellationToken _allToken;
|
||||
// private OperResult<ReadOnlyMemory<byte>> _readResult;
|
||||
// private int _readErrorCount;
|
||||
// private ValueTask<CancellationToken> _readerLockTask;
|
||||
// private ValueTask<OperResult<ReadOnlyMemory<byte>>> _readTask;
|
||||
// private int _step;
|
||||
|
||||
// public ReadVariableSourceEnumerator(CollectBase owner, object? state, CancellationToken cancellationToken)
|
||||
// {
|
||||
// _owner = owner;
|
||||
// _state = state;
|
||||
// _cancellationToken = cancellationToken;
|
||||
|
||||
// _variableSourceRead = default!;
|
||||
// _readToken = default;
|
||||
// _allToken = default;
|
||||
// _readResult = default;
|
||||
// _readErrorCount = 0;
|
||||
// _readerLockTask = default;
|
||||
// _readTask = default;
|
||||
// _step = 0;
|
||||
// }
|
||||
|
||||
// public ValueTask MoveNextAsync()
|
||||
// {
|
||||
// switch (_step)
|
||||
// {
|
||||
// case 0:
|
||||
// if (_state is not VariableSourceRead vsr) return default;
|
||||
// _variableSourceRead = vsr;
|
||||
|
||||
// if (_owner.Pause) return default;
|
||||
// if (_cancellationToken.IsCancellationRequested) return default;
|
||||
|
||||
//#pragma warning disable CA2012 // 正确使用 ValueTask
|
||||
// _readerLockTask = _owner.ReadWriteLock.ReaderLockAsync(_cancellationToken);
|
||||
//#pragma warning restore CA2012 // 正确使用 ValueTask
|
||||
// if (!_readerLockTask.IsCompleted)
|
||||
// {
|
||||
// _step = 1;
|
||||
// return AwaitReaderLock();
|
||||
// }
|
||||
// _readToken = _readerLockTask.Result;
|
||||
// goto case 2;
|
||||
|
||||
// case 1:
|
||||
// _readToken = _readerLockTask.Result;
|
||||
// goto case 2;
|
||||
|
||||
// case 2:
|
||||
// if (_readToken.IsCancellationRequested)
|
||||
// {
|
||||
// return _owner.ReadVariableSource(_state, _cancellationToken);
|
||||
// }
|
||||
|
||||
// var allTokenSource = _owner._linkedCtsCache.GetLinkedTokenSource(_cancellationToken, _readToken);
|
||||
// _allToken = allTokenSource.Token;
|
||||
|
||||
//#pragma warning disable CA2012 // 正确使用 ValueTask
|
||||
// _readTask = _owner.ReadSourceAsync(_variableSourceRead, _allToken);
|
||||
//#pragma warning restore CA2012 // 正确使用 ValueTask
|
||||
// if (!_readTask.IsCompleted)
|
||||
// {
|
||||
// _step = 3;
|
||||
// return AwaitRead();
|
||||
// }
|
||||
// _readResult = _readTask.Result;
|
||||
// goto case 4;
|
||||
|
||||
// case 3:
|
||||
// _readResult = _readTask.Result;
|
||||
// goto case 4;
|
||||
|
||||
// case 4:
|
||||
// while (!_readResult.IsSuccess && _readErrorCount < _owner.CollectProperties.RetryCount)
|
||||
// {
|
||||
// if (_owner.Pause) return default;
|
||||
// if (_cancellationToken.IsCancellationRequested) return default;
|
||||
|
||||
// if (_readToken.IsCancellationRequested)
|
||||
// {
|
||||
// return _owner.ReadVariableSource(_state, _cancellationToken);
|
||||
// }
|
||||
|
||||
// _readErrorCount++;
|
||||
// if (_owner.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
|
||||
// _owner.LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] failed - {3}",
|
||||
// _owner.DeviceName, _variableSourceRead?.RegisterAddress, _variableSourceRead?.Length, _readResult.ErrorMessage));
|
||||
|
||||
//#pragma warning disable CA2012 // 正确使用 ValueTask
|
||||
// _readTask = _owner.ReadSourceAsync(_variableSourceRead, _allToken);
|
||||
//#pragma warning restore CA2012 // 正确使用 ValueTask
|
||||
// if (!_readTask.IsCompleted)
|
||||
// {
|
||||
// _step = 5;
|
||||
// return AwaitReadRetry();
|
||||
// }
|
||||
// _readResult = _readTask.Result;
|
||||
// }
|
||||
|
||||
// goto case 6;
|
||||
|
||||
// case 5:
|
||||
// _readResult = _readTask.Result;
|
||||
// _step = 4;
|
||||
// return MoveNextAsync();
|
||||
|
||||
// case 6:
|
||||
// if (_readResult.IsSuccess)
|
||||
// {
|
||||
// if (_owner.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
|
||||
// _owner.LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] data succeeded {3}",
|
||||
// _owner.DeviceName, _variableSourceRead?.RegisterAddress, _variableSourceRead?.Length, _readResult.Content.Span.ToHexString(' ')));
|
||||
|
||||
// _owner.CurrentDevice.SetDeviceStatus(TimerX.Now, null);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (_cancellationToken.IsCancellationRequested) return default;
|
||||
// if (_readToken.IsCancellationRequested)
|
||||
// {
|
||||
// return _owner.ReadVariableSource(_state, _cancellationToken);
|
||||
// }
|
||||
|
||||
// if (_variableSourceRead.LastErrorMessage != _readResult.ErrorMessage)
|
||||
// {
|
||||
// if (!_cancellationToken.IsCancellationRequested)
|
||||
// _owner.LogMessage?.LogWarning(_readResult.Exception, string.Format(AppResource.CollectFail, _owner.DeviceName,
|
||||
// _variableSourceRead?.RegisterAddress, _variableSourceRead?.Length, _readResult.ErrorMessage));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (!_cancellationToken.IsCancellationRequested && _owner.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
|
||||
// _owner.LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] data failed - {3}",
|
||||
// _owner.DeviceName, _variableSourceRead?.RegisterAddress, _variableSourceRead?.Length, _readResult.ErrorMessage));
|
||||
// }
|
||||
|
||||
// _variableSourceRead.LastErrorMessage = _readResult.ErrorMessage;
|
||||
// _owner.CurrentDevice.SetDeviceStatus(TimerX.Now, null, _readResult.ErrorMessage);
|
||||
// var time = DateTime.Now;
|
||||
// foreach (var item in _variableSourceRead.VariableRuntimes)
|
||||
// {
|
||||
// item.SetValue(null, time, isOnline: false);
|
||||
// }
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
|
||||
// return default;
|
||||
// }
|
||||
|
||||
// private async ValueTask AwaitReaderLock()
|
||||
// {
|
||||
// await _readerLockTask.ConfigureAwait(false);
|
||||
// _step = 1;
|
||||
// await MoveNextAsync().ConfigureAwait(false);
|
||||
// }
|
||||
|
||||
// private async ValueTask AwaitRead()
|
||||
// {
|
||||
// await _readTask.ConfigureAwait(false);
|
||||
// _step = 3;
|
||||
// await MoveNextAsync().ConfigureAwait(false);
|
||||
// }
|
||||
|
||||
// private async ValueTask AwaitReadRetry()
|
||||
// {
|
||||
// await _readTask.ConfigureAwait(false);
|
||||
// _step = 5;
|
||||
// await MoveNextAsync().ConfigureAwait(false);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ public abstract class CollectFoundationBase : CollectBase
|
||||
// 从协议读取数据
|
||||
OperResult<ReadOnlyMemory<byte>> read = default;
|
||||
var readTask = @this.FoundationDevice.ReadAsync(variableSourceRead.AddressObject, cancellationToken);
|
||||
if (!readTask.IsCompleted)
|
||||
if (!readTask.IsCompletedSuccessfully)
|
||||
{
|
||||
read = await readTask.ConfigureAwait(false);
|
||||
}
|
||||
@@ -200,116 +200,6 @@ public abstract class CollectFoundationBase : CollectBase
|
||||
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// 采集驱动读取,读取成功后直接赋值变量,失败不做处理,注意非通用设备需重写
|
||||
///// </summary>
|
||||
// protected override ValueTask<OperResult<ReadOnlyMemory<byte>>> ReadSourceAsync(VariableSourceRead variableSourceRead, CancellationToken cancellationToken)
|
||||
// {
|
||||
// if (cancellationToken.IsCancellationRequested)
|
||||
// return new ValueTask<OperResult<ReadOnlyMemory<byte>>>( new OperResult<ReadOnlyMemory<byte>>(new OperationCanceledException()));
|
||||
|
||||
// // 值类型状态机
|
||||
// var stateMachine = new ReadSourceStateMachine(this, variableSourceRead, cancellationToken);
|
||||
// return stateMachine.MoveNextAsync();
|
||||
// }
|
||||
|
||||
// private struct ReadSourceStateMachine
|
||||
// {
|
||||
// private readonly VariableSourceRead _variableSourceRead;
|
||||
// private readonly CancellationToken _cancellationToken;
|
||||
// private readonly CollectFoundationBase _owner;
|
||||
// private OperResult<ReadOnlyMemory<byte>> _result;
|
||||
// private ValueTask<OperResult<ReadOnlyMemory<byte>>> _readTask;
|
||||
|
||||
// public ReadSourceStateMachine(CollectFoundationBase owner, VariableSourceRead variableSourceRead, CancellationToken cancellationToken)
|
||||
// {
|
||||
// _owner = owner;
|
||||
// _variableSourceRead = variableSourceRead;
|
||||
// _cancellationToken = cancellationToken;
|
||||
// _result = default;
|
||||
// State = 0;
|
||||
// }
|
||||
|
||||
// public int State { get; private set; }
|
||||
|
||||
// public ValueTask<OperResult<ReadOnlyMemory<byte>>> MoveNextAsync()
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// switch (State)
|
||||
// {
|
||||
// case 0:
|
||||
// // 异步读取
|
||||
// if (_cancellationToken.IsCancellationRequested)
|
||||
// {
|
||||
// _result = new OperResult<ReadOnlyMemory<byte>>(new OperationCanceledException());
|
||||
// return new ValueTask<OperResult<ReadOnlyMemory<byte>>>(_result);
|
||||
// }
|
||||
|
||||
//#pragma warning disable CA2012 // 正确使用 ValueTask
|
||||
// _readTask = _owner.FoundationDevice.ReadAsync(_variableSourceRead.AddressObject, _cancellationToken);
|
||||
//#pragma warning restore CA2012 // 正确使用 ValueTask
|
||||
|
||||
// // 检查是否任务已完成
|
||||
// if (_readTask.IsCompleted)
|
||||
// {
|
||||
// _result = _readTask.Result;
|
||||
// State = 1;
|
||||
// return MoveNextAsync();
|
||||
// }
|
||||
|
||||
// // 如果任务尚未完成,继续等待
|
||||
// State = 2;
|
||||
// return Awaited(_readTask);
|
||||
|
||||
// case 1:
|
||||
// // 解析结构化内容
|
||||
// if (_result.IsSuccess)
|
||||
// {
|
||||
// var parsedResult = _variableSourceRead.VariableRuntimes.PraseStructContent(_owner.FoundationDevice, _result.Content.Span, false);
|
||||
// return new ValueTask<OperResult<ReadOnlyMemory<byte>>>(new OperResult<ReadOnlyMemory<byte>>(parsedResult));
|
||||
// }
|
||||
|
||||
// return new ValueTask<OperResult<ReadOnlyMemory<byte>>>(_result);
|
||||
|
||||
// case 2:
|
||||
// // 完成任务后,解析内容
|
||||
// _result = _readTask.Result;
|
||||
|
||||
// if (_result.IsSuccess)
|
||||
// {
|
||||
// var parsedResult = _variableSourceRead.VariableRuntimes.PraseStructContent(_owner.FoundationDevice, _result.Content.Span, false);
|
||||
// return new ValueTask<OperResult<ReadOnlyMemory<byte>>>(new OperResult<ReadOnlyMemory<byte>>(parsedResult));
|
||||
// }
|
||||
|
||||
// return new ValueTask<OperResult<ReadOnlyMemory<byte>>>(_result);
|
||||
|
||||
// default:
|
||||
// throw new InvalidOperationException("Unexpected state.");
|
||||
// }
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// return new ValueTask<OperResult<ReadOnlyMemory<byte>>>(new OperResult<ReadOnlyMemory<byte>>(ex));
|
||||
// }
|
||||
// }
|
||||
|
||||
// private async ValueTask<OperResult<ReadOnlyMemory<byte>>> Awaited(ValueTask<OperResult<ReadOnlyMemory<byte>>> vt)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
|
||||
|
||||
// await vt.ConfigureAwait(false);
|
||||
// return await MoveNextAsync().ConfigureAwait(false);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// return new OperResult<ReadOnlyMemory<byte>>(ex);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// 批量写入变量值,需返回变量名称/结果,注意非通用设备需重写
|
||||
/// </summary>
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
[SugarIndex("index_device", nameof(Variable.DeviceId), OrderByType.Asc)]
|
||||
[SugarIndex("unique_deviceid_variable_name", nameof(Variable.Name), OrderByType.Asc, nameof(Variable.DeviceId), OrderByType.Asc, true)]
|
||||
#endif
|
||||
public class Variable : BaseDataEntity, IValidatableObject
|
||||
public class Variable : PrimaryKeyEntity, IValidatableObject
|
||||
{
|
||||
/// <summary>
|
||||
/// 主键Id
|
||||
|
||||
@@ -15,6 +15,7 @@ using PooledAwait;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
using ThingsGateway.Extension.Generic;
|
||||
using ThingsGateway.NewLife.DictionaryExtensions;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
@@ -109,11 +110,15 @@ public static class GlobalData
|
||||
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))//在指定机构列表查询
|
||||
return IdDevices.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 IEnumerable<long> GetCurrentUserDeviceIds(HashSet<long> dataScope)
|
||||
{
|
||||
return IdDevices.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.Key);
|
||||
}
|
||||
public static Task<IEnumerable<VariableRuntime>> GetCurrentUserIdVariables()
|
||||
{
|
||||
return GetCurrentUserIdVariables();
|
||||
@@ -121,11 +126,53 @@ public static class GlobalData
|
||||
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);
|
||||
|
||||
return IdVariables.Where(a => a.Value.IsInternalMemoryVariable == false)
|
||||
.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.DeviceRuntime.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIf(dataScope?.Count == 0, u => u.Value.DeviceRuntime.CreateUserId == UserManager.UserId).Select(a => a.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task CheckByDeviceNames(IEnumerable<string> deviceNames)
|
||||
{
|
||||
List<long> orgids = new();
|
||||
List<long> userIds = new();
|
||||
foreach (var deviceData in GlobalData.Devices.FilterByKeys(deviceNames))
|
||||
{
|
||||
orgids.Add(deviceData.Value.CreateOrgId);
|
||||
userIds.Add(deviceData.Value.CreateUserId);
|
||||
}
|
||||
await GlobalData.SysUserService.CheckApiDataScopeAsync(orgids, userIds).ConfigureAwait(false);
|
||||
}
|
||||
public static async Task CheckByDeviceIds(IEnumerable<long> deviceIds)
|
||||
{
|
||||
List<long> orgids = new();
|
||||
List<long> userIds = new();
|
||||
foreach (var deviceData in GlobalData.IdDevices.FilterByKeys(deviceIds))
|
||||
{
|
||||
orgids.Add(deviceData.Value.CreateOrgId);
|
||||
userIds.Add(deviceData.Value.CreateUserId);
|
||||
}
|
||||
await GlobalData.SysUserService.CheckApiDataScopeAsync(orgids, userIds).ConfigureAwait(false);
|
||||
}
|
||||
public static async Task CheckByVariableIds(IEnumerable<long> variableIds)
|
||||
{
|
||||
List<long> orgids = new();
|
||||
List<long> userIds = new();
|
||||
foreach (var deviceData in GlobalData.IdVariables.FilterByKeys(variableIds))
|
||||
{
|
||||
orgids.Add(deviceData.Value.DeviceRuntime.CreateOrgId);
|
||||
userIds.Add(deviceData.Value.DeviceRuntime.CreateUserId);
|
||||
}
|
||||
await GlobalData.SysUserService.CheckApiDataScopeAsync(orgids, userIds).ConfigureAwait(false);
|
||||
}
|
||||
public static async Task CheckByVariableId(long variableId)
|
||||
{
|
||||
if (GlobalData.IdVariables.TryGetValue(variableId, out var variable))
|
||||
{
|
||||
await GlobalData.SysUserService.CheckApiDataScopeAsync(variable.DeviceRuntime.CreateOrgId, variable.DeviceRuntime.CreateUserId).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
public static Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariablesAsync()
|
||||
{
|
||||
return GetCurrentUserRealAlarmVariablesAsync();
|
||||
@@ -133,7 +180,8 @@ public static class GlobalData
|
||||
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))//在指定机构列表查询
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -145,8 +193,8 @@ public static class GlobalData
|
||||
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);
|
||||
return AlarmEnableIdVariables.Where(a => a.Value.IsInternalMemoryVariable == false).WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.DeviceRuntime.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIf(dataScope?.Count == 0, u => u.Value.DeviceRuntime.CreateUserId == UserManager.UserId).Select(a => a.Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ public static partial class GatewayMapper
|
||||
[MapProperty($"{nameof(VariableRuntime.AlarmRuntimePropertys)}.{nameof(AlarmRuntimePropertys.EventTime)}", nameof(AlarmVariable.EventTime))]
|
||||
[MapProperty($"{nameof(VariableRuntime.AlarmRuntimePropertys)}.{nameof(AlarmRuntimePropertys.AlarmType)}", nameof(AlarmVariable.AlarmType))]
|
||||
[MapProperty($"{nameof(VariableRuntime.AlarmRuntimePropertys)}.{nameof(AlarmRuntimePropertys.EventType)}", nameof(AlarmVariable.EventType))]
|
||||
[MapProperty($"{nameof(VariableRuntime.DeviceRuntime)}.{nameof(DeviceRuntime.CreateOrgId)}", nameof(AlarmVariable.CreateOrgId))]
|
||||
[MapProperty($"{nameof(VariableRuntime.DeviceRuntime)}.{nameof(DeviceRuntime.CreateUserId)}", nameof(AlarmVariable.CreateUserId))]
|
||||
public static partial AlarmVariable AdaptAlarmVariable(this VariableRuntime src);
|
||||
|
||||
public static partial VariableDataWithValue AdaptVariableDataWithValue(this VariableBasicData src);
|
||||
|
||||
@@ -66,11 +66,6 @@ public partial class VariableRuntime : Variable
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public DateTime CollectTime { get => collectTime; set => collectTime = value; }
|
||||
|
||||
[SugarColumn(ColumnDescription = "排序码", IsNullable = true)]
|
||||
[AutoGenerateColumn(Visible = false, DefaultSort = false, Sortable = true)]
|
||||
[IgnoreExcel]
|
||||
public override int SortCode { get => sortCode; set => sortCode = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 上次值
|
||||
/// </summary>
|
||||
@@ -245,7 +240,6 @@ public partial class VariableRuntime : Variable
|
||||
|
||||
|
||||
private int index;
|
||||
private int sortCode;
|
||||
private DateTime changeTime = DateTime.UnixEpoch.ToLocalTime();
|
||||
|
||||
private DateTime collectTime = DateTime.UnixEpoch.ToLocalTime();
|
||||
|
||||
@@ -22,7 +22,6 @@ using System.Text;
|
||||
|
||||
using ThingsGateway.Common.Extension;
|
||||
using ThingsGateway.Common.Extension.Generic;
|
||||
using ThingsGateway.Extension.Generic;
|
||||
using ThingsGateway.Foundation.Extension.Dynamic;
|
||||
|
||||
using TouchSocket.Core;
|
||||
@@ -107,8 +106,6 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
variable.DataType = DataTypeEnum.Int16;
|
||||
variable.Name = name;
|
||||
variable.Id = id;
|
||||
variable.CreateOrgId = UserManager.OrgId;
|
||||
variable.CreateUserId = UserManager.UserId;
|
||||
variable.DeviceId = device.Id;
|
||||
variable.RegisterAddress = address;
|
||||
newVariables.Add(variable);
|
||||
@@ -334,8 +331,6 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
variable.DataType = DataTypeEnum.Int16;
|
||||
variable.Name = name;
|
||||
variable.Id = id;
|
||||
variable.CreateOrgId = UserManager.OrgId;
|
||||
variable.CreateUserId = UserManager.UserId;
|
||||
variable.DeviceId = device.Id;
|
||||
variable.RegisterAddress = address;
|
||||
newVariables.Add(variable);
|
||||
@@ -428,12 +423,9 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
differences.Remove(nameof(Variable.VariablePropertys));
|
||||
if (differences?.Count > 0)
|
||||
{
|
||||
var data = models.ToList();
|
||||
await GlobalData.CheckByDeviceIds(data.Select(a => a.DeviceId)).ConfigureAwait(false);
|
||||
using var db = GetDB();
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
var data = models
|
||||
.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIf(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
|
||||
.ToList();
|
||||
|
||||
var result = (await db.Updateable(data).UpdateColumns(differences.Select(a => a.Key).ToArray()).ExecuteCommandAsync().ConfigureAwait(false)) > 0;
|
||||
|
||||
@@ -448,24 +440,20 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
[OperDesc("DeleteVariable", isRecordPar: false, localizerType: typeof(Variable))]
|
||||
public async Task DeleteByDeviceIdAsync(IEnumerable<long> input, SqlSugarClient db)
|
||||
{
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
var ids = input.ToList();
|
||||
var result = await db.Deleteable<Variable>().Where(a => ids.Contains(a.DeviceId))
|
||||
.WhereIF(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
|
||||
.ExecuteCommandAsync().ConfigureAwait(false);
|
||||
await GlobalData.CheckByDeviceIds(ids).ConfigureAwait(false);
|
||||
|
||||
var result = await db.Deleteable<Variable>().Where(a => ids.Contains(a.DeviceId))
|
||||
.ExecuteCommandAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[OperDesc("DeleteVariable", isRecordPar: false, localizerType: typeof(Variable))]
|
||||
public async Task<bool> DeleteVariableAsync(IEnumerable<long> input)
|
||||
{
|
||||
using var db = GetDB();
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
var ids = input?.ToList();
|
||||
await GlobalData.CheckByVariableIds(ids).ConfigureAwait(false);
|
||||
var result = (await db.Deleteable<Variable>().WhereIF(input != null, a => ids.Contains(a.Id))
|
||||
.WhereIF(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
|
||||
.ExecuteCommandAsync().ConfigureAwait(false)) > 0;
|
||||
|
||||
return result;
|
||||
@@ -505,6 +493,11 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
private async Task<Func<ISugarQueryable<Variable>, ISugarQueryable<Variable>>> GetWhereQueryFunc(GatewayExportFilter exportFilter)
|
||||
{
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
List<long>? filterDeviceIds= null;
|
||||
if(dataScope!=null)
|
||||
{
|
||||
filterDeviceIds= GlobalData.GetCurrentUserDeviceIds(dataScope).ToList();
|
||||
}
|
||||
HashSet<long>? deviceId = null;
|
||||
if (!exportFilter.PluginName.IsNullOrWhiteSpace())
|
||||
{
|
||||
@@ -520,8 +513,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
.WhereIF(exportFilter.PluginType == PluginTypeEnum.Collect, a => a.DeviceId == exportFilter.DeviceId)
|
||||
.WhereIF(deviceId != null, a => deviceId.Contains(a.DeviceId))
|
||||
|
||||
.WhereIF(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
|
||||
.WhereIF(filterDeviceIds != null , u => filterDeviceIds.Contains(u.DeviceId))//在指定机构列表查询
|
||||
|
||||
.WhereIF(exportFilter.PluginType == PluginTypeEnum.Business, u => SqlFunc.JsonLike(u.VariablePropertys, exportFilter.DeviceId.ToString()));
|
||||
return whereQuery;
|
||||
@@ -530,6 +522,13 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
private async Task<Func<IEnumerable<Variable>, IEnumerable<Variable>>> GetWhereEnumerableFunc(GatewayExportFilter exportFilter, bool sql = false)
|
||||
{
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
List<long>? filterDeviceIds = null;
|
||||
if (dataScope != null)
|
||||
{
|
||||
filterDeviceIds = GlobalData.GetCurrentUserDeviceIds(dataScope).ToList();
|
||||
}
|
||||
|
||||
|
||||
HashSet<long>? deviceId = null;
|
||||
if (!exportFilter.PluginName.IsNullOrWhiteSpace())
|
||||
{
|
||||
@@ -545,8 +544,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
.WhereIF(exportFilter.PluginType == PluginTypeEnum.Collect, a => a.DeviceId == exportFilter.DeviceId)
|
||||
.WhereIF(deviceId != null, a => deviceId.Contains(a.DeviceId))
|
||||
|
||||
.WhereIF(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
|
||||
.WhereIF(filterDeviceIds != null, u => filterDeviceIds.Contains(u.DeviceId))//在指定机构列表查询
|
||||
|
||||
.WhereIF(sql && exportFilter.PluginType == PluginTypeEnum.Business, u => SqlFunc.JsonLike(u.VariablePropertys, exportFilter.DeviceId.ToString()))
|
||||
.WhereIF(!sql && exportFilter.PluginType == PluginTypeEnum.Business && exportFilter.DeviceId > 0, u =>
|
||||
@@ -566,7 +564,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
public async Task<bool> SaveVariableAsync(Variable input, ItemChangedType type)
|
||||
{
|
||||
if (type == ItemChangedType.Update)
|
||||
await GlobalData.SysUserService.CheckApiDataScopeAsync(input.CreateOrgId, input.CreateUserId).ConfigureAwait(false);
|
||||
await GlobalData.CheckByVariableId(input.Id).ConfigureAwait(false);
|
||||
else
|
||||
ManageHelper.CheckVariableCount(1);
|
||||
|
||||
@@ -767,6 +765,13 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
|
||||
public ImportPreviewOutput<Dictionary<string, Variable>> SetVariableData(HashSet<long>? dataScope, IReadOnlyDictionary<string, DeviceRuntime> deviceDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, ImportPreviewOutput<Dictionary<string, Variable>> deviceImportPreview, Dictionary<string, PluginInfo> driverPluginNameDict, NonBlockingDictionary<string, (Type, Dictionary<string, PropertyInfo>, Dictionary<string, PropertyInfo>)> propertysDict, string sheetName, IEnumerable<IDictionary<string, object>> rows)
|
||||
{
|
||||
|
||||
List<long>? filterDeviceIds = null;
|
||||
if (dataScope != null)
|
||||
{
|
||||
filterDeviceIds = GlobalData.GetCurrentUserDeviceIds(dataScope).ToList();
|
||||
}
|
||||
|
||||
string ImportNullError = Localizer["ImportNullError"];
|
||||
string RedundantDeviceError = Localizer["RedundantDeviceError"];
|
||||
|
||||
@@ -839,17 +844,14 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
if (GlobalData.IdDevices.TryGetValue(variable.DeviceId, out var dbvar1s) && dbvar1s.VariableRuntimes.TryGetValue(variable.Name, out var dbvar1))
|
||||
{
|
||||
variable.Id = dbvar1.Id;
|
||||
variable.CreateOrgId = dbvar1.CreateOrgId;
|
||||
variable.CreateUserId = dbvar1.CreateUserId;
|
||||
variable.IsUp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
variable.IsUp = false;
|
||||
variable.CreateOrgId = UserManager.OrgId;
|
||||
variable.CreateUserId = UserManager.UserId;
|
||||
}
|
||||
if (device.IsUp && ((dataScope != null && dataScope?.Count > 0 && !dataScope.Contains(variable.CreateOrgId)) || dataScope?.Count == 0 && variable.CreateUserId != UserManager.UserId))
|
||||
|
||||
if (device.IsUp && (filterDeviceIds?.Contains(variable.DeviceId) != false))
|
||||
{
|
||||
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, "Operation not permitted"));
|
||||
}
|
||||
|
||||
@@ -1610,9 +1610,10 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
|
||||
{
|
||||
|
||||
Disposed = true;
|
||||
ChannelRuntimeDispatchService.UnSubscribe(Refresh);
|
||||
ChannelRuntimeDispatchService?.UnSubscribe(Refresh);
|
||||
|
||||
await Module.InvokeVoidAsync("dispose", Id);
|
||||
if (Module != null)
|
||||
await Module.InvokeVoidAsync("dispose", Id);
|
||||
|
||||
await base.DisposeAsync(disposing);
|
||||
}
|
||||
|
||||
@@ -296,9 +296,10 @@ public partial class VariableRuntimeInfo
|
||||
{
|
||||
|
||||
Disposed = true;
|
||||
VariableRuntimeDispatchService.UnSubscribe(Refresh);
|
||||
VariableRuntimeDispatchService?.UnSubscribe(Refresh);
|
||||
|
||||
await Module.InvokeVoidAsync("dispose", Id);
|
||||
if (Module != null)
|
||||
await Module.InvokeVoidAsync("dispose", Id);
|
||||
|
||||
await base.DisposeAsync(disposing);
|
||||
}
|
||||
|
||||
@@ -1,426 +0,0 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,11 +11,6 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Diagnosers;
|
||||
|
||||
using Longbow.Modbus;
|
||||
using Longbow.TcpSocket;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
using System.Net.Sockets;
|
||||
|
||||
using ThingsGateway.Foundation.Modbus;
|
||||
@@ -33,14 +28,12 @@ namespace ThingsGateway.Foundation;
|
||||
[MemoryDiagnoser]
|
||||
public class ModbusBenchmark : IDisposable
|
||||
{
|
||||
public static int ClientCount = 1;
|
||||
public static int ClientCount = 10;
|
||||
public static int TaskNumberOfItems = 1;
|
||||
public static int NumberOfItems = 10;
|
||||
public static int NumberOfItems = 100;
|
||||
|
||||
private readonly List<IModbusClient> _lgbModbusClients = [];
|
||||
private List<ModbusMaster> thingsgatewaymodbuss = new();
|
||||
private List<IModbusMaster> nmodbuss = new();
|
||||
//private List<ModbusTcpNet> modbusTcpNets = new();
|
||||
private List<ModbusTcpMaster> modbusTcpMasters = new();
|
||||
|
||||
[GlobalSetup]
|
||||
@@ -74,15 +67,7 @@ public class ModbusBenchmark : IDisposable
|
||||
await nmodbus.ReadHoldingRegistersAsync(1, 0, 100);
|
||||
nmodbuss.Add(nmodbus);
|
||||
}
|
||||
//for (int i = 0; i < ClientCount; i++)
|
||||
//{
|
||||
// ModbusTcpNet modbusTcpNet = new();
|
||||
// modbusTcpNet.IpAddress = "127.0.0.1";
|
||||
// modbusTcpNet.Port = 502;
|
||||
// modbusTcpNet.ConnectServer();
|
||||
// modbusTcpNet.ReadAsync("0", 100);
|
||||
// modbusTcpNets.Add(modbusTcpNet);
|
||||
//}
|
||||
|
||||
|
||||
for (int i = 0; i < ClientCount; i++)
|
||||
{
|
||||
@@ -94,23 +79,6 @@ public class ModbusBenchmark : IDisposable
|
||||
modbusTcpMasters.Add(client);
|
||||
}
|
||||
|
||||
{
|
||||
var sc = new ServiceCollection();
|
||||
sc.AddTcpSocketFactory();
|
||||
sc.AddModbusFactory();
|
||||
|
||||
var provider = sc.BuildServiceProvider();
|
||||
var factory = provider.GetRequiredService<IModbusFactory>();
|
||||
|
||||
for (int i = 0; i < ClientCount; i++)
|
||||
{
|
||||
var client = factory.GetOrCreateTcpMaster();
|
||||
await client.ConnectAsync("127.0.0.1", 502);
|
||||
await client.ReadHoldingRegistersAsync(0x01, 0x00, 10);
|
||||
|
||||
_lgbModbusClients.Add(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
@@ -140,33 +108,6 @@ public class ModbusBenchmark : IDisposable
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public async Task LongbowModbus()
|
||||
{
|
||||
List<Task> tasks = new List<Task>();
|
||||
foreach (var client in _lgbModbusClients)
|
||||
{
|
||||
|
||||
for (int i = 0; i < TaskNumberOfItems; i++)
|
||||
{
|
||||
tasks.Add(Task.Run(async () =>
|
||||
{
|
||||
for (int i = 0; i < NumberOfItems; i++)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(3000);
|
||||
var result = await client.ReadHoldingRegistersAsync(1, 0, 100, cts.Token).ConfigureAwait(false);
|
||||
var data = result.ReadUShortValues(100);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
throw new Exception(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff") + result.Exception);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public async Task TouchSocket()
|
||||
{
|
||||
@@ -214,39 +155,12 @@ public class ModbusBenchmark : IDisposable
|
||||
}
|
||||
|
||||
|
||||
//并发失败
|
||||
//[Benchmark]
|
||||
//public async Task HslCommunication()
|
||||
//{
|
||||
// List<Task> tasks = new List<Task>();
|
||||
// foreach (var modbusTcpNet in modbusTcpNets)
|
||||
// {
|
||||
// for (int i = 0; i < TaskNumberOfItems; i++)
|
||||
// {
|
||||
// tasks.Add(Task.Run(async () =>
|
||||
// {
|
||||
// for (int i = 0; i < NumberOfItems; i++)
|
||||
// {
|
||||
// var result = await modbusTcpNet.ReadAsync("0", 100);
|
||||
// if (!result.IsSuccess)
|
||||
// {
|
||||
// throw new Exception(result.Message);
|
||||
// }
|
||||
// }
|
||||
// }));
|
||||
// }
|
||||
// }
|
||||
// await Task.WhenAll(tasks);
|
||||
//}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
thingsgatewaymodbuss?.ForEach(a => a.Channel.SafeDispose());
|
||||
thingsgatewaymodbuss?.ForEach(a => a.SafeDispose());
|
||||
nmodbuss?.ForEach(a => a.SafeDispose());
|
||||
//modbusTcpNets?.ForEach(a => a.SafeDispose());
|
||||
_lgbModbusClients?.ForEach(a => a.DisposeAsync());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,8 +11,6 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Diagnosers;
|
||||
|
||||
using HslCommunication.Profinet.Siemens;
|
||||
|
||||
using S7.Net;
|
||||
|
||||
using ThingsGateway.Foundation.SiemensS7;
|
||||
@@ -33,7 +31,6 @@ public class S7Benchmark : IDisposable
|
||||
private List<SiemensS7Master> siemensS7s = new();
|
||||
|
||||
private List<Plc> plcs = new();
|
||||
private List<SiemensS7Net> siemensS7Nets = new();
|
||||
|
||||
[GlobalSetup]
|
||||
public async Task Init()
|
||||
@@ -57,13 +54,7 @@ public class S7Benchmark : IDisposable
|
||||
await siemensS7.ReadAsync("M1", 100);
|
||||
siemensS7s.Add(siemensS7);
|
||||
}
|
||||
for (int i = 0; i < ClientCount; i++)
|
||||
{
|
||||
var siemensS7Net = new SiemensS7Net(SiemensPLCS.S1500, "127.0.0.1");
|
||||
await siemensS7Net.ConnectServerAsync();
|
||||
await siemensS7Net.ReadAsync("M0", 100);
|
||||
siemensS7Nets.Add(siemensS7Net);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ClientCount; i++)
|
||||
{
|
||||
var plc = new Plc(CpuType.S71500, "127.0.0.1", 102, 0, 0);
|
||||
@@ -94,34 +85,6 @@ public class S7Benchmark : IDisposable
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public async Task HslCommunication()
|
||||
{
|
||||
List<Task> tasks = new List<Task>();
|
||||
foreach (var siemensS7Net in siemensS7Nets)
|
||||
{
|
||||
for (int i = 0; i < TaskNumberOfItems; i++)
|
||||
{
|
||||
tasks.Add(Task.Run(async () =>
|
||||
{
|
||||
for (int i = 0; i < NumberOfItems; i++)
|
||||
{
|
||||
var result = await siemensS7Net.ReadAsync("M0", 100);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
throw new Exception(result.Message);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[Benchmark]
|
||||
public async Task ThingsGateway()
|
||||
@@ -151,7 +114,6 @@ public class S7Benchmark : IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
plcs.ForEach(a => a.SafeDispose());
|
||||
siemensS7Nets.ForEach(a => a.SafeDispose());
|
||||
siemensS7s.ForEach(a => a.Channel.SafeDispose());
|
||||
siemensS7s.ForEach(a => a.SafeDispose());
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Diagnosers;
|
||||
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.NewLife.Collections;
|
||||
|
||||
namespace ThingsGateway.Foundation;
|
||||
@@ -47,7 +48,7 @@ public class TimeoutBenchmark
|
||||
var _reusableTimeout = _reusableTimeouts.Get();
|
||||
try
|
||||
{
|
||||
await Task.Delay(5, _reusableTimeout.GetTokenSource(10, otherCts.Token).Token).ConfigureAwait(false); // 模拟工作
|
||||
await Task.Delay(5, _reusableTimeout.GetTokenSource(10, otherCts.Token)).ConfigureAwait(false); // 模拟工作
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Running;
|
||||
|
||||
namespace BenchmarkConsoleApp
|
||||
namespace ThingsGateway.Foundation
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
@@ -45,18 +45,18 @@ namespace BenchmarkConsoleApp
|
||||
//ManualConfig.Create(DefaultConfig.Instance)
|
||||
//.WithOptions(ConfigOptions.DisableOptimizationsValidator)
|
||||
//);
|
||||
BenchmarkRunner.Run<BenchmarkAsyncWaitData>(
|
||||
ManualConfig.Create(DefaultConfig.Instance)
|
||||
.WithOptions(ConfigOptions.DisableOptimizationsValidator)
|
||||
);
|
||||
// BenchmarkRunner.Run<BenchmarkAsyncWaitData>(
|
||||
//ManualConfig.Create(DefaultConfig.Instance)
|
||||
//.WithOptions(ConfigOptions.DisableOptimizationsValidator)
|
||||
//);
|
||||
// BenchmarkRunner.Run<SemaphoreBenchmark>(
|
||||
// ManualConfig.Create(DefaultConfig.Instance)
|
||||
// .WithOptions(ConfigOptions.DisableOptimizationsValidator)
|
||||
//);
|
||||
// BenchmarkRunner.Run<ModbusBenchmark>(
|
||||
//ManualConfig.Create(DefaultConfig.Instance)
|
||||
//.WithOptions(ConfigOptions.DisableOptimizationsValidator)
|
||||
//);
|
||||
BenchmarkRunner.Run<ModbusBenchmark>(
|
||||
ManualConfig.Create(DefaultConfig.Instance)
|
||||
.WithOptions(ConfigOptions.DisableOptimizationsValidator)
|
||||
);
|
||||
// BenchmarkRunner.Run<S7Benchmark>(
|
||||
//ManualConfig.Create(DefaultConfig.Instance)
|
||||
//.WithOptions(ConfigOptions.DisableOptimizationsValidator)
|
||||
|
||||
@@ -42,10 +42,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.15.4" />
|
||||
<PackageReference Include="HslCommunication" Version="12.5.1" />
|
||||
<PackageReference Include="Longbow.Modbus" Version="9.1.1" />
|
||||
<PackageReference Include="NModbus" Version="3.0.81" />
|
||||
<PackageReference Include="NModbus.Serial" Version="3.0.81" />
|
||||
<PackageReference Include="S7netplus" Version="0.20.0" />
|
||||
<!--<PackageReference Include="ThingsGateway.Foundation.Modbus" Version="$(DefaultVersion)" />
|
||||
<PackageReference Include="ThingsGateway.Foundation.SiemensS7" Version="$(DefaultVersion)" />-->
|
||||
|
||||
@@ -1,14 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ThingsGateway.Foundation.Modbus;
|
||||
namespace ThingsGateway.Foundation.Modbus;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -558,7 +558,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryParseRequest(object requestInfo, out ModbusRequest modbusRequest, out ReadOnlySequence<byte> sequences, out bool modbusRtu)
|
||||
private static bool TryParseRequest(IRequestInfo requestInfo, out ModbusRequest modbusRequest, out ReadOnlySequence<byte> sequences, out bool modbusRtu)
|
||||
{
|
||||
modbusRequest = default;
|
||||
sequences = default;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
|
||||
"RemoteServerManagement": {
|
||||
"Enable": true,
|
||||
"Enable": false,
|
||||
"Name": "ThingsGateway",
|
||||
"ServerUri": "0.0.0.0:8399",
|
||||
"VerifyToken": "ThingsGateway",
|
||||
|
||||
Reference in New Issue
Block a user