添加IAsyncDisposable

This commit is contained in:
Diego
2025-08-01 16:36:27 +08:00
parent 8ce0b981c1
commit 453817ef86
54 changed files with 572 additions and 195 deletions

View File

@@ -683,36 +683,20 @@ public class MachineInfo : IExtend
if (dic.TryGetValue("MemTotal", out var str) && !str.IsNullOrEmpty())
Memory = (UInt64)str.TrimEnd(" kB").ToLong();
/*
指标 含义 是否可回收
MemTotal 系统总物理内存 ❌ 固定值
MemFree 完全未使用的内存(不含缓存/缓冲区) ✅ 100% 可用
MemAvailable 内核估计的可用内存(含可回收缓存和缓冲区) ✅ 最权威的保守估计
Cached 文件系统缓存Page Cache可完全回收 ✅ 100% 可回收
SReclaimable Slab 缓存中可回收的部分(如 dentry/inode ✅ 大部分可回收80%~90%
Buffers 磁盘块缓存(现代内核中值较小,可回收) ✅ 可回收
Slab 内核对象缓存总大小(含可回收和不可回收部分) ⚠️ 需区分 SReclaimable
SwapCached 被缓存到 Swap 的内存(可回收,但性能较差) ✅ 可回收但不建议依赖
ulong ma = 0;
if (dic.TryGetValue("MemAvailable", out str) && !str.IsNullOrEmpty())
{
ma = (UInt64)(str.TrimEnd(" kB").ToLong());
}
*/
var ma = (UInt64)(dic["MemAvailable"]?.TrimEnd(" kB").ToLong() ?? 0);
//低于3.14内核的版本用 free+cache
var mf = (UInt64)(dic["MemFree"]?.TrimEnd(" kB").ToLong() ?? 0);
var mc = (UInt64)(dic["Cached"]?.TrimEnd(" kB").ToLong() ?? 0);
var bf = (UInt64)(dic["Buffers"]?.TrimEnd(" kB").ToLong() ?? 0);
if (dic.TryGetValue("SReclaimable", out str) && !str.IsNullOrEmpty())
{
var sr = (UInt64)(str?.TrimEnd(" kB").ToLong() ?? 0);
mf += (ulong)(sr * 0.9);
}
if (dic.TryGetValue("Buffers", out str) && !str.IsNullOrEmpty())
{
var bf = (UInt64)(str?.TrimEnd(" kB").ToLong() ?? 0);
mf += bf;
}
mf += mc;
var free = mf + mc + bf;
AvailableMemory = ma > mf ? ma : mf;
AvailableMemory = ma > free ? ma : free;
}
// A2/A4温度获取BuildrootCPU温度和主板温度

View File

@@ -1,9 +1,9 @@
<Project>
<PropertyGroup>
<PluginVersion>10.10.1</PluginVersion>
<ProPluginVersion>10.10.1</ProPluginVersion>
<DefaultVersion>10.10.2</DefaultVersion>
<PluginVersion>10.10.2</PluginVersion>
<ProPluginVersion>10.10.2</ProPluginVersion>
<DefaultVersion>10.10.3</DefaultVersion>
<AuthenticationVersion>2.9.29</AuthenticationVersion>
<SourceGeneratorVersion>10.9.29</SourceGeneratorVersion>
<NET8Version>8.0.18</NET8Version>

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<TargetFrameworks>net462;netstandard2.0;net6.0;</TargetFrameworks>
<TargetFrameworks>net462;netstandard2.0;net6.0;net8.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>

View File

@@ -134,6 +134,7 @@ public abstract class TcpServiceChannelBase<TClient> : TcpService<TClient>, ITcp
protected override void SafetyDispose(bool disposing)
{
m_transport?.SafeCancel();
m_transport?.SafeDispose();
base.SafetyDispose(disposing);
}

View File

@@ -204,6 +204,7 @@ public class UdpSessionChannel : UdpSession, IClientChannel
/// <inheritdoc/>
protected override void SafetyDispose(bool disposing)
{
m_transport?.SafeCancel();
m_transport?.SafeDispose();
WaitHandlePool.SafeDispose();
base.SafetyDispose(disposing);

View File

@@ -24,7 +24,7 @@ namespace ThingsGateway.Foundation;
/// <summary>
/// 协议基类
/// </summary>
public abstract class DeviceBase : DisposableObject, IDevice
public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
{
/// <inheritdoc/>
public IChannel Channel { get; private set; }
@@ -585,13 +585,6 @@ public abstract class DeviceBase : DisposableObject, IDevice
}
catch (Exception ex)
{
if (cancellationToken.IsCancellationRequested)
{
if (!this.DisposedValue)
{
await Task.Delay(timeout, Channel.ClosedToken).ConfigureAwait(false);
}
}
return new MessageBase(ex);
}
var result = waitData.Check();
@@ -1041,6 +1034,56 @@ public abstract class DeviceBase : DisposableObject, IDevice
base.Dispose(disposing);
}
/// <inheritdoc/>
protected override async Task DisposeAsync(bool disposing)
{
if (Channel != null)
{
Channel.Starting.Remove(ChannelStarting);
Channel.Stoped.Remove(ChannelStoped);
Channel.Started.Remove(ChannelStarted);
Channel.Stoping.Remove(ChannelStoping);
Channel.ChannelReceived.Remove(ChannelReceived);
if (Channel.Collects.Count == 1)
{
if (Channel is ITcpServiceChannel tcpServiceChannel)
{
tcpServiceChannel.Clients.ForEach(a => a.WaitHandlePool.SafeDispose());
}
try
{
//只关闭,不释放
await Channel.CloseAsync().ConfigureAwait(false);
if (Channel is IClientChannel client)
{
client.WaitHandlePool.SafeDispose();
}
}
catch (Exception ex)
{
Logger?.LogWarning(ex);
}
}
else
{
if (Channel is ITcpServiceChannel tcpServiceChannel && this is IDtu dtu)
{
if (tcpServiceChannel.TryGetClient($"ID={dtu.DtuId}", out var client))
{
client.WaitHandlePool?.SafeDispose();
await client.CloseAsync().ConfigureAwait(false);
}
}
}
Channel.Collects.Remove(this);
}
_deviceLogger?.TryDispose();
base.Dispose(disposing);
}
/// <inheritdoc/>
public virtual Action<IPluginManager> ConfigurePlugins(TouchSocketConfig config)
{
@@ -1058,4 +1101,5 @@ public abstract class DeviceBase : DisposableObject, IDevice
return a => { };
}
public abstract ValueTask<OperResult<ReadOnlyMemory<byte>>> ReadAsync(object state, CancellationToken cancellationToken = default);
}

