Compare commits
	
		
			4 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 453817ef86 | ||
|   | 8ce0b981c1 | ||
|   | 4e5c51b54c | ||
|   | 3cc9d31f28 | 
| @@ -683,13 +683,20 @@ public class MachineInfo : IExtend | ||||
|             if (dic.TryGetValue("MemTotal", out var str) && !str.IsNullOrEmpty()) | ||||
|                 Memory = (UInt64)str.TrimEnd(" kB").ToLong(); | ||||
|  | ||||
|             ulong ma = 0; | ||||
|             if (dic.TryGetValue("MemAvailable", out str) && !str.IsNullOrEmpty()) | ||||
|                 AvailableMemory = (UInt64)str.TrimEnd(" kB").ToLong(); | ||||
|             else if (dic.TryGetValue("MemFree", out str) && !str.IsNullOrEmpty()) | ||||
|                 AvailableMemory = | ||||
|                     (UInt64)(str.TrimEnd(" kB").ToLong() + | ||||
|                     dic["Buffers"]?.TrimEnd(" kB").ToLong() ?? 0 + | ||||
|                     dic["Cached"]?.TrimEnd(" kB").ToLong() ?? 0); | ||||
|             { | ||||
|                 ma = (UInt64)(str.TrimEnd(" kB").ToLong()); | ||||
|             } | ||||
|  | ||||
|             //低于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); | ||||
|  | ||||
|             var free = mf + mc + bf; | ||||
|  | ||||
|             AvailableMemory = ma > free ? ma : free; | ||||
|         } | ||||
|  | ||||
|         // A2/A4温度获取,Buildroot,CPU温度和主板温度 | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| <Project> | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFrameworks>net462;netstandard2.0;net6.0;</TargetFrameworks> | ||||
| 		<TargetFrameworks>net462;netstandard2.0;net6.0;net8.0</TargetFrameworks> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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); | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -15,7 +15,7 @@ namespace ThingsGateway.Foundation; | ||||
| /// <summary> | ||||
| /// 协议设备接口 | ||||
| /// </summary> | ||||
| public interface IDevice : IDisposable, IDisposableObject | ||||
| public interface IDevice : IDisposable, IDisposableObject, IAsyncDisposable | ||||
| { | ||||
|     #region 属性 | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
| @@ -9,6 +9,7 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Foundation; | ||||
| #pragma warning disable CA1851 | ||||
|  | ||||
| public static class PackHelpers | ||||
| { | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
| @@ -54,7 +54,7 @@ public class AsyncReadWriteLock | ||||
|         { | ||||
|             var cancellationTokenSource = _cancellationTokenSource; | ||||
|             _cancellationTokenSource = new(); | ||||
|             await cancellationTokenSource.CancelAsync().ConfigureAwait(false); // 取消读取 | ||||
|             await cancellationTokenSource.SafeCancelAsync().ConfigureAwait(false); // 取消读取 | ||||
|             cancellationTokenSource.SafeDispose(); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -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)"/> 方法。 | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
|     /// 开始通讯执行的方法 | ||||
|   | ||||
| @@ -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))!; | ||||
|     } | ||||
|  | ||||
| @@ -39,8 +40,7 @@ public abstract class DriverBase : DisposableObject, IDriver | ||||
|     /// <summary> | ||||
|     /// 当前设备 | ||||
|     /// </summary> | ||||
|     public DeviceRuntime? CurrentDevice => WeakReferenceCurrentDevice?.TryGetTarget(out var target) == true ? target : null; | ||||
|     private WeakReference<DeviceRuntime> WeakReferenceCurrentDevice { get; set; } | ||||
|     public DeviceRuntime? CurrentDevice { get; private set; } | ||||
|     /// <summary> | ||||
|     /// 当前设备Id | ||||
|     /// </summary> | ||||
| @@ -208,7 +208,7 @@ public abstract class DriverBase : DisposableObject, IDriver | ||||
|     /// </summary> | ||||
|     internal void InitDevice(DeviceRuntime device) | ||||
|     { | ||||
|         WeakReferenceCurrentDevice = new WeakReference<DeviceRuntime>(device); | ||||
|         CurrentDevice = device; | ||||
|  | ||||
|         _logger = App.RootServices.GetService<Microsoft.Extensions.Logging.ILoggerFactory>().CreateLogger($"Driver[{CurrentDevice.Name}]"); | ||||
|  | ||||
| @@ -313,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() | ||||
|     { | ||||
|         if (!DisposedValue) | ||||
|         { | ||||
|             lock (stopLock) | ||||
|     internal virtual async Task StopAsync() | ||||
|     { | ||||
|         if (!DisposedValue) | ||||
|         { | ||||
|             await stopLock.WaitAsync().ConfigureAwait(false); | ||||
|             try | ||||
|             { | ||||
|  | ||||
|  | ||||
|                 if (!DisposedValue) | ||||
|                 { | ||||
|  | ||||
|                     // 执行资源释放操作 | ||||
|                         Dispose(); | ||||
|                     await this.SafeDisposeAsync().ConfigureAwait(false); | ||||
|  | ||||
|                     // 记录设备线程已停止的信息 | ||||
|                     LogMessage?.LogInformation(string.Format(AppResource.DeviceTaskStop, DeviceName)); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 // 记录 Dispose 方法执行失败的错误信息 | ||||
|                 LogMessage?.LogError(ex, "Dispose"); | ||||
|             } | ||||
|                     // 记录设备线程已停止的信息 | ||||
|                     LogMessage?.LogInformation(string.Format(AppResource.DeviceTaskStop, DeviceName)); | ||||
|                 } | ||||
|             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) | ||||
|   | ||||
| @@ -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; } | ||||
|   | ||||
| @@ -17,6 +17,7 @@ using TouchSocket.Core; | ||||
| using TouchSocket.Sockets; | ||||
|  | ||||
| namespace ThingsGateway.Gateway.Application; | ||||
| #pragma warning disable CS0649 | ||||
|  | ||||
| /// <summary> | ||||
| /// 通道表 | ||||
|   | ||||
| @@ -17,6 +17,7 @@ using System.ComponentModel.DataAnnotations; | ||||
| using ThingsGateway.NewLife.Extension; | ||||
|  | ||||
| namespace ThingsGateway.Gateway.Application; | ||||
| #pragma warning disable CS0649 | ||||
|  | ||||
| /// <summary> | ||||
| /// 设备表 | ||||
|   | ||||
| @@ -16,6 +16,7 @@ using System.Collections.Concurrent; | ||||
| using System.ComponentModel.DataAnnotations; | ||||
|  | ||||
| namespace ThingsGateway.Gateway.Application; | ||||
| #pragma warning disable CS0649 | ||||
|  | ||||
| /// <summary> | ||||
| /// 设备变量表 | ||||
|   | ||||
| @@ -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 核心实现 | ||||
|   | ||||
| @@ -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) | ||||
|               { | ||||
|   | ||||
| @@ -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,7 +393,7 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage | ||||
|         try | ||||
|         { | ||||
|             ConcurrentList<VariableRuntime> saveVariableRuntimes = new(); | ||||
|             deviceIds.ParallelForEach((deviceId) => | ||||
|             await deviceIds.ParallelForEachAsync(async (deviceId, cancellationToken) => | ||||
|              { | ||||
|                  var now = DateTime.Now; | ||||
|                  // 查找具有指定设备ID的驱动程序对象 | ||||
| @@ -421,9 +421,12 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage | ||||
|                  { | ||||
|                      if (token != null) | ||||
|                      { | ||||
|                         driver.Stop(); | ||||
|                         token.Cancel(); | ||||
|                         token.Dispose(); | ||||
|                          await token.SafeCancelAsync().ConfigureAwait(false); | ||||
|                          token.SafeDispose(); | ||||
|                          if (driver != null) | ||||
|                          { | ||||
|                              await driver.StopAsync().ConfigureAwait(false); | ||||
|                          } | ||||
|                      } | ||||
|                  } | ||||
|  | ||||
| @@ -431,7 +434,7 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage | ||||
|                  { | ||||
|                      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); | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|     } | ||||
|   | ||||
| @@ -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; | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -47,7 +47,7 @@ public class TimeIntervalTriggerNode : TextNode, ITriggerNode, IDisposable | ||||
|     public void Dispose() | ||||
|     { | ||||
|         _task?.Stop(); | ||||
|         _task.TryDispose(); | ||||
|         _task?.TryDispose(); | ||||
|  | ||||
|         GC.SuppressFinalize(this); | ||||
|     } | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Dlt645; | ||||
| #pragma warning disable CA1851 | ||||
|  | ||||
| internal static class PackHelper | ||||
| { | ||||
|   | ||||
| @@ -11,6 +11,7 @@ | ||||
| using ThingsGateway.NewLife.Extension; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Modbus; | ||||
| #pragma warning disable CA1851 | ||||
|  | ||||
| /// <summary> | ||||
| /// PackHelper | ||||
|   | ||||
| @@ -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/> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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 私有方法 | ||||
| } | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Foundation.SiemensS7; | ||||
| #pragma warning disable CA1851 | ||||
|  | ||||
| internal static class PackHelper | ||||
| { | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
|     { | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -150,17 +150,17 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScriptAll | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         _ = Task.Run(async () => | ||||
|     protected override async Task DisposeAsync(bool disposing) | ||||
|     { | ||||
|         await base.DisposeAsync(disposing).ConfigureAwait(false); | ||||
|  | ||||
|         if (_mqttClient != null) | ||||
|         { | ||||
|             await _mqttClient.DisconnectAsync().ConfigureAwait(false); | ||||
|             _mqttClient.SafeDispose(); | ||||
|         } | ||||
|         }); | ||||
|         base.Dispose(disposing); | ||||
|         _mqttClient = null; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     protected override async Task ProtectedStartAsync(CancellationToken cancellationToken) | ||||
|   | ||||
| @@ -505,6 +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 (_mqttClient?.IsConnected == true) | ||||
|             return OperResult.Success; | ||||
|         return await Client().ConfigureAwait(false); | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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); | ||||
|         } | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user