10.11.33
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -364,8 +364,5 @@ FodyWeavers.xsd
|
|||||||
|
|
||||||
/src/*Pro*/
|
/src/*Pro*/
|
||||||
/src/*Pro*
|
/src/*Pro*
|
||||||
/src/**/*Pro*
|
|
||||||
/src/*pro*
|
|
||||||
/src/*pro*/
|
|
||||||
/src/ThingsGateway.Server/Configuration/GiteeOAuthSettings.json
|
/src/ThingsGateway.Server/Configuration/GiteeOAuthSettings.json
|
||||||
/src/.idea/
|
/src/.idea/
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
using ThingsGateway.NewLife.Log;
|
using ThingsGateway.NewLife.Log;
|
||||||
using ThingsGateway.NewLife.Reflection;
|
using ThingsGateway.NewLife.Reflection;
|
||||||
@@ -76,7 +75,7 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
|
|||||||
|
|
||||||
_timer.TryDispose();
|
_timer.TryDispose();
|
||||||
|
|
||||||
WriteLog($"Dispose {typeof(T).FullName} FreeCount={FreeCount:n0} BusyCount={BusyCount:n0} Total={Total:n0}");
|
WriteLog($"Dispose {typeof(T).FullName} FreeCount={FreeCount:n0} BusyCount={BusyCount:n0}");
|
||||||
|
|
||||||
Clear();
|
Clear();
|
||||||
}
|
}
|
||||||
@@ -112,10 +111,6 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public virtual T Get()
|
public virtual T Get()
|
||||||
{
|
{
|
||||||
var sw = Log == null || Log == Logger.Null ? null : Stopwatch.StartNew();
|
|
||||||
Interlocked.Increment(ref _Total);
|
|
||||||
|
|
||||||
var success = false;
|
|
||||||
Item? pi = null;
|
Item? pi = null;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@@ -123,8 +118,6 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
|
|||||||
if (_free.TryPop(out pi) || _free2.TryDequeue(out pi))
|
if (_free.TryPop(out pi) || _free2.TryDequeue(out pi))
|
||||||
{
|
{
|
||||||
Interlocked.Decrement(ref _FreeCount);
|
Interlocked.Decrement(ref _FreeCount);
|
||||||
|
|
||||||
success = true;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -150,8 +143,6 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
|
|||||||
WriteLog("Acquire Create Free={0} Busy={1}", FreeCount, count + 1);
|
WriteLog("Acquire Create Free={0} Busy={1}", FreeCount, count + 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Interlocked.Increment(ref _NewCount);
|
|
||||||
success = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 借出时如果不可用,再次借取
|
// 借出时如果不可用,再次借取
|
||||||
@@ -164,17 +155,6 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
|
|||||||
_busy.TryAdd(pi.Value, pi);
|
_busy.TryAdd(pi.Value, pi);
|
||||||
|
|
||||||
Interlocked.Increment(ref _BusyCount);
|
Interlocked.Increment(ref _BusyCount);
|
||||||
if (success) Interlocked.Increment(ref _Success);
|
|
||||||
if (sw != null)
|
|
||||||
{
|
|
||||||
sw.Stop();
|
|
||||||
var ms = sw.Elapsed.TotalMilliseconds;
|
|
||||||
|
|
||||||
if (Cost < 0.001)
|
|
||||||
Cost = ms;
|
|
||||||
else
|
|
||||||
Cost = (Cost * 3 + ms) / 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pi.Value;
|
return pi.Value;
|
||||||
}
|
}
|
||||||
@@ -200,7 +180,6 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
WriteLog("Return Error");
|
WriteLog("Return Error");
|
||||||
#endif
|
#endif
|
||||||
Interlocked.Increment(ref _ReleaseCount);
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -210,13 +189,11 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
|
|||||||
// 是否可用
|
// 是否可用
|
||||||
if (!OnReturn(value))
|
if (!OnReturn(value))
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref _ReleaseCount);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value is DisposeBase db && db.Disposed)
|
if (value is DisposeBase db && db.Disposed)
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref _ReleaseCount);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,39 +350,14 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ncount = _NewCount;
|
if (count > 0)
|
||||||
var fcount = _ReleaseCount;
|
|
||||||
if (count > 0 || ncount > 0 || fcount > 0)
|
|
||||||
{
|
{
|
||||||
Interlocked.Add(ref _NewCount, -ncount);
|
|
||||||
Interlocked.Add(ref _ReleaseCount, -fcount);
|
|
||||||
|
|
||||||
var p = Total == 0 ? 0 : (Double)Success / Total;
|
WriteLog("Release New={6:n0} Release={7:n0} Free={0} Busy={1} 清除过期资源 {2:n0} 项。", FreeCount, BusyCount, count);
|
||||||
|
|
||||||
WriteLog("Release New={6:n0} Release={7:n0} Free={0} Busy={1} 清除过期资源 {2:n0} 项。总请求 {3:n0} 次,命中 {4:p2},平均 {5:n2}us", FreeCount, BusyCount, count, Total, p, Cost * 1000, ncount, fcount);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 统计
|
|
||||||
private Int32 _Total;
|
|
||||||
/// <summary>总请求数</summary>
|
|
||||||
public Int32 Total => _Total;
|
|
||||||
|
|
||||||
private Int32 _Success;
|
|
||||||
/// <summary>成功数</summary>
|
|
||||||
public Int32 Success => _Success;
|
|
||||||
|
|
||||||
/// <summary>新创建数</summary>
|
|
||||||
private Int32 _NewCount;
|
|
||||||
|
|
||||||
/// <summary>释放数</summary>
|
|
||||||
private Int32 _ReleaseCount;
|
|
||||||
|
|
||||||
/// <summary>平均耗时。单位ms</summary>
|
|
||||||
private Double Cost;
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 日志
|
#region 日志
|
||||||
/// <summary>日志</summary>
|
/// <summary>日志</summary>
|
||||||
public ILog Log { get; set; } = Logger.Null;
|
public ILog Log { get; set; } = Logger.Null;
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PluginVersion>10.11.31</PluginVersion>
|
<PluginVersion>10.11.33</PluginVersion>
|
||||||
<ProPluginVersion>10.11.31</ProPluginVersion>
|
<ProPluginVersion>10.11.33</ProPluginVersion>
|
||||||
<DefaultVersion>10.11.31</DefaultVersion>
|
<DefaultVersion>10.11.33</DefaultVersion>
|
||||||
<AuthenticationVersion>10.11.3</AuthenticationVersion>
|
<AuthenticationVersion>10.11.3</AuthenticationVersion>
|
||||||
<SourceGeneratorVersion>10.11.3</SourceGeneratorVersion>
|
<SourceGeneratorVersion>10.11.3</SourceGeneratorVersion>
|
||||||
<NET8Version>8.0.19</NET8Version>
|
<NET8Version>8.0.19</NET8Version>
|
||||||
|
@@ -29,17 +29,7 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
||||||
public void SetDataHandlingAdapterLogger(ILog log)
|
|
||||||
{
|
|
||||||
if (_deviceDataHandleAdapter != ReadOnlyDataHandlingAdapter && ReadOnlyDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
|
||||||
{
|
|
||||||
_deviceDataHandleAdapter = handleAdapter;
|
|
||||||
}
|
|
||||||
if (_deviceDataHandleAdapter != null)
|
|
||||||
{
|
|
||||||
_deviceDataHandleAdapter.Logger = log;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
||||||
{
|
{
|
||||||
@@ -83,16 +73,27 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
|||||||
|
|
||||||
//private readonly WaitLock _connectLock = new WaitLock();
|
//private readonly WaitLock _connectLock = new WaitLock();
|
||||||
|
|
||||||
private IDeviceDataHandleAdapter _deviceDataHandleAdapter;
|
|
||||||
|
|
||||||
|
|
||||||
|
private bool logSet;
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void SetDataHandlingAdapterLogger(ILog log)
|
||||||
|
{
|
||||||
|
if (!logSet && ReadOnlyDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||||
|
{
|
||||||
|
logSet = true;
|
||||||
|
handleAdapter.Logger = log;
|
||||||
|
}
|
||||||
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetDataHandlingAdapter(DataHandlingAdapter adapter)
|
public void SetDataHandlingAdapter(DataHandlingAdapter adapter)
|
||||||
{
|
{
|
||||||
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
||||||
SetAdapter(singleStreamDataHandlingAdapter);
|
SetAdapter(singleStreamDataHandlingAdapter);
|
||||||
if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter)
|
|
||||||
_deviceDataHandleAdapter = deviceDataHandleAdapter;
|
logSet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设置数据处理适配器。
|
/// 设置数据处理适配器。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -47,16 +47,15 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
|||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => ProtectedDataHandlingAdapter;
|
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => ProtectedDataHandlingAdapter;
|
||||||
private IDeviceDataHandleAdapter _deviceDataHandleAdapter;
|
|
||||||
|
private bool logSet;
|
||||||
|
/// <inheritdoc/>
|
||||||
public void SetDataHandlingAdapterLogger(ILog log)
|
public void SetDataHandlingAdapterLogger(ILog log)
|
||||||
{
|
{
|
||||||
if (_deviceDataHandleAdapter != ProtectedDataHandlingAdapter && ProtectedDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
if (!logSet && ProtectedDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||||
{
|
{
|
||||||
_deviceDataHandleAdapter = handleAdapter;
|
logSet = true;
|
||||||
}
|
handleAdapter.Logger = log;
|
||||||
if (_deviceDataHandleAdapter != null)
|
|
||||||
{
|
|
||||||
_deviceDataHandleAdapter.Logger = log;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -64,10 +63,11 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
|||||||
{
|
{
|
||||||
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
||||||
SetAdapter(singleStreamDataHandlingAdapter);
|
SetAdapter(singleStreamDataHandlingAdapter);
|
||||||
if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter)
|
|
||||||
_deviceDataHandleAdapter = deviceDataHandleAdapter;
|
logSet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Started { get; } = new();
|
public ChannelEventHandler Started { get; } = new();
|
||||||
|
|
||||||
|
@@ -30,16 +30,13 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
|||||||
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
||||||
pool?.CancelAll();
|
pool?.CancelAll();
|
||||||
}
|
}
|
||||||
private IDeviceDataHandleAdapter _deviceDataHandleAdapter;
|
private bool logSet;
|
||||||
public void SetDataHandlingAdapterLogger(ILog log)
|
public void SetDataHandlingAdapterLogger(ILog log)
|
||||||
{
|
{
|
||||||
if (_deviceDataHandleAdapter != DataHandlingAdapter && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
if (!logSet && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||||
{
|
{
|
||||||
_deviceDataHandleAdapter = handleAdapter;
|
logSet = true;
|
||||||
}
|
handleAdapter.Logger = log;
|
||||||
if (_deviceDataHandleAdapter != null)
|
|
||||||
{
|
|
||||||
_deviceDataHandleAdapter.Logger = log;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -47,8 +44,8 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
|||||||
{
|
{
|
||||||
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
||||||
SetAdapter(singleStreamDataHandlingAdapter);
|
SetAdapter(singleStreamDataHandlingAdapter);
|
||||||
if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter)
|
|
||||||
_deviceDataHandleAdapter = deviceDataHandleAdapter;
|
logSet = false;
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
|
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
|
||||||
|
@@ -21,16 +21,14 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
|
|||||||
public TcpSessionClientChannel()
|
public TcpSessionClientChannel()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
private IDeviceDataHandleAdapter _deviceDataHandleAdapter;
|
private bool logSet;
|
||||||
|
/// <inheritdoc/>
|
||||||
public void SetDataHandlingAdapterLogger(ILog log)
|
public void SetDataHandlingAdapterLogger(ILog log)
|
||||||
{
|
{
|
||||||
if (_deviceDataHandleAdapter != DataHandlingAdapter && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
if (!logSet && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||||
{
|
{
|
||||||
_deviceDataHandleAdapter = handleAdapter;
|
logSet = true;
|
||||||
}
|
handleAdapter.Logger = log;
|
||||||
if (_deviceDataHandleAdapter != null)
|
|
||||||
{
|
|
||||||
_deviceDataHandleAdapter.Logger = log;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -38,9 +36,10 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
|
|||||||
{
|
{
|
||||||
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
||||||
SetAdapter(singleStreamDataHandlingAdapter);
|
SetAdapter(singleStreamDataHandlingAdapter);
|
||||||
if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter)
|
|
||||||
_deviceDataHandleAdapter = deviceDataHandleAdapter;
|
logSet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
||||||
{
|
{
|
||||||
var pool = WaitHandlePool;
|
var pool = WaitHandlePool;
|
||||||
|
@@ -26,16 +26,14 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
|||||||
ResetSign();
|
ResetSign();
|
||||||
}
|
}
|
||||||
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
||||||
private IDeviceDataHandleAdapter _deviceDataHandleAdapter;
|
private bool logSet;
|
||||||
|
/// <inheritdoc/>
|
||||||
public void SetDataHandlingAdapterLogger(ILog log)
|
public void SetDataHandlingAdapterLogger(ILog log)
|
||||||
{
|
{
|
||||||
if (_deviceDataHandleAdapter != DataHandlingAdapter && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
if (!logSet && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||||
{
|
{
|
||||||
_deviceDataHandleAdapter = handleAdapter;
|
logSet = true;
|
||||||
}
|
handleAdapter.Logger = log;
|
||||||
if (_deviceDataHandleAdapter != null)
|
|
||||||
{
|
|
||||||
_deviceDataHandleAdapter.Logger = log;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -43,9 +41,11 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
|||||||
{
|
{
|
||||||
if (adapter is UdpDataHandlingAdapter udpDataHandlingAdapter)
|
if (adapter is UdpDataHandlingAdapter udpDataHandlingAdapter)
|
||||||
SetAdapter(udpDataHandlingAdapter);
|
SetAdapter(udpDataHandlingAdapter);
|
||||||
if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter)
|
|
||||||
_deviceDataHandleAdapter = deviceDataHandleAdapter;
|
logSet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
||||||
{
|
{
|
||||||
var pool = WaitHandlePool;
|
var pool = WaitHandlePool;
|
||||||
|
@@ -331,34 +331,23 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
}
|
}
|
||||||
public bool AutoConnect { get; protected set; } = true;
|
public bool AutoConnect { get; protected set; } = true;
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
private async ValueTask<OperResult> SendAsync(ISendMessage sendMessage, IClientChannel channel, CancellationToken token = default)
|
private async Task SendAsync(ISendMessage sendMessage, IClientChannel channel, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
|
if (SendDelayTime != 0)
|
||||||
|
await Task.Delay(SendDelayTime, token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (channel is IDtuUdpSessionChannel udpSession)
|
||||||
{
|
{
|
||||||
|
EndPoint? endPoint = GetUdpEndpoint();
|
||||||
|
await udpSession.SendAsync(endPoint, sendMessage, token).ConfigureAwait(false);
|
||||||
|
|
||||||
if (SendDelayTime != 0)
|
|
||||||
await Task.Delay(SendDelayTime, token).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (token.IsCancellationRequested)
|
|
||||||
return new OperResult(new OperationCanceledException());
|
|
||||||
|
|
||||||
if (channel is IDtuUdpSessionChannel udpSession)
|
|
||||||
{
|
|
||||||
EndPoint? endPoint = GetUdpEndpoint();
|
|
||||||
await udpSession.SendAsync(endPoint, sendMessage, token).ConfigureAwait(false);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await channel.SendAsync(sendMessage, token).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return OperResult.Success;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
else
|
||||||
{
|
{
|
||||||
return new(ex);
|
await channel.SendAsync(sendMessage, token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task BeforeSendAsync(IClientChannel channel, CancellationToken token)
|
private Task BeforeSendAsync(IClientChannel channel, CancellationToken token)
|
||||||
@@ -417,7 +406,8 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
channelResult.Content.SetDataHandlingAdapterLogger(Logger);
|
channelResult.Content.SetDataHandlingAdapterLogger(Logger);
|
||||||
|
|
||||||
|
|
||||||
return await SendAsync(sendMessage, channelResult.Content, cancellationToken).ConfigureAwait(false);
|
await SendAsync(sendMessage, channelResult.Content, cancellationToken).ConfigureAwait(false);
|
||||||
|
return OperResult.Success;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -426,8 +416,6 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
if (!cancellationToken.IsCancellationRequested)
|
|
||||||
await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
|
|
||||||
return new(ex);
|
return new(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -538,7 +526,6 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ObjectPool<ReusableCancellationTokenSource> _reusableTimeouts = new();
|
private ObjectPool<ReusableCancellationTokenSource> _reusableTimeouts = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 发送并等待数据
|
/// 发送并等待数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -548,6 +535,8 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
int timeout = 3000,
|
int timeout = 3000,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
var waitData = clientChannel.WaitHandlePool.GetWaitDataAsync(out var sign);
|
var waitData = clientChannel.WaitHandlePool.GetWaitDataAsync(out var sign);
|
||||||
command.Sign = sign;
|
command.Sign = sign;
|
||||||
WaitLock? waitLock = null;
|
WaitLock? waitLock = null;
|
||||||
@@ -557,13 +546,12 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
await BeforeSendAsync(clientChannel, cancellationToken).ConfigureAwait(false);
|
await BeforeSendAsync(clientChannel, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
waitLock = GetWaitLock(clientChannel);
|
waitLock = GetWaitLock(clientChannel);
|
||||||
|
|
||||||
await waitLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
await waitLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
clientChannel.SetDataHandlingAdapterLogger(Logger);
|
clientChannel.SetDataHandlingAdapterLogger(Logger);
|
||||||
|
|
||||||
var sendResult = await SendAsync(command, clientChannel, cancellationToken).ConfigureAwait(false);
|
await SendAsync(command, clientChannel, cancellationToken).ConfigureAwait(false);
|
||||||
if (!sendResult.IsSuccess)
|
|
||||||
return new MessageBase(sendResult);
|
|
||||||
|
|
||||||
if (waitData.Status == WaitDataStatus.Success)
|
if (waitData.Status == WaitDataStatus.Success)
|
||||||
return waitData.CompletedData;
|
return waitData.CompletedData;
|
||||||
@@ -573,6 +561,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
var reusableTimeout = _reusableTimeouts.Get();
|
var reusableTimeout = _reusableTimeouts.Get();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
var cts = reusableTimeout.GetTokenSource(timeout, cancellationToken, Channel.ClosedToken);
|
var cts = reusableTimeout.GetTokenSource(timeout, cancellationToken, Channel.ClosedToken);
|
||||||
await waitData.WaitAsync(cts.Token).ConfigureAwait(false);
|
await waitData.WaitAsync(cts.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -606,6 +595,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
{
|
{
|
||||||
waitLock?.Release();
|
waitLock?.Release();
|
||||||
waitData?.SafeDispose();
|
waitData?.SafeDispose();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,35 +8,48 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Application;
|
namespace ThingsGateway.Foundation;
|
||||||
|
|
||||||
public class LinkedCancellationTokenSourceCache : IDisposable
|
public class LinkedCancellationTokenSourceCache : IDisposable
|
||||||
{
|
{
|
||||||
private CancellationTokenSource? _cachedCts;
|
private CancellationTokenSource? _cachedCts;
|
||||||
private CancellationToken _token1;
|
private CancellationToken _token1;
|
||||||
private CancellationToken _token2;
|
private CancellationToken _token2;
|
||||||
|
private CancellationToken _token3;
|
||||||
private readonly object _lock = new();
|
private readonly object _lock = new();
|
||||||
~LinkedCancellationTokenSourceCache()
|
~LinkedCancellationTokenSourceCache()
|
||||||
{
|
{
|
||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取一个 CancellationTokenSource,它是由两个 token 链接而成的。
|
/// 获取一个 CancellationTokenSource,它是由两个 token 链接而成的。
|
||||||
/// 会尝试复用之前缓存的 CTS,前提是两个 token 仍然相同且未取消。
|
/// 会尝试复用之前缓存的 CTS,前提是两个 token 仍然相同且未取消。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public CancellationTokenSource GetLinkedTokenSource(CancellationToken token1, CancellationToken token2)
|
public CancellationTokenSource GetLinkedTokenSource(CancellationToken token1, CancellationToken token2, CancellationToken token3 = default)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
// 如果缓存的 CTS 已经取消或 Dispose,或者 token 不同,重新创建
|
// 如果缓存的 CTS 已经取消或 Dispose,或者 token 不同,重新创建
|
||||||
if (_cachedCts?.IsCancellationRequested != false ||
|
if (_cachedCts?.IsCancellationRequested != false ||
|
||||||
!_token1.Equals(token1) || !_token2.Equals(token2))
|
!_token1.Equals(token1) || !_token2.Equals(token2) || !_token3.Equals(token3))
|
||||||
{
|
{
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
if (_cachedCts?.TryReset() != true)
|
||||||
|
{
|
||||||
|
_cachedCts?.Dispose();
|
||||||
|
_cachedCts = CancellationTokenSource.CreateLinkedTokenSource(token1, token2, token3);
|
||||||
|
}
|
||||||
|
#else
|
||||||
_cachedCts?.Dispose();
|
_cachedCts?.Dispose();
|
||||||
|
|
||||||
_cachedCts = CancellationTokenSource.CreateLinkedTokenSource(token1, token2);
|
_cachedCts = CancellationTokenSource.CreateLinkedTokenSource(token1, token2, token3);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
_token1 = token1;
|
_token1 = token1;
|
||||||
_token2 = token2;
|
_token2 = token2;
|
||||||
|
_token3 = token3;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _cachedCts;
|
return _cachedCts;
|
@@ -18,7 +18,6 @@ using System.Threading;
|
|||||||
public sealed class ReusableCancellationTokenSource : IDisposable
|
public sealed class ReusableCancellationTokenSource : IDisposable
|
||||||
{
|
{
|
||||||
private readonly Timer _timer;
|
private readonly Timer _timer;
|
||||||
private readonly object _lock = new();
|
|
||||||
private CancellationTokenSource? _cts;
|
private CancellationTokenSource? _cts;
|
||||||
|
|
||||||
public ReusableCancellationTokenSource()
|
public ReusableCancellationTokenSource()
|
||||||
@@ -30,59 +29,29 @@ public sealed class ReusableCancellationTokenSource : IDisposable
|
|||||||
|
|
||||||
private void OnTimeout(object? state)
|
private void OnTimeout(object? state)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
TimeoutStatus = true;
|
||||||
{
|
|
||||||
TimeoutStatus = true;
|
|
||||||
|
|
||||||
if (_cts?.IsCancellationRequested == false)
|
if (_cts?.IsCancellationRequested == false)
|
||||||
_cts?.Cancel();
|
_cts?.Cancel();
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// <summary>
|
|
||||||
/// 获取一个 CTS,并启动超时
|
|
||||||
/// </summary>
|
|
||||||
public CancellationTokenSource GetTokenSource(long timeout, CancellationToken external1 = default)
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
TimeoutStatus = false;
|
|
||||||
|
|
||||||
// 如果已有 CTS,先 Dispose
|
private readonly LinkedCancellationTokenSourceCache _linkedCtsCache = new();
|
||||||
_cts?.SafeCancel();
|
|
||||||
_cts?.SafeDispose();
|
|
||||||
|
|
||||||
// 创建新的 CTS
|
|
||||||
_cts = CancellationTokenSource.CreateLinkedTokenSource(external1);
|
|
||||||
|
|
||||||
// 启动 Timer
|
|
||||||
_timer.Change(timeout, Timeout.Infinite);
|
|
||||||
|
|
||||||
return _cts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取一个 CTS,并启动超时
|
/// 获取一个 CTS,并启动超时
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public CancellationTokenSource GetTokenSource(long timeout, CancellationToken external1 = default, CancellationToken external2 = default, CancellationToken external3 = default)
|
public CancellationTokenSource GetTokenSource(long timeout, CancellationToken external1 = default, CancellationToken external2 = default, CancellationToken external3 = default)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
TimeoutStatus = false;
|
||||||
{
|
|
||||||
TimeoutStatus = false;
|
|
||||||
|
|
||||||
// 如果已有 CTS,先 Dispose
|
// 创建新的 CTS
|
||||||
_cts?.SafeCancel();
|
_cts = _linkedCtsCache.GetLinkedTokenSource(external1, external2, external3);
|
||||||
_cts?.SafeDispose();
|
|
||||||
|
|
||||||
// 创建新的 CTS
|
// 启动 Timer
|
||||||
_cts = CancellationTokenSource.CreateLinkedTokenSource(external1, external2, external3);
|
_timer.Change(timeout, Timeout.Infinite);
|
||||||
|
|
||||||
// 启动 Timer
|
return _cts;
|
||||||
_timer.Change(timeout, Timeout.Infinite);
|
|
||||||
|
|
||||||
return _cts;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -96,20 +65,15 @@ public sealed class ReusableCancellationTokenSource : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Cancel()
|
public void Cancel()
|
||||||
{
|
{
|
||||||
lock (_lock)
|
_cts?.SafeCancel();
|
||||||
{
|
|
||||||
_cts?.SafeCancel();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
lock (_lock)
|
_cts?.SafeCancel();
|
||||||
{
|
_cts?.SafeDispose();
|
||||||
_cts?.SafeCancel();
|
_linkedCtsCache.SafeDispose();
|
||||||
_cts?.SafeDispose();
|
_timer.SafeDispose();
|
||||||
_timer.SafeDispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,194 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://kimdiego2098.github.io/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using BenchmarkConsoleApp;
|
||||||
|
|
||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using BenchmarkDotNet.Diagnosers;
|
||||||
|
|
||||||
|
using HslCommunication.ModBus;
|
||||||
|
|
||||||
|
using System.Net.Sockets;
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Modbus;
|
||||||
|
|
||||||
|
using TouchSocket.Core;
|
||||||
|
using TouchSocket.Modbus;
|
||||||
|
|
||||||
|
using IModbusMaster = NModbus.IModbusMaster;
|
||||||
|
using ModbusMaster = ThingsGateway.Foundation.Modbus.ModbusMaster;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation;
|
||||||
|
|
||||||
|
[MemoryDiagnoser]
|
||||||
|
public class ModbusBenchmark : IDisposable
|
||||||
|
{
|
||||||
|
private List<ModbusMaster> thingsgatewaymodbuss = new();
|
||||||
|
private List<IModbusMaster> nmodbuss = new();
|
||||||
|
private List<ModbusTcpNet> modbusTcpNets = new();
|
||||||
|
private List<ModbusTcpMaster> modbusTcpMasters = new();
|
||||||
|
|
||||||
|
public ModbusBenchmark()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Program.ClientCount; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
var clientConfig = new TouchSocket.Core.TouchSocketConfig();
|
||||||
|
var clientChannel = clientConfig.GetTcpClientWithIPHost(new ChannelOptions() { RemoteUrl = "127.0.0.1:502", MaxConcurrentCount = 10 });
|
||||||
|
var thingsgatewaymodbus = new ModbusMaster()
|
||||||
|
{
|
||||||
|
//modbus协议格式
|
||||||
|
ModbusType = ModbusTypeEnum.ModbusTcp,
|
||||||
|
};
|
||||||
|
thingsgatewaymodbus.InitChannel(clientChannel);
|
||||||
|
clientChannel.SetupAsync(clientChannel.Config).GetFalseAwaitResult();
|
||||||
|
clientChannel.Logger.LogLevel = LogLevel.Warning;
|
||||||
|
thingsgatewaymodbus.ConnectAsync(CancellationToken.None).GetFalseAwaitResult();
|
||||||
|
thingsgatewaymodbus.ReadAsync("40001", 100).GetAwaiter().GetResult();
|
||||||
|
thingsgatewaymodbuss.Add(thingsgatewaymodbus);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < Program.ClientCount; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
var factory = new NModbus.ModbusFactory();
|
||||||
|
var nmodbus = factory.CreateMaster(new TcpClient("127.0.0.1", 502));
|
||||||
|
nmodbus.ReadHoldingRegistersAsync(1, 0, 100).GetFalseAwaitResult();
|
||||||
|
nmodbuss.Add(nmodbus);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < Program.ClientCount; i++)
|
||||||
|
{
|
||||||
|
ModbusTcpNet modbusTcpNet = new();
|
||||||
|
modbusTcpNet.IpAddress = "127.0.0.1";
|
||||||
|
modbusTcpNet.Port = 502;
|
||||||
|
modbusTcpNet.ConnectServer();
|
||||||
|
modbusTcpNet.ReadAsync("0", 100).GetFalseAwaitResult();
|
||||||
|
modbusTcpNets.Add(modbusTcpNet);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < Program.ClientCount; i++)
|
||||||
|
{
|
||||||
|
var client = new ModbusTcpMaster();
|
||||||
|
client.SetupAsync(new TouchSocketConfig()
|
||||||
|
.SetRemoteIPHost("127.0.0.1:502")).GetFalseAwaitResult();
|
||||||
|
client.ConnectAsync(CancellationToken.None).GetFalseAwaitResult();
|
||||||
|
client.ReadHoldingRegistersAsync(0, 100).GetFalseAwaitResult();
|
||||||
|
modbusTcpMasters.Add(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public async Task ThingsGateway()
|
||||||
|
{
|
||||||
|
ModbusAddress addr = new ModbusAddress() { FunctionCode = 3, StartAddress = 0, Length = 100 };
|
||||||
|
List<Task> tasks = new List<Task>();
|
||||||
|
foreach (var thingsgatewaymodbus in thingsgatewaymodbuss)
|
||||||
|
{
|
||||||
|
|
||||||
|
for (int i = 0; i < Program.TaskNumberOfItems; i++)
|
||||||
|
{
|
||||||
|
tasks.Add(Task.Run(async () =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Program.NumberOfItems; i++)
|
||||||
|
{
|
||||||
|
var result = await thingsgatewaymodbus.ModbusReadAsync(addr);
|
||||||
|
if (!result.IsSuccess)
|
||||||
|
{
|
||||||
|
throw new Exception(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff") + result.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public async Task TouchSocket()
|
||||||
|
{
|
||||||
|
List<Task> tasks = new List<Task>();
|
||||||
|
foreach (var modbusTcpMaster in modbusTcpMasters)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Program.TaskNumberOfItems; i++)
|
||||||
|
{
|
||||||
|
tasks.Add(Task.Run(async () =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Program.NumberOfItems; i++)
|
||||||
|
{
|
||||||
|
var result = await modbusTcpMaster.ReadHoldingRegistersAsync(0, 100);
|
||||||
|
if (!result.IsSuccess)
|
||||||
|
{
|
||||||
|
throw new Exception(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff") + result.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public async Task NModbus4()
|
||||||
|
{
|
||||||
|
List<Task> tasks = new List<Task>();
|
||||||
|
foreach (var nmodbus in nmodbuss)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Program.TaskNumberOfItems; i++)
|
||||||
|
{
|
||||||
|
tasks.Add(Task.Run(async () =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Program.NumberOfItems; i++)
|
||||||
|
{
|
||||||
|
var result = await nmodbus.ReadHoldingRegistersAsync(1, 0, 100);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//并发失败
|
||||||
|
//[Benchmark]
|
||||||
|
//public async Task HslCommunication()
|
||||||
|
//{
|
||||||
|
// List<Task> tasks = new List<Task>();
|
||||||
|
// foreach (var modbusTcpNet in modbusTcpNets)
|
||||||
|
// {
|
||||||
|
// for (int i = 0; i < Program.TaskNumberOfItems; i++)
|
||||||
|
// {
|
||||||
|
// tasks.Add(Task.Run(async () =>
|
||||||
|
// {
|
||||||
|
// for (int i = 0; i < Program.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());
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,153 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://kimdiego2098.github.io/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using BenchmarkConsoleApp;
|
||||||
|
|
||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using BenchmarkDotNet.Diagnosers;
|
||||||
|
|
||||||
|
using HslCommunication.Profinet.Siemens;
|
||||||
|
|
||||||
|
using S7.Net;
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.SiemensS7;
|
||||||
|
|
||||||
|
using TouchSocket.Core;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation;
|
||||||
|
|
||||||
|
[MemoryDiagnoser]
|
||||||
|
public class S7Benchmark : IDisposable
|
||||||
|
{
|
||||||
|
private List<SiemensS7Master> siemensS7s = new();
|
||||||
|
|
||||||
|
private List<Plc> plcs = new();
|
||||||
|
private List<SiemensS7Net> siemensS7Nets = new();
|
||||||
|
|
||||||
|
public S7Benchmark()
|
||||||
|
|
||||||
|
{
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Program.ClientCount; i++)
|
||||||
|
{
|
||||||
|
var clientConfig = new TouchSocket.Core.TouchSocketConfig();
|
||||||
|
var clientChannel = clientConfig.GetTcpClientWithIPHost(new ChannelOptions() { RemoteUrl = "127.0.0.1:102" });
|
||||||
|
var siemensS7 = new SiemensS7Master()
|
||||||
|
{
|
||||||
|
//modbus协议格式
|
||||||
|
SiemensS7Type = SiemensTypeEnum.S1500
|
||||||
|
};
|
||||||
|
siemensS7.InitChannel(clientChannel);
|
||||||
|
clientChannel.SetupAsync(clientChannel.Config).GetFalseAwaitResult();
|
||||||
|
clientChannel.Logger.LogLevel = LogLevel.Warning;
|
||||||
|
siemensS7.ConnectAsync(CancellationToken.None).GetFalseAwaitResult();
|
||||||
|
siemensS7.ReadAsync("M1", 100).GetAwaiter().GetResult();
|
||||||
|
siemensS7s.Add(siemensS7);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < Program.ClientCount; i++)
|
||||||
|
{
|
||||||
|
var siemensS7Net = new SiemensS7Net(SiemensPLCS.S1500, "127.0.0.1");
|
||||||
|
siemensS7Net.ConnectServer();
|
||||||
|
siemensS7Net.ReadAsync("M0", 100).GetFalseAwaitResult();
|
||||||
|
siemensS7Nets.Add(siemensS7Net);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < Program.ClientCount; i++)
|
||||||
|
{
|
||||||
|
var plc = new Plc(CpuType.S7300, "127.0.0.1", 102, 0, 0);
|
||||||
|
plc.Open();//打开plc连接
|
||||||
|
plc.ReadAsync(DataType.Memory, 1, 0, VarType.Byte, 100).GetFalseAwaitResult();
|
||||||
|
plcs.Add(plc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public async Task S7netplus()
|
||||||
|
{
|
||||||
|
List<Task> tasks = new List<Task>();
|
||||||
|
foreach (var plc in plcs)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Program.TaskNumberOfItems; i++)
|
||||||
|
{
|
||||||
|
tasks.Add(Task.Run(async () =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Program.NumberOfItems; i++)
|
||||||
|
{
|
||||||
|
var result = await plc.ReadAsync(DataType.Memory, 1, 0, VarType.Byte, 100);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 < Program.TaskNumberOfItems; i++)
|
||||||
|
// {
|
||||||
|
// tasks.Add(Task.Run(async () =>
|
||||||
|
// {
|
||||||
|
// for (int i = 0; i < Program.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()
|
||||||
|
{
|
||||||
|
SiemensS7Address[] siemensS7Address = [SiemensS7Address.ParseFrom("M1", 100)];
|
||||||
|
List<Task> tasks = new List<Task>();
|
||||||
|
foreach (var siemensS7 in siemensS7s)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Program.TaskNumberOfItems; i++)
|
||||||
|
{
|
||||||
|
tasks.Add(Task.Run(async () =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Program.NumberOfItems; i++)
|
||||||
|
{
|
||||||
|
var result = await siemensS7.S7ReadAsync(siemensS7Address);
|
||||||
|
if (!result.IsSuccess)
|
||||||
|
{
|
||||||
|
throw new Exception(result.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
plcs.ForEach(a => a.SafeDispose());
|
||||||
|
siemensS7Nets.ForEach(a => a.SafeDispose());
|
||||||
|
siemensS7s.ForEach(a => a.Channel.SafeDispose());
|
||||||
|
siemensS7s.ForEach(a => a.SafeDispose());
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,62 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://kimdiego2098.github.io/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using BenchmarkDotNet.Diagnosers;
|
||||||
|
|
||||||
|
using ThingsGateway.NewLife.Collections;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation;
|
||||||
|
|
||||||
|
[MemoryDiagnoser]
|
||||||
|
public class TimeoutBenchmark
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public async ValueTask CtsWaitAsync()
|
||||||
|
{
|
||||||
|
using var otherCts = new CancellationTokenSource();
|
||||||
|
for (int i1 = 0; i1 < 10; i1++)
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
using var ctsTime = new CancellationTokenSource(TimeSpan.FromMilliseconds(10));
|
||||||
|
using var cts = CancellationTokenSource.CreateLinkedTokenSource(ctsTime.Token, otherCts.Token);
|
||||||
|
|
||||||
|
await Task.Delay(5, cts.Token).ConfigureAwait(false); // 模拟工作
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObjectPool<ReusableCancellationTokenSource> _reusableTimeouts;
|
||||||
|
[Benchmark]
|
||||||
|
public async ValueTask ReusableTimeoutWaitAsync()
|
||||||
|
{
|
||||||
|
_reusableTimeouts ??= new();
|
||||||
|
using var otherCts = new CancellationTokenSource();
|
||||||
|
for (int i1 = 0; i1 < 10; i1++)
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
var _reusableTimeout = _reusableTimeouts.Get();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(5, _reusableTimeout.GetTokenSource(10, otherCts.Token).Token).ConfigureAwait(false); // 模拟工作
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_reusableTimeouts.Return(_reusableTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_reusableTimeouts.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
59
src/Plugin/ThingsGateway.Foundation.Benchmark/Program.cs
Normal file
59
src/Plugin/ThingsGateway.Foundation.Benchmark/Program.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权(除特别声明或在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首页:http://rrqm_home.gitee.io/touchsocket/
|
||||||
|
// 交流QQ群:234762506
|
||||||
|
// 感谢您的下载和使用
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using BenchmarkDotNet.Configs;
|
||||||
|
using BenchmarkDotNet.Running;
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation;
|
||||||
|
|
||||||
|
namespace BenchmarkConsoleApp
|
||||||
|
{
|
||||||
|
internal class Program
|
||||||
|
{
|
||||||
|
public static int ClientCount = 30;
|
||||||
|
public static int TaskNumberOfItems = 30;
|
||||||
|
public static int NumberOfItems = 30;
|
||||||
|
|
||||||
|
private static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
Console.WriteLine("开始测试前,请先启动ModbusSlave,建议使用本项目自带的ThingsGateway.Debug.Photino软件开启,S7可以用KEPSERVER的S7模拟服务");
|
||||||
|
Console.WriteLine($"多客户端({ClientCount}),多线程({TaskNumberOfItems})并发读取({NumberOfItems})测试,共{ClientCount * TaskNumberOfItems * NumberOfItems}次");
|
||||||
|
await Task.CompletedTask;
|
||||||
|
//ModbusBenchmark modbusBenchmark = new ModbusBenchmark();
|
||||||
|
//System.Diagnostics.Stopwatch stopwatch = new();
|
||||||
|
//stopwatch.Start();
|
||||||
|
//await modbusBenchmark.ThingsGateway();
|
||||||
|
//stopwatch.Stop();
|
||||||
|
//Console.WriteLine($"ThingsGateway耗时:{stopwatch.ElapsedMilliseconds}ms");
|
||||||
|
//stopwatch.Restart();
|
||||||
|
//await modbusBenchmark.TouchSocket();
|
||||||
|
//stopwatch.Stop();
|
||||||
|
//Console.WriteLine($"TouchSocket耗时:{stopwatch.ElapsedMilliseconds}ms");
|
||||||
|
//Console.ReadLine();
|
||||||
|
|
||||||
|
// BenchmarkRunner.Run<TimeoutBenchmark>(
|
||||||
|
//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)
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,58 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
|
||||||
|
<AnalysisModeDesign>None</AnalysisModeDesign>
|
||||||
|
<AnalysisModeDocumentation>None</AnalysisModeDocumentation>
|
||||||
|
<AnalysisModeGlobalization>None</AnalysisModeGlobalization>
|
||||||
|
<AnalysisModeInteroperability>None</AnalysisModeInteroperability>
|
||||||
|
<AnalysisModeMaintainability>None</AnalysisModeMaintainability>
|
||||||
|
<AnalysisModeNaming>None</AnalysisModeNaming>
|
||||||
|
<AnalysisModePerformance>None</AnalysisModePerformance>
|
||||||
|
<AnalysisModeSingleFile>None</AnalysisModeSingleFile>
|
||||||
|
<AnalysisModeReliability>None</AnalysisModeReliability>
|
||||||
|
<AnalysisModeSecurity>None</AnalysisModeSecurity>
|
||||||
|
<AnalysisModeUsage>None</AnalysisModeUsage>
|
||||||
|
<AnalysisModeStyle>None</AnalysisModeStyle>
|
||||||
|
|
||||||
|
|
||||||
|
<TargetFrameworks>net8.0;</TargetFrameworks>
|
||||||
|
<LangVersion>13.0</LangVersion>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<Authors>Diego</Authors>
|
||||||
|
<Company>Diego</Company>
|
||||||
|
<Product>Diego</Product>
|
||||||
|
<Copyright>版权所有 © 2023-present Diego</Copyright>
|
||||||
|
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
|
||||||
|
<RepositoryType>Gitee</RepositoryType>
|
||||||
|
<GenerateResxSourceIncludeDefaultValues>true</GenerateResxSourceIncludeDefaultValues>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Remove="Roslynator.Analyzers">
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="BenchmarkDotNet" Version="0.15.2" />
|
||||||
|
<PackageReference Include="HslCommunication" Version="12.3.3" />
|
||||||
|
<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="10.11.32" />
|
||||||
|
<PackageReference Include="ThingsGateway.Foundation.SiemensS7" Version="10.11.32" />
|
||||||
|
<PackageReference Include="TouchSocket.Modbus" Version="4.0.0-beta.25" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<!--<ProjectReference Include="..\..\Plugin\ThingsGateway.Foundation.Modbus\ThingsGateway.Foundation.Modbus.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Plugin\ThingsGateway.Foundation.SiemensS7\ThingsGateway.Foundation.SiemensS7.csproj" />-->
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
Reference in New Issue
Block a user