View File

@@ -15,7 +15,7 @@ namespace ThingsGateway.Foundation;
/// <summary>
/// 协议设备接口
/// </summary>
public interface IDevice : IDisposable, IDisposableObject
public interface IDevice : IDisposable, IDisposableObject, IAsyncDisposable
{
#region

View File

@@ -0,0 +1,73 @@
// ------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
// ------------------------------------------------------------------------------
namespace ThingsGateway;
public static class DisposableExtensions
{
#region IDisposable
/// <summary>
/// 安全性释放(不用判断对象是否为空)。不会抛出任何异常。
/// </summary>
/// <param name="dis"></param>
/// <returns>释放状态,当对象为<see langword="null"/>,或者已被释放时,均会返回<see cref="Result.Success"/>,只有实际在释放时遇到异常时,才显示其他状态。</returns>
public static async Task<Result> SafeDisposeAsync(this IAsyncDisposable dis)
{
if (dis == default)
{
return Result.Success;
}
try
{
await dis.DisposeAsync().ConfigureAwait(false);
return Result.Success;
}
catch (Exception ex)
{
return Result.FromException(ex);
}
}
#endregion IDisposable
#if NET8_0_OR_GREATER
/// <summary>
/// 安全地取消 <see cref="CancellationTokenSource"/>,并返回操作结果。
/// </summary>
/// <param name="tokenSource">要取消的 <see cref="CancellationTokenSource"/>。</param>
/// <returns>一个 <see cref="Result"/> 对象,表示操作的结果。</returns>
public static async Task<Result> SafeCancelAsync(this CancellationTokenSource tokenSource)
{
if (tokenSource is null)
{
return Result.Success;
}
try
{
await tokenSource.CancelAsync().ConfigureAwait(false);
return Result.Success;
}
catch (ObjectDisposedException)
{
return Result.Disposed;
}
catch (Exception ex)
{
return Result.FromException(ex);
}
}
#endif
}

View File

@@ -9,6 +9,7 @@
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation;
#pragma warning disable CA1851
public static class PackHelpers
{

View File

@@ -0,0 +1,109 @@
//------------------------------------------------------------------------------
// 此代码版权除特别声明或在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 System.Runtime.CompilerServices;
namespace ThingsGateway.Foundation;
/// <summary>
/// 具有释放的对象。内部实现了<see cref="GC.SuppressFinalize(object)"/>,但不包括析构函数相关。
/// </summary>
public abstract partial class AsyncAndSyncDisposableObject :
IDisposableObject,
IAsyncDisposable
{
/// <summary>
/// 判断当前对象是否已经被释放。
/// 如果已经被释放,则抛出<see cref="ObjectDisposedException"/>异常。
/// </summary>
/// <exception cref="ObjectDisposedException">当对象已经被释放时抛出此异常</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void ThrowIfDisposed()
{
// 检查对象是否已经被释放
if (this.m_disposedValue)
{
// 如果对象已被释放抛出ObjectDisposedException异常
throw new ObjectDisposedException($"The object instance with type {this.GetType().FullName} has been released");
}
}
private int m_count = 0;
private int m_asyncCount = 0;
/// <summary>
/// 判断是否已释放。
/// </summary>
private volatile bool m_disposedValue;
/// <inheritdoc/>
public bool DisposedValue => this.m_disposedValue;
/// <summary>
/// 处置资源
/// </summary>
/// <param name="disposing">一个值,表示是否释放托管资源</param>
protected virtual void Dispose(bool disposing)
{
// 标记当前对象为已处置状态
this.m_disposedValue = true;
}
/// <summary>
/// 释放资源。内部已经处理了<see cref="GC.SuppressFinalize(object)"/>
/// </summary>
public void Dispose()
{
if (this.DisposedValue)
{
return;
}
if (Interlocked.Increment(ref this.m_count) == 1)
{
this.Dispose(disposing: true);
}
GC.SuppressFinalize(this);
}
/// <summary>
/// 处置资源
/// </summary>
/// <param name="disposing">一个值,表示是否释放托管资源</param>
protected virtual Task DisposeAsync(bool disposing)
{
// 标记当前对象为已处置状态
this.m_disposedValue = true;
return Task.CompletedTask;
}
/// <summary>
/// 释放资源。内部已经处理了<see cref="GC.SuppressFinalize(object)"/>
/// </summary>
public async ValueTask DisposeAsync()
{
if (this.DisposedValue)
{
return;
}
//if (Interlocked.Increment(ref this.m_count) == 1)
//{
// this.Dispose(disposing: true);
//}
if (Interlocked.Increment(ref this.m_asyncCount) == 1)
{
await this.DisposeAsync(disposing: true).ConfigureAwait(false);
}
GC.SuppressFinalize(this);
}
}

View File

@@ -0,0 +1,108 @@
//------------------------------------------------------------------------------
// 此代码版权除特别声明或在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 System.Runtime.CompilerServices;
namespace ThingsGateway.Foundation;
/// <summary>
/// 具有释放的对象。内部实现了<see cref="GC.SuppressFinalize(object)"/>,但不包括析构函数相关。
/// </summary>
public abstract partial class AsyncDisposableObject :
//IDisposableObject,
IAsyncDisposable
{
/// <summary>
/// 判断当前对象是否已经被释放。
/// 如果已经被释放,则抛出<see cref="ObjectDisposedException"/>异常。
/// </summary>
/// <exception cref="ObjectDisposedException">当对象已经被释放时抛出此异常</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void ThrowIfDisposed()
{
// 检查对象是否已经被释放
if (this.m_disposedValue)
{
// 如果对象已被释放抛出ObjectDisposedException异常
throw new ObjectDisposedException($"The object instance with type {this.GetType().FullName} has been released");
}
}
private int m_asyncCount = 0;
/// <summary>
/// 判断是否已释放。
/// </summary>
private volatile bool m_disposedValue;
/// <inheritdoc/>
public bool DisposedValue => this.m_disposedValue;
///// <summary>
///// 处置资源
///// </summary>
///// <param name="disposing">一个值,表示是否释放托管资源</param>
//protected virtual void Dispose(bool disposing)
//{
// // 标记当前对象为已处置状态
// this.m_disposedValue = true;
//}
///// <summary>
///// 释放资源。内部已经处理了<see cref="GC.SuppressFinalize(object)"/>
///// </summary>
//public void Dispose()
//{
// if (this.DisposedValue)
// {
// return;
// }
// if (Interlocked.Increment(ref this.m_count) == 1)
// {
// this.Dispose(disposing: true);
// }
// GC.SuppressFinalize(this);
//}
/// <summary>
/// 处置资源
/// </summary>
/// <param name="disposing">一个值,表示是否释放托管资源</param>
protected virtual Task DisposeAsync(bool disposing)
{
// 标记当前对象为已处置状态
this.m_disposedValue = true;
return Task.CompletedTask;
}
/// <summary>
/// 释放资源。内部已经处理了<see cref="GC.SuppressFinalize(object)"/>
/// </summary>
public async ValueTask DisposeAsync()
{
if (this.DisposedValue)
{
return;
}
//if (Interlocked.Increment(ref this.m_count) == 1)
//{
// this.Dispose(disposing: true);
//}
if (Interlocked.Increment(ref this.m_asyncCount) == 1)
{
await this.DisposeAsync(disposing: true).ConfigureAwait(false);
}
GC.SuppressFinalize(this);
}
}

View File

@@ -54,7 +54,7 @@ public class AsyncReadWriteLock
{
var cancellationTokenSource = _cancellationTokenSource;
_cancellationTokenSource = new();
await cancellationTokenSource.CancelAsync().ConfigureAwait(false); // 取消读取
await cancellationTokenSource.SafeCancelAsync().ConfigureAwait(false); // 取消读取
cancellationTokenSource.SafeDispose();
}

View File

@@ -59,10 +59,10 @@ public abstract class BusinessBaseWithCacheAlarm : BusinessBaseWithCache
await base.InitChannelAsync(channel, cancellationToken).ConfigureAwait(false);
}
protected override void Dispose(bool disposing)
protected override Task DisposeAsync(bool disposing)
{
GlobalData.AlarmChangedEvent -= AlarmValueChange;
base.Dispose(disposing);
return base.DisposeAsync(disposing);
}
/// <summary>
/// 当报警值发生变化时触发此事件处理方法。该方法内部会检查是否需要进行报警上传,如果需要,则调用 <see cref="AlarmChange(AlarmVariable)"/> 方法。

View File

@@ -130,7 +130,7 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
/// <summary>
/// 释放资源方法
/// </summary>
protected override void Dispose(bool disposing)
protected override Task DisposeAsync(bool disposing)
{
// 解绑事件
GlobalData.AlarmChangedEvent -= AlarmValueChange;
@@ -142,7 +142,7 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
_memoryDevModelQueue.Clear();
_memoryVarModelQueue.Clear();
_memoryVarModelsQueue.Clear();
base.Dispose(disposing);
return base.DisposeAsync(disposing);
}
/// <summary>

View File

@@ -47,10 +47,11 @@ public abstract class CollectFoundationBase : CollectBase
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
protected override async Task DisposeAsync(bool disposing)
{
FoundationDevice?.Dispose();
base.Dispose(disposing);
if (FoundationDevice != null)
await FoundationDevice.SafeDisposeAsync().ConfigureAwait(false);
await base.DisposeAsync(disposing).ConfigureAwait(false);
}
/// <summary>
/// 开始通讯执行的方法

View File

@@ -26,11 +26,12 @@ namespace ThingsGateway.Gateway.Application;
/// <summary>
/// 插件基类
/// </summary>
public abstract class DriverBase : DisposableObject, IDriver
public abstract class DriverBase : AsyncDisposableObject, IDriver
{
/// <inheritdoc cref="DriverBase"/>
public DriverBase()
{
Localizer = App.CreateLocalizerByType(typeof(DriverBase))!;
}
@@ -312,38 +313,44 @@ public abstract class DriverBase : DisposableObject, IDriver
protected abstract List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken);
protected object stopLock = new();
protected WaitLock stopLock = new(nameof(DriverBase));
/// <summary>
/// 已停止任务,释放插件
/// </summary>
internal virtual void Stop()
internal virtual async Task StopAsync()
{
if (!DisposedValue)
{
lock (stopLock)
await stopLock.WaitAsync().ConfigureAwait(false);
try
{
if (!DisposedValue)
{
try
{
// 执行资源释放操作
Dispose();
}
catch (Exception ex)
{
// 记录 Dispose 方法执行失败的错误信息
LogMessage?.LogError(ex, "Dispose");
}
// 执行资源释放操作
await this.SafeDisposeAsync().ConfigureAwait(false);
// 记录设备线程已停止的信息
LogMessage?.LogInformation(string.Format(AppResource.DeviceTaskStop, DeviceName));
}
}
catch (Exception ex)
{
// 记录 Dispose 方法执行失败的错误信息
LogMessage?.LogError(ex, "Dispose");
}
finally
{
stopLock.Release();
}
}
}
protected override void Dispose(bool disposing)
protected override async Task DisposeAsync(bool disposing)
{
base.Dispose(disposing);
await base.DisposeAsync(disposing).ConfigureAwait(false);
if (TaskSchedulerLoop != null)
{
lock (TaskSchedulerLoop)

View File

@@ -14,7 +14,7 @@ using TouchSocket.Core;
namespace ThingsGateway.Gateway.Application
{
public interface IDriver : IDisposable
public interface IDriver : IAsyncDisposable
{
bool DisposedValue { get; }
ChannelRuntime CurrentChannel { get; }

View File

@@ -17,6 +17,7 @@ using TouchSocket.Core;
using TouchSocket.Sockets;
namespace ThingsGateway.Gateway.Application;
#pragma warning disable CS0649
/// <summary>
/// 通道表

View File

@@ -17,6 +17,7 @@ using System.ComponentModel.DataAnnotations;
using ThingsGateway.NewLife.Extension;
namespace ThingsGateway.Gateway.Application;
#pragma warning disable CS0649
/// <summary>
/// 设备表

View File

@@ -16,6 +16,7 @@ using System.Collections.Concurrent;
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Gateway.Application;
#pragma warning disable CS0649
/// <summary>
/// 设备变量表

View File

@@ -12,9 +12,10 @@ using Microsoft.Extensions.Logging;
using ThingsGateway.Common.Extension;
using ThingsGateway.Gateway.Application.Extensions;
using ThingsGateway.NewLife;
using ThingsGateway.NewLife.Extension;
using TouchSocket.Core;
namespace ThingsGateway.Gateway.Application;
/// <summary>
@@ -44,7 +45,7 @@ internal sealed class AlarmTask : IDisposable
public void Dispose()
{
StopTask();
scheduledTask?.TryDispose();
scheduledTask?.SafeDispose();
}
#region

View File

@@ -43,7 +43,7 @@ internal sealed class ChannelThreadManage : IChannelThreadManage
{
if (!DeviceThreadManages.TryRemove(channelId, out var deviceThreadManage)) return;
await deviceThreadManage.DisposeAsync().ConfigureAwait(false);
await deviceThreadManage.SafeDisposeAsync().ConfigureAwait(false);
}
catch (Exception ex)
{

View File

@@ -326,7 +326,7 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
{
try
{
await oldCts.CancelAsync().ConfigureAwait(false);
await oldCts.SafeCancelAsync().ConfigureAwait(false);
oldCts.SafeDispose();
}
catch
@@ -340,7 +340,7 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
CancellationTokenSources.TryAdd(driver.DeviceId, cts);
_ = Task.Factory.StartNew((state) => DriverStart(state, token), driver, token, TaskCreationOptions.None, TaskScheduler.Default);
}).ConfigureAwait(false);
}, App.HostApplicationLifetime.ApplicationStopping).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -393,45 +393,48 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
try
{
ConcurrentList<VariableRuntime> saveVariableRuntimes = new();
deviceIds.ParallelForEach((deviceId) =>
{
var now = DateTime.Now;
// 查找具有指定设备ID的驱动程序对象
if (Drivers.TryRemove(deviceId, out var driver))
{
driver.CurrentDevice.SetDeviceStatus(now, false, "Communication connection has been removed");
if (IsCollectChannel == true)
{
foreach (var a in driver.IdVariableRuntimes)
{
a.Value.SetValue(a.Value.Value, now, false);
a.Value.SetErrorMessage("Communication connection has been removed");
if (a.Value.SaveValue && !a.Value.DynamicVariable)
{
saveVariableRuntimes.Add(a.Value);
}
}
await deviceIds.ParallelForEachAsync(async (deviceId, cancellationToken) =>
{
var now = DateTime.Now;
// 查找具有指定设备ID的驱动程序对象
if (Drivers.TryRemove(deviceId, out var driver))
{
driver.CurrentDevice.SetDeviceStatus(now, false, "Communication connection has been removed");
if (IsCollectChannel == true)
{
foreach (var a in driver.IdVariableRuntimes)
{
a.Value.SetValue(a.Value.Value, now, false);
a.Value.SetErrorMessage("Communication connection has been removed");
if (a.Value.SaveValue && !a.Value.DynamicVariable)
{
saveVariableRuntimes.Add(a.Value);
}
}
}
}
}
}
// 取消驱动程序的操作
if (CancellationTokenSources.TryRemove(deviceId, out var token))
{
if (token != null)
{
driver.Stop();
token.Cancel();
token.Dispose();
}
}
// 取消驱动程序的操作
if (CancellationTokenSources.TryRemove(deviceId, out var token))
{
if (token != null)
{
await token.SafeCancelAsync().ConfigureAwait(false);
token.SafeDispose();
if (driver != null)
{
await driver.StopAsync().ConfigureAwait(false);
}
}
}
if (DriverTasks.TryRemove(deviceId, out var task))
{
task.Stop();
}
});
if (DriverTasks.TryRemove(deviceId, out var task))
{
task.Stop();
}
}).ConfigureAwait(false);
await Task.Delay(100).ConfigureAwait(false);
@@ -796,7 +799,7 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
Disposed = true;
try
{
await CancellationTokenSource.CancelAsync().ConfigureAwait(false);
await CancellationTokenSource.SafeCancelAsync().ConfigureAwait(false);
CancellationTokenSource.SafeDispose();
GlobalData.DeviceStatusChangeEvent -= GlobalData_DeviceStatusChangeEvent;
await NewDeviceLock.WaitAsync().ConfigureAwait(false);

View File

@@ -78,7 +78,7 @@ internal sealed class GatewayMonitorHostedService : BackgroundService, IGatewayM
public override async Task StopAsync(CancellationToken cancellationToken)
{
await ChannelThreadManage.DisposeAsync().ConfigureAwait(false);
await ChannelThreadManage.SafeDisposeAsync().ConfigureAwait(false);
await base.StopAsync(cancellationToken).ConfigureAwait(false);
}
}

View File

@@ -312,10 +312,10 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
{
await StopTaskAsync().ConfigureAwait(false);
TextLogger?.TryDispose();
scheduledTask?.TryDispose();
scheduledTask?.SafeDispose();
_tcpDmtpService?.TryDispose();
_tcpDmtpClient?.TryDispose();
_tcpDmtpService?.SafeDispose();
_tcpDmtpClient?.SafeDispose();
_tcpDmtpService = null;
_tcpDmtpClient = null;
}

View File

@@ -78,12 +78,12 @@ internal sealed class PluginService : IPluginService
public Type GetDebugUI(string pluginName)
{
using var driver = GetDriver(pluginName);
var driver = GetDriver(pluginName);
return driver?.DriverDebugUIType;
}
public Type GetAddressUI(string pluginName)
{
using var driver = GetDriver(pluginName);
var driver = GetDriver(pluginName);
return driver?.DriverVariableAddressUIType;
}
@@ -167,7 +167,6 @@ internal sealed class PluginService : IPluginService
{
string cacheKey = $"{nameof(PluginService)}_{nameof(GetDriverMethodInfos)}_{CultureInfo.CurrentUICulture.Name}";
// 如果未提供驱动基类对象,则尝试根据插件名称获取驱动对象
var dispose = driver == null; // 标记是否需要释放驱动对象
driver ??= GetDriver(pluginName); // 如果未提供驱动对象,则根据插件名称获取驱动对象
// 检查插件名称是否为空或null
@@ -183,10 +182,10 @@ internal sealed class PluginService : IPluginService
}
// 如果未从缓存中获取到指定插件的属性信息,则尝试从驱动基类对象中获取
return SetDriverMethodInfosCache(driver, pluginName, cacheKey, dispose); // 获取并设置属性信息缓存
return SetDriverMethodInfosCache(driver, pluginName, cacheKey); // 获取并设置属性信息缓存
// 用于设置驱动方法信息缓存的内部方法
List<DriverMethodInfo> SetDriverMethodInfosCache(IDriver driver, string pluginName, string cacheKey, bool dispose)
List<DriverMethodInfo> SetDriverMethodInfosCache(IDriver driver, string pluginName, string cacheKey)
{
// 获取驱动对象的方法信息,并筛选出带有 DynamicMethodAttribute 特性的方法
var dependencyPropertyWithInfos = driver.GetType().GetMethods()?.SelectMany(it =>
@@ -206,10 +205,6 @@ internal sealed class PluginService : IPluginService
var result = dependencyPropertyWithInfos.ToList();
App.CacheService.HashAdd(cacheKey, pluginName, result);
// 如果是通过方法内部创建的驱动对象,则在方法执行完成后释放该驱动对象
if (dispose)
driver.SafeDispose();
// 返回获取到的属性信息字典
return result;
}
@@ -228,7 +223,6 @@ internal sealed class PluginService : IPluginService
{
string cacheKey = $"{nameof(PluginService)}_{nameof(GetDriverPropertyTypes)}_{CultureInfo.CurrentUICulture.Name}";
var dispose = driver == null;
driver ??= GetDriver(pluginName); // 如果 driver 为 null 获取驱动实例
// 检查插件名称是否为空或空字符串
if (!pluginName.IsNullOrEmpty())
@@ -245,17 +239,15 @@ internal sealed class PluginService : IPluginService
}
// 如果缓存中不存在该插件的数据,则重新获取并缓存
return (SetCache(driver, pluginName, cacheKey, dispose), driver.DriverProperties, driver.DriverPropertyUIType); // 调用 SetCache 方法进行缓存并返回结果
return (SetCache(driver, pluginName, cacheKey), driver.DriverProperties, driver.DriverPropertyUIType); // 调用 SetCache 方法进行缓存并返回结果
// 定义 SetCache 方法,用于设置缓存并返回
IEnumerable<IEditorItem> SetCache(IDriver driver, string pluginName, string cacheKey, bool dispose)
IEnumerable<IEditorItem> SetCache(IDriver driver, string pluginName, string cacheKey)
{
var editorItems = PluginServiceUtil.GetEditorItems(driver.DriverProperties?.GetType()).ToList();
// 将结果存入缓存中,键为插件名称
App.CacheService.HashAdd(cacheKey, pluginName, editorItems);
// 如果 dispose 参数为 true则释放 driver 对象
if (dispose)
driver.SafeDispose();
return editorItems;
}
}
@@ -291,7 +283,6 @@ internal sealed class PluginService : IPluginService
{
string cacheKey = $"{nameof(PluginService)}_{nameof(GetVariablePropertyTypes)}_{CultureInfo.CurrentUICulture.Name}";
var dispose = businessBase == null;
businessBase ??= (BusinessBase)GetDriver(pluginName); // 如果 driver 为 null 获取驱动实例
var data = App.CacheService.HashGetAll<List<IEditorItem>>(cacheKey);
@@ -309,8 +300,6 @@ internal sealed class PluginService : IPluginService
// 将结果存入缓存中,键为插件名称
App.CacheService.HashAdd(cacheKey, pluginName, editorItems);
// 如果 dispose 参数为 true则释放 driver 对象
if (dispose)
businessBase.SafeDispose();
return editorItems;
}
}

View File

@@ -47,7 +47,7 @@ public class TimeIntervalTriggerNode : TextNode, ITriggerNode, IDisposable
public void Dispose()
{
_task?.Stop();
_task.TryDispose();
_task?.TryDispose();
GC.SuppressFinalize(this);
}

View File

@@ -468,7 +468,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
if (deviceDicts.TryGetValue(a.Key, out var device) && channelDicts.TryGetValue(device.ChannelId, out var channel))
{
var pluginKey = channel?.PluginName;
using var businessBase = (BusinessBase)GlobalData.PluginService.GetDriver(pluginKey);
var businessBase = (BusinessBase)GlobalData.PluginService.GetDriver(pluginKey);
return new KeyValuePair<string, VariablePropertyBase>(pluginKey, businessBase.VariablePropertys);
}
return new KeyValuePair<string, VariablePropertyBase>(string.Empty, null);
@@ -500,7 +500,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
if (deviceDicts.TryGetValue(a.Key, out var device) && channelDicts.TryGetValue(device.ChannelId, out var channel))
{
var pluginKey = channel?.PluginName;
using var businessBase = (BusinessBase)GlobalData.PluginService.GetDriver(pluginKey);
var businessBase = (BusinessBase)GlobalData.PluginService.GetDriver(pluginKey);
return new KeyValuePair<string, VariablePropertyBase>(pluginKey, businessBase.VariablePropertys);
}
return new KeyValuePair<string, VariablePropertyBase>(string.Empty, null);

View File

@@ -30,7 +30,7 @@ public static class VariableServiceHelpers
if (deviceDicts.TryGetValue(a.Key, out var device) && channelDicts.TryGetValue(device.ChannelId, out var channel))
{
var pluginKey = channel?.PluginName;
using var businessBase = (BusinessBase)GlobalData.PluginService.GetDriver(pluginKey);
var businessBase = (BusinessBase)GlobalData.PluginService.GetDriver(pluginKey);
return new KeyValuePair<string, VariablePropertyBase>(pluginKey, businessBase.VariablePropertys);
}
return new KeyValuePair<string, VariablePropertyBase>(string.Empty, null);

View File

@@ -9,6 +9,7 @@
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation.Dlt645;
#pragma warning disable CA1851
internal static class PackHelper
{

View File

@@ -11,6 +11,7 @@
using ThingsGateway.NewLife.Extension;
namespace ThingsGateway.Foundation.Modbus;
#pragma warning disable CA1851
/// <summary>
/// PackHelper

View File

@@ -155,7 +155,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
protected override Task DisposeAsync(bool disposing)
{
foreach (var item in ModbusServer01ByteBlocks)
{
@@ -177,7 +177,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress
ModbusServer02ByteBlocks.Clear();
ModbusServer03ByteBlocks.Clear();
ModbusServer04ByteBlocks.Clear();
base.Dispose(disposing);
return base.DisposeAsync(disposing);
}
/// <inheritdoc/>

View File

@@ -3,7 +3,7 @@
<Import Project="..\..\PackNuget.props" />
<Import Project="..\..\Version.props" />
<PropertyGroup>
<TargetFrameworks>net462;netstandard2.0;net6.0;</TargetFrameworks>
<TargetFrameworks>net462;netstandard2.0;net6.0;net8.0;</TargetFrameworks>
<Description>工业设备通讯协议-OpcDa协议</Description>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<DocumentationFile></DocumentationFile>

View File

@@ -28,7 +28,7 @@ public delegate void LogEventHandler(byte level, object sender, string message,
/// <summary>
/// OpcUaMaster
/// </summary>
public class OpcUaMaster : IDisposable
public class OpcUaMaster : IDisposable, IAsyncDisposable
{
#region
@@ -405,6 +405,18 @@ public class OpcUaMaster : IDisposable
}
}
/// <summary>
/// 断开连接。
/// </summary>
public async Task DisconnectAsync()
{
await PrivateDisconnectAsync().ConfigureAwait(false);
// disconnect any existing session.
if (m_session != null)
{
m_session = null;
}
}
/// <inheritdoc/>
public void Dispose()
{
@@ -412,7 +424,12 @@ public class OpcUaMaster : IDisposable
_variableDicts?.Clear();
_subscriptionDicts?.Clear();
}
public async ValueTask DisposeAsync()
{
await DisconnectAsync().ConfigureAwait(false);
_variableDicts?.Clear();
_subscriptionDicts?.Clear();
}
/// <summary>
/// 获取变量说明
/// </summary>
@@ -865,7 +882,7 @@ public class OpcUaMaster : IDisposable
{
return;
}
PrivateDisconnect();
await PrivateDisconnectAsync().ConfigureAwait(false);
if (LastServerUrl != serverUrl)
{
_variableDicts.Clear();
@@ -944,6 +961,29 @@ public class OpcUaMaster : IDisposable
DoConnectComplete(false);
}
}
private async Task PrivateDisconnectAsync()
{
bool state = m_session?.Connected == true;
if (m_reConnectHandler != null)
{
try { m_reConnectHandler.Dispose(); } catch { }
m_reConnectHandler = null;
}
if (m_session != null)
{
m_session.KeepAlive -= Session_KeepAlive;
await m_session.CloseAsync(10000).ConfigureAwait(false);
m_session.Dispose();
m_session = null;
}
if (state)
{
Log(2, null, "Disconnected");
DoConnectComplete(false);
}
}
#endregion
@@ -1376,5 +1416,7 @@ public class OpcUaMaster : IDisposable
}
}
#endregion
}

View File

@@ -9,6 +9,7 @@
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation.SiemensS7;
#pragma warning disable CA1851
internal static class PackHelper
{

View File

@@ -417,13 +417,15 @@ public partial class SiemensS7Master : DeviceBase
//本地TSAP
if (SiemensS7Type == SiemensTypeEnum.S200 || SiemensS7Type == SiemensTypeEnum.S200Smart)
{
ISO_CR[13] = BitConverter.GetBytes(LocalTSAP)[1];
ISO_CR[14] = BitConverter.GetBytes(LocalTSAP)[0];
var data = s7BitConverter.GetBytes(LocalTSAP);
ISO_CR[13] = data[0];
ISO_CR[14] = data[1];
}
else
{
ISO_CR[16] = BitConverter.GetBytes(LocalTSAP)[1];
ISO_CR[17] = BitConverter.GetBytes(LocalTSAP)[0];
var data = s7BitConverter.GetBytes(LocalTSAP);
ISO_CR[16] = data[0];
ISO_CR[17] = data[1];
}
}
if (Rack > 0 || Slot > 0)

View File

@@ -49,10 +49,10 @@ public partial class QuestDBProducer : BusinessBaseWithCacheIntervalVariable, ID
private SqlSugarClient _db;
protected override void Dispose(bool disposing)
protected override Task DisposeAsync(bool disposing)
{
_db?.TryDispose();
base.Dispose(disposing);
return base.DisposeAsync(disposing);
}
protected override async Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken)

View File

@@ -51,10 +51,10 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariable, IDBH
protected override BusinessPropertyWithCacheInterval _businessPropertyWithCacheInterval => _driverPropertys;
private SqlSugarClient _db;
protected override void Dispose(bool disposing)
protected override Task DisposeAsync(bool disposing)
{
_db?.TryDispose();
base.Dispose(disposing);
return base.DisposeAsync(disposing);
}
public async Task<SqlSugarPagedList<IDBHistoryValue>> GetDBHistoryValuePagesAsync(DBHistoryValuePageInput input)
{

View File

@@ -47,10 +47,10 @@ public partial class SqlHistoryAlarm : BusinessBaseWithCacheAlarm, IDBHistoryAla
/// <returns></returns>
public override bool IsConnected() => success;
protected override void Dispose(bool disposing)
protected override Task DisposeAsync(bool disposing)
{
_db?.TryDispose();
base.Dispose(disposing);
return base.DisposeAsync(disposing);
}
protected override Task ProtectedStartAsync(CancellationToken cancellationToken)

View File

@@ -53,10 +53,10 @@ public partial class TDengineDBProducer : BusinessBaseWithCacheIntervalVariable,
protected override BusinessPropertyWithCacheInterval _businessPropertyWithCacheInterval => _driverPropertys;
protected override void Dispose(bool disposing)
protected override Task DisposeAsync(bool disposing)
{
_db?.TryDispose();
base.Dispose(disposing);
return base.DisposeAsync(disposing);
}
protected override async Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken)

View File

@@ -10,8 +10,6 @@
using ThingsGateway.Gateway.Application;
using TouchSocket.Core;
namespace ThingsGateway.Plugin.Dlt645;
/// <summary>
@@ -52,7 +50,8 @@ public class Dlt645_2007Master : CollectFoundationBase
var plc = _plc;
_plc = new();
plc?.SafeDispose();
if (plc != null)
await plc.SafeDisposeAsync().ConfigureAwait(false);
//载入配置
_plc.DtuId = _driverPropertys.DtuId;

View File

@@ -73,7 +73,7 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScriptAll
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
protected override Task DisposeAsync(bool disposing)
{
try
{
@@ -83,7 +83,7 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScriptAll
{
}
_producer?.SafeDispose();
base.Dispose(disposing);
return base.DisposeAsync(disposing);
}
protected override Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken)

View File

@@ -11,8 +11,6 @@
using ThingsGateway.Debug;
using ThingsGateway.Gateway.Application;
using TouchSocket.Core;
namespace ThingsGateway.Plugin.Modbus;
/// <summary>
@@ -65,7 +63,8 @@ public class ModbusMaster : CollectFoundationBase
ArgumentNullException.ThrowIfNull(channel);
var plc = _plc;
_plc = new();
plc?.SafeDispose();
if (plc != null)
await plc.SafeDisposeAsync().ConfigureAwait(false);
//载入配置
_plc.DataFormat = _driverPropertys.DataFormat;
_plc.DtuId = _driverPropertys.DtuId;

View File

@@ -92,7 +92,8 @@ public class ModbusSlave : BusinessBase
ArgumentNullException.ThrowIfNull(channel);
var plc = _plc;
_plc = new();
plc?.SafeDispose();
if (plc != null)
await plc.SafeDisposeAsync().ConfigureAwait(false);
//载入配置
_plc.DataFormat = _driverPropertys.DataFormat;
_plc.IsStringReverseByteWord = _driverPropertys.IsStringReverseByteWord;
@@ -137,13 +138,14 @@ public class ModbusSlave : BusinessBase
);
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
protected override async Task DisposeAsync(bool disposing)
{
ModbusVariables?.Clear();
ModbusVariableQueue?.Clear();
GlobalData.VariableValueChangeEvent -= VariableValueChange;
_plc?.SafeDispose();
base.Dispose(disposing);
if (_plc != null)
await _plc.SafeDisposeAsync().ConfigureAwait(false);
await base.DisposeAsync(disposing).ConfigureAwait(false);
}
protected override async Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken)

View File

@@ -150,18 +150,17 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScriptAll
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
protected override async Task DisposeAsync(bool disposing)
{
base.Dispose(disposing);
_ = Task.Run(async () =>
await base.DisposeAsync(disposing).ConfigureAwait(false);
if (_mqttClient != null)
{
if (_mqttClient != null)
{
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
_mqttClient.SafeDispose();
}
_mqttClient = null;
});
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
_mqttClient.SafeDispose();
}
_mqttClient = null;
}
protected override async Task ProtectedStartAsync(CancellationToken cancellationToken)

View File

@@ -505,8 +505,8 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScriptAll
private async ValueTask<OperResult> TryMqttClientAsync(CancellationToken cancellationToken)
{
if(DisposedValue|| _mqttClient==null) return new OperResult("MqttClient is disposed");
if (DisposedValue || _mqttClient == null) return new OperResult("MqttClient is disposed");
if (_mqttClient?.IsConnected == true)
return OperResult.Success;
return await Client().ConfigureAwait(false);

View File

@@ -38,11 +38,16 @@ public partial class MqttCollect : CollectBase
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
protected override async Task DisposeAsync(bool disposing)
{
_mqttClient?.SafeDispose();
await base.DisposeAsync(disposing).ConfigureAwait(false);
if (_mqttClient != null)
{
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
_mqttClient.SafeDispose();
}
_mqttClient = null;
TopicItemDict?.Clear();
base.Dispose(disposing);
}
public override string GetAddressDescription()

View File

@@ -66,9 +66,9 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScriptAll
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
protected override async Task DisposeAsync(bool disposing)
{
base.Dispose(disposing);
await base.DisposeAsync(disposing).ConfigureAwait(false);
if (_mqttServer != null)
{
_mqttServer.ClientDisconnectedAsync -= MqttServer_ClientDisconnectedAsync;

View File

@@ -84,14 +84,14 @@ public class OpcDaMaster : CollectBase
/// <inheritdoc/>
/// <inheritdoc/>
protected override void Dispose(bool disposing)
protected override Task DisposeAsync(bool disposing)
{
if (_plc != null)
_plc.DataChangedHandler -= DataChangedHandler;
_plc?.SafeDispose();
VariableAddresDicts?.Clear();
base.Dispose(disposing);
return base.DisposeAsync(disposing);
}
public override string GetAddressDescription()

View File

@@ -70,7 +70,7 @@ public class OpcUaMaster : CollectBase
{
plc.DataChangedHandler -= DataChangedHandler;
plc.LogEvent -= _plc_LogEvent;
plc.SafeDispose();
await plc.SafeDisposeAsync().ConfigureAwait(false);
}
_plc.LogEvent += _plc_LogEvent;
@@ -88,19 +88,18 @@ public class OpcUaMaster : CollectBase
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
protected override async Task DisposeAsync(bool disposing)
{
if (_plc != null)
{
_plc.DataChangedHandler -= DataChangedHandler;
_plc.LogEvent -= _plc_LogEvent;
_plc.Disconnect();
_plc.SafeDispose();
await _plc.DisposeAsync().ConfigureAwait(false);
}
VariableAddresDicts?.Clear();
base.Dispose(disposing);
await base.DisposeAsync(disposing).ConfigureAwait(false);
}
public override string GetAddressDescription()
@@ -301,7 +300,8 @@ public class OpcUaMaster : CollectBase
{
try
{
_plc?.Disconnect();
if (_plc != null)
await _plc.DisconnectAsync().ConfigureAwait(false);
await base.AfterVariablesChangedAsync(cancellationToken).ConfigureAwait(false);
}
finally

View File

@@ -175,13 +175,13 @@ public partial class OpcUaServer : BusinessBase
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
protected override Task DisposeAsync(bool disposing)
{
GlobalData.VariableValueChangeEvent -= VariableValueChange;
UaDispose();
CollectVariableRuntimes?.Clear();
IdVariableRuntimes?.Clear();
base.Dispose(disposing);
return base.DisposeAsync(disposing);
}
protected override async Task ProtectedStartAsync(CancellationToken cancellationToken)

View File

@@ -119,7 +119,7 @@ public partial class OpcUaMaster : IDisposable
{
try
{
_plc.Disconnect();
await _plc.DisconnectAsync().ConfigureAwait(false);
LogPath = _plc?.GetHashCode().ToLong().GetDebugLogPath();
await GetOpc().ConnectAsync(CancellationToken.None);
}

View File

@@ -12,8 +12,6 @@ using RabbitMQ.Client;
using ThingsGateway.Foundation;
using TouchSocket.Core;
namespace ThingsGateway.Plugin.RabbitMQ;
/// <summary>
@@ -53,11 +51,13 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScriptAll
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
protected override async Task DisposeAsync(bool disposing)
{
_channel?.SafeDispose();
_connection?.SafeDispose();
base.Dispose(disposing);
if (_channel != null)
await _channel.SafeDisposeAsync().ConfigureAwait(false);
if (_connection != null)
await _connection.SafeDisposeAsync().ConfigureAwait(false);
await base.DisposeAsync(disposing).ConfigureAwait(false);
}
protected override async Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken)

View File

@@ -60,7 +60,8 @@ public class SiemensS7Master : CollectFoundationBase
var plc = _plc;
_plc = new();
plc?.SafeDispose();
if (plc != null)
await plc.SafeDisposeAsync().ConfigureAwait(false);
//载入配置
_plc.DataFormat = _driverPropertys.DataFormat;