Compare commits

...

18 Commits

Author SHA1 Message Date
2248356998 qq.com
e785f6660c 10.10.5 2025-08-01 21:53:47 +08:00
Diego
831c611797 10.10.4 2025-08-01 17:30:37 +08:00
Diego
453817ef86 添加IAsyncDisposable 2025-08-01 16:36:27 +08:00
2248356998 qq.com
8ce0b981c1 no message 2025-08-01 12:55:01 +08:00
2248356998 qq.com
4e5c51b54c 2025-08-01 12:47:21 +08:00
2248356998 qq.com
3cc9d31f28 修改可用内存策略 2025-08-01 12:43:39 +08:00
2248356998 qq.com
10391f869b 支持相对路径创建sqlite 2025-07-31 23:41:58 +08:00
Diego
fba0723a6d 更新依赖 2025-07-31 18:39:46 +08:00
Diego
2db3f78f0c 添加 控制写操作与读操作的比率 的插件配置属性 2025-07-31 18:18:41 +08:00
Diego
badf61fe01 10.9.99 2025-07-31 16:25:47 +08:00
Diego
d74e0952dc 10.9.98 2025-07-31 15:57:33 +08:00
Diego
fb1699ce80 10.9.97 2025-07-31 14:06:47 +08:00
Diego
44adddbcd4 10.9.97 2025-07-31 13:56:49 +08:00
Diego
0eab889452 10.9.97 2025-07-31 13:56:34 +08:00
Diego
e14d39a459 添加 rpc写入 多写日志 2025-07-31 12:52:40 +08:00
2248356998 qq.com
7575264ede 添加变量初始化标记 2025-07-31 00:40:03 +08:00
2248356998 qq.com
3e1a077b96 添加变量初始化标记 2025-07-31 00:30:53 +08:00
2248356998 qq.com
a921cb8400 refactor: 连接时判断setup 2025-07-30 23:27:35 +08:00
100 changed files with 972 additions and 439 deletions

View File

@@ -13,7 +13,7 @@
<ItemGroup>
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.6" />
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
<PackageReference Include="BootstrapBlazor" Version="9.8.2" />
<PackageReference Include="BootstrapBlazor" Version="9.9.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -213,12 +213,18 @@ public static class AppServiceCollectionExtensions
// 缓存
if (cacheOptions.CacheType == CacheType.Memory)
{
services.AddSingleton<ICache, MemoryCache>(a => new()
services.AddSingleton<ICache>(a =>
{
Capacity = cacheOptions.MemoryCacheOptions.Capacity,
Expire = cacheOptions.MemoryCacheOptions.Expire,
Period = cacheOptions.MemoryCacheOptions.Period
});
Cache.Default = new MemoryCache()
{
Capacity = cacheOptions.MemoryCacheOptions.Capacity,
Expire = cacheOptions.MemoryCacheOptions.Expire,
Period = cacheOptions.MemoryCacheOptions.Period
};
return Cache.Default;
}
);
}
else if (cacheOptions.CacheType == CacheType.Redis)
{

View File

@@ -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温度获取BuildrootCPU温度和主板温度

View File

@@ -86,7 +86,7 @@ namespace ThingsGateway.SqlSugar
public void Add(string key, V value, int cacheDurationInSeconds)
{
Check.ThrowNotSupportedException("ReflectionInoCache.Add(string key, V value, int cacheDurationInSeconds)");
this.InstanceCache.Add<V>(key, value, cacheDurationInSeconds);
}
public void Remove(string key)
@@ -109,7 +109,8 @@ namespace ThingsGateway.SqlSugar
public V GetOrCreate(string cacheKey, Func<V> create)
{
return InstanceCache.GetOrAdd<V>(cacheKey, (a) => create());
return InstanceCache.GetOrAdd<V>(cacheKey, (a) =>
create());
}
}
public static class ReflectionInoHelper

View File

@@ -406,22 +406,24 @@ AND sql LIKE '%" + tableName + "%'");
public override bool CreateDatabase(string databaseName, string databaseDirectory = null)
{
var connString = this.Context.CurrentConnectionConfig.ConnectionString;
var path = Regex.Match(connString, @"[a-z,A-Z]\:\\.+\\").Value;
if (path.IsNullOrEmpty())
// 提取 Data Source=xxx不管是绝对还是相对路径
var match = Regex.Match(connString, @"(?i)Data\s+Source\s*=\s*(.+?)(;|$)");
if (match.Success)
{
path = Regex.Match(connString, @"\/.+\/").Value;
}
if (path.IsNullOrEmpty())
{
path = Regex.Match(connString, @"[a-z,A-Z]\:\\").Value;
}
if (!path.IsNullOrEmpty())
{
if (!FileHelper.IsExistDirectory(path))
var filePath = match.Groups[1].Value.Trim(); // => ./DB/data.sqlite
var folderPath = Path.GetDirectoryName(filePath); // => ./DB
if (!folderPath.IsNullOrEmpty())
{
FileHelper.CreateDirectory(path);
if (!FileHelper.IsExistDirectory(folderPath))
{
FileHelper.CreateDirectory(folderPath);
}
}
}
this.Context.Ado.Connection.Open();
this.Context.Ado.Connection.Close();
return true;

View File

@@ -31,11 +31,12 @@
<PackageReference Include="CsvHelper" Version="33.1.0" />
<PackageReference Include="TDengine.Connector" Version="3.1.7" />
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="23.9.1" />
<PackageReference Include="Oscar.Data.SqlClient" Version="4.2.20" />
<PackageReference Include="Oscar.Data.SqlClient" Version="4.2.21" />
<PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.0" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageReference Include="System.Formats.Asn1" Version="8.0.2" />
</ItemGroup>
</Project>

View File

@@ -1,9 +1,9 @@
<Project>
<PropertyGroup>
<PluginVersion>10.9.91</PluginVersion>
<ProPluginVersion>10.9.91</ProPluginVersion>
<DefaultVersion>10.9.92</DefaultVersion>
<PluginVersion>10.10.2</PluginVersion>
<ProPluginVersion>10.10.2</ProPluginVersion>
<DefaultVersion>10.10.5</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

@@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CS-Script" Version="4.10.0" />
<PackageReference Include="CS-Script" Version="4.10.1" />
</ItemGroup>
<ItemGroup>

View File

@@ -27,6 +27,7 @@ public class OtherChannel : SetupConfigObject, IClientChannel
public OtherChannel(IChannelOptions channelOptions)
{
ChannelOptions = channelOptions;
ResetSign();
}
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
@@ -39,7 +40,7 @@ public class OtherChannel : SetupConfigObject, IClientChannel
pool?.SafeDispose();
}
/// <inheritdoc/>
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
/// <inheritdoc/>
public IChannelOptions ChannelOptions { get; }
@@ -51,16 +52,16 @@ public class OtherChannel : SetupConfigObject, IClientChannel
public ConcurrentList<IDevice> Collects { get; } = new();
/// <inheritdoc/>
public ChannelEventHandler Started { get; set; } = new();
public ChannelEventHandler Started { get; } = new();
/// <inheritdoc/>
public ChannelEventHandler Starting { get; set; } = new();
public ChannelEventHandler Starting { get; } = new();
/// <inheritdoc/>
public ChannelEventHandler Stoped { get; set; } = new();
public ChannelEventHandler Stoped { get; } = new();
/// <inheritdoc/>
public ChannelEventHandler Stoping { get; set; } = new();
public ChannelEventHandler Stoping { get; } = new();
/// <summary>
/// 等待池
/// </summary>

View File

@@ -24,6 +24,7 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
public SerialPortChannel(IChannelOptions channelOptions)
{
ChannelOptions = channelOptions;
ResetSign();
}
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
@@ -36,7 +37,7 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
pool?.SafeDispose();
}
/// <inheritdoc/>
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
/// <inheritdoc/>
public IChannelOptions ChannelOptions { get; }
@@ -51,16 +52,16 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => ProtectedDataHandlingAdapter;
/// <inheritdoc/>
public ChannelEventHandler Started { get; set; } = new();
public ChannelEventHandler Started { get; } = new();
/// <inheritdoc/>
public ChannelEventHandler Starting { get; set; } = new();
public ChannelEventHandler Starting { get; } = new();
/// <inheritdoc/>
public ChannelEventHandler Stoped { get; set; } = new();
public ChannelEventHandler Stoped { get; } = new();
/// <inheritdoc/>
public ChannelEventHandler Stoping { get; set; } = new();
public ChannelEventHandler Stoping { get; } = new();
/// <summary>
/// 等待池
/// </summary>

View File

@@ -23,7 +23,7 @@ public class TcpClientChannel : TcpClient, IClientChannel
public TcpClientChannel(IChannelOptions channelOptions)
{
ChannelOptions = channelOptions;
ResetSign();
}
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)

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);
}
@@ -179,7 +180,7 @@ public class TcpServiceChannel<TClient> : TcpServiceChannelBase<TClient>, IChann
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
/// <inheritdoc/>
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
/// <inheritdoc/>
public IChannelOptions ChannelOptions { get; }
@@ -191,15 +192,15 @@ public class TcpServiceChannel<TClient> : TcpServiceChannelBase<TClient>, IChann
public bool Online => ServerState == ServerState.Running;
/// <inheritdoc/>
public ChannelEventHandler Started { get; set; } = new();
public ChannelEventHandler Started { get; } = new();
/// <inheritdoc/>
public ChannelEventHandler Starting { get; set; } = new();
public ChannelEventHandler Starting { get; } = new();
/// <inheritdoc/>
public ChannelEventHandler Stoped { get; set; } = new();
public ChannelEventHandler Stoped { get; } = new();
/// <inheritdoc/>
public ChannelEventHandler Stoping { get; set; } = new();
public ChannelEventHandler Stoping { get; } = new();
/// <inheritdoc/>
public Task<Result> CloseAsync(string msg, CancellationToken token)
@@ -227,8 +228,8 @@ public class TcpServiceChannel<TClient> : TcpServiceChannelBase<TClient>, IChann
data.ResetSign(MinSign, MaxSign);
return data;
}
public int MaxSign { get; set; }
public int MinSign { get; set; }
public int MaxSign { get; private set; } = 0;
public int MinSign { get; private set; } = ushort.MaxValue;
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
{
MinSign = minSign;

View File

@@ -32,7 +32,7 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
pool?.SafeDispose();
}
/// <inheritdoc/>
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
/// <inheritdoc/>
public IChannelOptions ChannelOptions { get; internal set; }
@@ -47,15 +47,15 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => DataHandlingAdapter;
/// <inheritdoc/>
public ChannelEventHandler Started { get; set; } = new();
public ChannelEventHandler Started { get; } = new();
/// <inheritdoc/>
public ChannelEventHandler Starting { get; set; } = new();
public ChannelEventHandler Starting { get; } = new();
/// <inheritdoc/>
public ChannelEventHandler Stoped { get; set; } = new();
public ChannelEventHandler Stoped { get; } = new();
/// <inheritdoc/>
public ChannelEventHandler Stoping { get; set; } = new();
public ChannelEventHandler Stoping { get; } = new();
/// <summary>
/// 等待池

View File

@@ -25,6 +25,7 @@ public class UdpSessionChannel : UdpSession, IClientChannel
public UdpSessionChannel(IChannelOptions channelOptions)
{
ChannelOptions = channelOptions;
ResetSign();
}
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
@@ -37,7 +38,7 @@ public class UdpSessionChannel : UdpSession, IClientChannel
}
/// <inheritdoc/>
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
/// <inheritdoc/>
public IChannelOptions ChannelOptions { get; }
@@ -55,15 +56,15 @@ public class UdpSessionChannel : UdpSession, IClientChannel
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => DataHandlingAdapter;
/// <inheritdoc/>
public ChannelEventHandler Started { get; set; } = new();
public ChannelEventHandler Started { get; } = new();
/// <inheritdoc/>
public ChannelEventHandler Starting { get; set; } = new();
public ChannelEventHandler Starting { get; } = new();
/// <inheritdoc/>
public ChannelEventHandler Stoped { get; set; } = new();
public ChannelEventHandler Stoped { get; } = new();
/// <inheritdoc/>
public ChannelEventHandler Stoping { get; set; } = new();
public ChannelEventHandler Stoping { get; } = new();
/// <summary>
/// 等待池
@@ -204,6 +205,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

@@ -58,7 +58,7 @@ public class DeviceSingleStreamDataHandleAdapter<TRequest> : TcpCustomDataHandli
protected override FilterResult Filter<TByteBlock>(ref TByteBlock byteBlock, bool beCached, ref TRequest request, ref int tempCapacity)
{
if (Logger?.LogLevel <= LogLevel.Trace)
Logger?.Trace($"{ToString()}- Receive:{(IsHexLog ? byteBlock.AsSegmentTake().ToHexString() : byteBlock.ToString(byteBlock.Position))}");
Logger?.Trace($"{ToString()}- Receive:{(IsHexLog ? byteBlock.AsSegmentTake().ToHexString(' ') : byteBlock.ToString(byteBlock.Position))}");
try
{
@@ -172,7 +172,7 @@ public class DeviceSingleStreamDataHandleAdapter<TRequest> : TcpCustomDataHandli
{
cancellationToken.ThrowIfCancellationRequested();
if (Logger?.LogLevel <= LogLevel.Trace)
Logger?.Trace($"{ToString()}- Send:{(IsHexLog ? memory.Span.ToHexString() : (memory.Span.ToString(Encoding.UTF8)))}");
Logger?.Trace($"{ToString()}- Send:{(IsHexLog ? memory.Span.ToHexString(' ') : (memory.Span.ToString(Encoding.UTF8)))}");
//发送
await GoSendAsync(memory, cancellationToken).ConfigureAwait(false);
@@ -191,7 +191,7 @@ public class DeviceSingleStreamDataHandleAdapter<TRequest> : TcpCustomDataHandli
{
sendMessage.Build(ref byteBlock);
if (Logger?.LogLevel <= LogLevel.Trace)
Logger?.Trace($"{ToString()}- Send:{(IsHexLog ? byteBlock.Span.ToHexString() : (byteBlock.Span.ToString(Encoding.UTF8)))}");
Logger?.Trace($"{ToString()}- Send:{(IsHexLog ? byteBlock.Span.ToHexString(' ') : (byteBlock.Span.ToString(Encoding.UTF8)))}");
//非并发主从协议
if (IsSingleThread)
{

View File

@@ -65,7 +65,7 @@ public class DeviceUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter where
byteBlock.Position = 0;
if (Logger?.LogLevel <= LogLevel.Trace)
Logger?.Trace($"{remoteEndPoint}- Receive:{(IsHexLog ? byteBlock.AsSegmentTake().ToHexString() : byteBlock.ToString(byteBlock.Position))}");
Logger?.Trace($"{remoteEndPoint}- Receive:{(IsHexLog ? byteBlock.AsSegmentTake().ToHexString(' ') : byteBlock.ToString(byteBlock.Position))}");
TRequest request = null;
if (IsSingleThread)
@@ -151,7 +151,7 @@ public class DeviceUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter where
cancellationToken.ThrowIfCancellationRequested();
if (Logger?.LogLevel <= LogLevel.Trace)
Logger?.Trace($"{ToString()}- Send:{(IsHexLog ? memory.Span.ToHexString() : (memory.Span.ToString(Encoding.UTF8)))}");
Logger?.Trace($"{ToString()}- Send:{(IsHexLog ? memory.Span.ToHexString(' ') : (memory.Span.ToString(Encoding.UTF8)))}");
//发送
await GoSendAsync(endPoint, memory, cancellationToken).ConfigureAwait(false);
}
@@ -169,7 +169,7 @@ public class DeviceUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter where
{
sendMessage.Build(ref byteBlock);
if (Logger?.LogLevel <= LogLevel.Trace)
Logger?.Trace($"{endPoint}- Send:{(IsHexLog ? byteBlock.Span.ToHexString() : (byteBlock.Span.ToString(Encoding.UTF8)))}");
Logger?.Trace($"{endPoint}- Send:{(IsHexLog ? byteBlock.Span.ToHexString(' ') : (byteBlock.Span.ToString(Encoding.UTF8)))}");
if (IsSingleThread)
{

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; }
@@ -394,6 +394,8 @@ public abstract class DeviceBase : DisposableObject, IDevice
await connectWaitLock.WaitAsync(token).ConfigureAwait(false);
if (AutoConnect && Channel != null && Channel?.Online != true)
{
if (Channel.PluginManager == null)
await Channel.SetupAsync(Channel.Config.Clone()).ConfigureAwait(false);
await Channel.CloseAsync().ConfigureAwait(false);
await Task.Delay(500, token).ConfigureAwait(false);
await Channel.ConnectAsync(Channel.ChannelOptions.ConnectTimeout, token).ConfigureAwait(false);
@@ -444,7 +446,13 @@ public abstract class DeviceBase : DisposableObject, IDevice
public virtual OperResult<IClientChannel> GetChannel(string socketId)
{
if (string.IsNullOrWhiteSpace(socketId))
return new OperResult<IClientChannel>() { Content = (IClientChannel)Channel };
{
if (Channel is IClientChannel clientChannel)
return new OperResult<IClientChannel>() { Content = clientChannel };
else
return new OperResult<IClientChannel>("The communication link cannot be obtained, DtuId must be set!");
}
if (Channel is ITcpServiceChannel serviceChannel)
{
@@ -462,7 +470,12 @@ public abstract class DeviceBase : DisposableObject, IDevice
}
}
else
return new OperResult<IClientChannel>() { Content = (IClientChannel)Channel };
{
if (Channel is IClientChannel clientChannel)
return new OperResult<IClientChannel>() { Content = clientChannel };
else
return new OperResult<IClientChannel>("The communication link cannot be obtained!");
}
}
/// <inheritdoc/>
@@ -562,12 +575,18 @@ public abstract class DeviceBase : DisposableObject, IDevice
var sendOperResult = await SendAsync(command, clientChannel, endPoint, cancellationToken).ConfigureAwait(false);
if (!sendOperResult.IsSuccess)
throw sendOperResult.Exception ?? new(sendOperResult.ErrorMessage ?? "unknown error");
return new MessageBase(sendOperResult);
waitData.SetCancellationToken(cancellationToken);
await waitData.WaitAsync(timeout).ConfigureAwait(false);
try
{
waitData.SetCancellationToken(Channel.ClosedToken);
await waitData.WaitAsync(timeout).ConfigureAwait(false);
}
catch (Exception ex)
{
return new MessageBase(ex);
}
var result = waitData.Check();
if (result.IsSuccess)
{
@@ -575,25 +594,11 @@ public abstract class DeviceBase : DisposableObject, IDevice
}
else
{
if (cancellationToken.IsCancellationRequested)
{
if (!this.DisposedValue)
{
await Task.Delay(timeout, CancellationToken.None).ConfigureAwait(false);
}
}
return new MessageBase(result);
}
}
catch (Exception ex)
{
if (cancellationToken.IsCancellationRequested)
{
if (!this.DisposedValue)
{
await Task.Delay(timeout, CancellationToken.None).ConfigureAwait(false);
}
}
return new MessageBase(ex);
}
finally
@@ -1029,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)
{
@@ -1046,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

@@ -252,7 +252,7 @@ public static class ByteExtensions
/// 字节数组默认转16进制字符
/// </summary>
/// <returns></returns>
public static string ToHexString(this ArraySegment<byte> buffer, char splite = ' ', int newLineCount = 0)
public static string ToHexString(this ArraySegment<byte> buffer, char splite = default, int newLineCount = 0)
{
return DataTransUtil.ByteToHexString(buffer, splite, newLineCount);
}
@@ -261,7 +261,7 @@ public static class ByteExtensions
/// 字节数组默认转16进制字符
/// </summary>
/// <returns></returns>
public static string ToHexString(this byte[] buffer, char splite = ' ', int newLineCount = 0)
public static string ToHexString(this byte[] buffer, char splite = default, int newLineCount = 0)
{
return DataTransUtil.ByteToHexString(buffer, splite, newLineCount);
}
@@ -269,7 +269,7 @@ public static class ByteExtensions
/// 字节数组默认转16进制字符
/// </summary>
/// <returns></returns>
public static string ToHexString(this Span<byte> buffer, char splite = ' ', int newLineCount = 0)
public static string ToHexString(this Span<byte> buffer, char splite = default, int newLineCount = 0)
{
return DataTransUtil.ByteToHexString(buffer, splite, newLineCount);
}
@@ -277,7 +277,7 @@ public static class ByteExtensions
/// 字节数组默认转16进制字符
/// </summary>
/// <returns></returns>
public static string ToHexString(this ReadOnlySpan<byte> buffer, char splite = ' ', int newLineCount = 0)
public static string ToHexString(this ReadOnlySpan<byte> buffer, char splite = default, int newLineCount = 0)
{
return DataTransUtil.ByteToHexString(buffer, splite, newLineCount);
}

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

@@ -14,48 +14,88 @@ namespace ThingsGateway.Gateway.Application;
public class AsyncReadWriteLock
{
private readonly int _writeReadRatio = 3; // 写3次会允许1次读但写入也不会被阻止具体协议取决于插件协议实现
public AsyncReadWriteLock(int writeReadRatio)
{
_writeReadRatio = writeReadRatio;
}
private AsyncAutoResetEvent _readerLock = new AsyncAutoResetEvent(false); // 控制读计数
private long _writerCount = 0; // 当前活跃的写线程数
private long _readerCount = 0; // 当前被阻塞的读线程数
private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
/// <summary>
/// 获取读锁,支持多个线程并发读取,但写入时会阻止所有读取。
/// </summary>
public async Task<CancellationToken> ReaderLockAsync(CancellationToken cancellationToken)
{
if (Interlocked.Read(ref _writerCount) > 0)
{
Interlocked.Increment(ref _readerCount);
// 第一个读者需要获取写入锁,防止写操作
await _readerLock.WaitOneAsync(cancellationToken).ConfigureAwait(false);
Interlocked.Decrement(ref _readerCount);
}
return _cancellationTokenSource.Token;
}
public bool WriteWaited => _writerCount > 0;
/// <summary>
/// 获取写锁,阻止所有读取。
/// </summary>
public IDisposable WriterLock()
public async Task<IDisposable> WriterLockAsync(CancellationToken cancellationToken)
{
if (Interlocked.Increment(ref _writerCount) == 1)
{
var cancellationTokenSource = _cancellationTokenSource;
_cancellationTokenSource = new();
cancellationTokenSource.Cancel();//取消读取
await cancellationTokenSource.SafeCancelAsync().ConfigureAwait(false); // 取消读取
cancellationTokenSource.SafeDispose();
}
return new Writer(this);
}
private void ReleaseWriter()
{
if (Interlocked.Decrement(ref _writerCount) == 0)
var writerCount = Interlocked.Decrement(ref _writerCount);
if (writerCount == 0)
{
var resetEvent = _readerLock;
_readerLock = new(false);
Interlocked.Exchange(ref _writeSinceLastReadCount, 0);
resetEvent.SetAll();
}
else
{
// 读写占空比, 用于控制写操作与读操作的比率。该比率 n 次写入操作会执行一次读取操作。即使在应用程序执行大量的连续写入操作时,也必须确保足够的读取数据处理时间。相对于更加均衡的读写数据流而言,该特点使得外部写入可连续无顾忌操作
if (_writeReadRatio > 0)
{
if (Interlocked.Read(ref _readerCount) > 0)
{
var count = Interlocked.Increment(ref _writeSinceLastReadCount);
if (count >= _writeReadRatio)
{
Interlocked.Exchange(ref _writeSinceLastReadCount, 0);
_readerLock.Set();
}
}
}
else
{
_readerLock.Set();
}
}
}
private int _writeSinceLastReadCount = 0;
private struct Writer : IDisposable
{
private readonly AsyncReadWriteLock _lock;

View File

@@ -16,20 +16,20 @@ namespace ThingsGateway.Gateway.Application;
[ThingsGateway.DependencyInjection.SuppressSniffer]
public static class ThingsGatewayCacheConst
{
/// <summary>
/// 通道
/// </summary>
public const string Cache_Channel = $"{Cache_Prefix}Cache_Channel:List";
///// <summary>
///// 通道
///// </summary>
//public const string Cache_Channel = $"{Cache_Prefix}Cache_Channel:List";
/// <summary>
/// device
/// </summary>
public const string Cache_Device = $"{Cache_Prefix}Cache_Device:List";
///// <summary>
///// device
///// </summary>
//public const string Cache_Device = $"{Cache_Prefix}Cache_Device:List";
/// <summary>
/// variable
/// </summary>
public const string Cache_Variable = $"{Cache_Prefix}Cache_Variable:IdNameList";
///// <summary>
///// variable
///// </summary>
//public const string Cache_Variable = $"{Cache_Prefix}Cache_Variable:IdNameList";
/// <summary>
/// 前缀

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

@@ -94,7 +94,7 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
IdVariableRuntimes.ForEach(a =>
{
if (((!_businessPropertyWithCacheInterval.OnlineFilter) || a.Value.IsOnline) && _businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval)
VariableValueChange(a.Value, a.Value.AdaptVariableBasicData());
VariableValueInit(a.Value);
});
}
}
@@ -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>
@@ -269,7 +269,7 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
IdVariableRuntimes.ForEach(a =>
{
if (((!_businessPropertyWithCacheInterval.OnlineFilter) || a.Value.IsOnline) && _businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval)
VariableValueChange(a.Value, a.Value.AdaptVariableBasicData());
VariableValueInit(a.Value);
});
}
}
@@ -316,4 +316,29 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
VariableChange(variableRuntime, variable);
}
}
/// <summary>
/// 当初始化时触发此事件处理方法。该方法内部会检查是否需要进行变量上传,如果需要,则调用 <see cref="VariableChange(VariableRuntime, VariableBasicData)"/> 方法。
/// </summary>
/// <param name="variableRuntime">变量运行时信息</param>
protected void VariableValueInit(VariableRuntime variableRuntime)
{
if (CurrentDevice?.Pause != false)
return;
if (!VarModelEnable) return;
if (TaskSchedulerLoop?.Stoped == true) return;
// 如果业务属性的缓存为间隔上传,则不执行后续操作
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
{
// 检查当前设备的变量是否包含此变量,如果包含,则触发变量的变化处理方法
if (IdVariableRuntimes.ContainsKey(variableRuntime.Id))
{
var data = variableRuntime.AdaptVariableBasicData();
data.ValueInited = false;
VariableChange(variableRuntime, data);
}
}
}
}

View File

@@ -194,6 +194,8 @@ public abstract class CollectBase : DriverBase, IRpcDriver
// 从插件服务中获取当前设备关联的驱动方法信息列表
DriverMethodInfos = GlobalData.PluginService.GetDriverMethodInfos(device.PluginName, this);
ReadWriteLock = new(CollectProperties.DutyCycle);
}
public virtual string GetAddressDescription()
@@ -474,7 +476,7 @@ public abstract class CollectBase : DriverBase, IRpcDriver
/// <returns></returns>
protected abstract Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables);
protected AsyncReadWriteLock ReadWriteLock = new();
protected AsyncReadWriteLock ReadWriteLock;
/// <summary>
/// 采集驱动读取,读取成功后直接赋值变量
@@ -565,7 +567,8 @@ public abstract class CollectBase : DriverBase, IRpcDriver
ConcurrentDictionary<string, OperResult<object>> operResults = new();
using var writeLock = ReadWriteLock.WriterLock();
using var writeLock = await ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false);
var list = writeInfoLists
.Where(a => !results.Any(b => b.Key == a.Key.Name))
.ToDictionary(item => item.Key, item => item.Value).ToArray();

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>
/// 开始通讯执行的方法
@@ -175,7 +176,7 @@ public abstract class CollectFoundationBase : CollectBase
/// <returns></returns>
protected override async ValueTask<Dictionary<string, OperResult>> WriteValuesAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
{
using var writeLock = ReadWriteLock.WriterLock();
using var writeLock = await ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false);
// 检查协议是否为空,如果为空则抛出异常
if (FoundationDevice == null)
throw new NotSupportedException();
@@ -188,8 +189,6 @@ public abstract class CollectFoundationBase : CollectBase
{
try
{
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug)
LogMessage?.Debug(string.Format("{0} - Writing [{1} - {2} - {3}]", DeviceName, writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType));
// 调用协议的写入方法,将写入信息中的数据写入到对应的寄存器地址,并获取操作结果
var result = await FoundationDevice.WriteJTokenAsync(writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType, cancellationToken).ConfigureAwait(false);

View File

@@ -31,6 +31,11 @@ public abstract class CollectPropertyBase : DriverPropertyBase
/// 失败重试次数默认3
/// </summary>
public virtual int RetryCount { get; set; } = 3;
/// <summary>
/// 读写占空比
/// </summary>
public virtual int DutyCycle { get; set; } = 3;
}
/// <summary>
@@ -45,4 +50,8 @@ public abstract class CollectPropertyRetryBase : CollectPropertyBase
/// </summary>
[DynamicProperty]
public override int RetryCount { get; set; } = 3;
[DynamicProperty(Remark = "n 次写入操作会执行一次读取")]
public override int DutyCycle { get; set; } = 3;
}

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))!;
}
@@ -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,45 @@ 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");
}
// 记录设备线程已停止的信息
LogMessage?.LogInformation(string.Format(AppResource.DeviceTaskStop, DeviceName));
// 执行资源释放操作
await this.SafeDisposeAsync().ConfigureAwait(false);
}
}
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

@@ -27,7 +27,7 @@ public class LogJob : IJob
await DeleteRpcLog(rpcLogDaysdaysAgo, stoppingToken).ConfigureAwait(false);
await DeleteBackendLog(backendLogdaysAgo, stoppingToken).ConfigureAwait(false);
await DeleteTextLog(stoppingToken).ConfigureAwait(false);
await DeleteLocalDB(stoppingToken).ConfigureAwait(false);
DeleteLocalDB(stoppingToken);
}
private static async Task DeleteRpcLog(int daysAgo, CancellationToken stoppingToken)
@@ -49,8 +49,8 @@ public class LogJob : IJob
//网关通道日志以通道id命名
var channelService = App.RootServices.GetService<IChannelService>();
var deviceService = App.RootServices.GetService<IDeviceService>();
var channelNames = (await channelService.GetAllAsync().ConfigureAwait(false)).Select(a => a.Name.ToString()).ToHashSet();
var deviceNames = (await deviceService.GetAllAsync().ConfigureAwait(false)).Select(a => a.Name.ToString()).ToHashSet();
var channelNames = (GlobalData.Channels.Keys).ToHashSet();
var deviceNames = (GlobalData.Devices.Keys).ToHashSet();
var channelBaseDir = LoggerExtensions.GetChannelLogBasePath();
Directory.CreateDirectory(channelBaseDir);
var deviceBaseDir = LoggerExtensions.GetDeviceLogBasePath();
@@ -112,10 +112,9 @@ public class LogJob : IJob
}
}
public async Task DeleteLocalDB(CancellationToken stoppingToken)
public void DeleteLocalDB(CancellationToken stoppingToken)
{
var deviceService = App.RootServices.GetService<IDeviceService>();
var data = (await deviceService.GetAllAsync().ConfigureAwait(false)).Select(a => a.Name).ToHashSet();
var data = (GlobalData.Devices.Keys).ToHashSet();
var dir = CacheDBUtil.GetCacheFileBasePath();
string[] dirs = Directory.GetDirectories(dir);
foreach (var item in dirs)

View File

@@ -295,7 +295,8 @@
"RetryCount": "RetryCount"
},
"ThingsGateway.Gateway.Application.CollectPropertyRetryBase": {
"RetryCount": "RetryCount"
"RetryCount": "RetryCount",
"DutyCycle": "DutyCycle"
},
"ThingsGateway.Gateway.Application.ControlController": {
"BatchSaveChannelAsync": "BatchSaveChannel",

View File

@@ -294,7 +294,8 @@
"RetryCount": "失败重试次数"
},
"ThingsGateway.Gateway.Application.CollectPropertyRetryBase": {
"RetryCount": "失败重试次数"
"RetryCount": "失败重试次数",
"DutyCycle": "占空比"
},
"ThingsGateway.Gateway.Application.ControlController": {
"BatchSaveChannelAsync": "保存通道",

View File

@@ -188,4 +188,7 @@ public class VariableBasicData
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)]
public long CreateOrgId { get; set; }
/// <inheritdoc cref="VariableRuntime.ValueInited"/>
public bool ValueInited { get; set; }
}

View File

@@ -36,6 +36,7 @@ public partial class VariableRuntime : Variable, IVariable, IDisposable
private bool _isOnline;
private bool _isOnlineChanged;
private bool _valueInited;
private string alarmLimit;
private string alarmText;
@@ -165,6 +166,8 @@ public partial class VariableRuntime : Variable, IVariable, IDisposable
LastSetValue = _value;
ValueInited = true;
if (_isOnline == true)
{
_value = data;

View File

@@ -20,6 +20,8 @@ namespace ThingsGateway.Gateway.Application;
public partial class VariableRuntime : Variable, IVariable, IDisposable
{
[AutoGenerateColumn(Visible = false)]
public bool ValueInited { get => _valueInited; set => _valueInited = value; }
#region
/// <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

@@ -259,12 +259,12 @@ public class ChannelRuntimeService : IChannelRuntimeService
try
{
await WaitLock.WaitAsync().ConfigureAwait(false);
var channelIds = GlobalData.IdChannels.Keys.ToList();
//网关启动时,获取所有通道
var newChannelRuntimes = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).Where(a => ids.Contains(a.Id) || !GlobalData.IdChannels.ContainsKey(a.Id)).AdaptListChannelRuntime();
var newChannelRuntimes = (await GlobalData.ChannelService.GetFromDBAsync((a => ids.Contains(a.Id) || !channelIds.Contains(a.Id))).ConfigureAwait(false)).AdaptListChannelRuntime();
var chanelIds = newChannelRuntimes.Select(a => a.Id).ToHashSet();
var newDeviceRuntimes = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).Where(a => chanelIds.Contains(a.ChannelId)).AdaptListDeviceRuntime();
var newDeviceRuntimes = (await GlobalData.DeviceService.GetFromDBAsync((a => chanelIds.Contains(a.ChannelId))).ConfigureAwait(false)).AdaptListDeviceRuntime();
await RuntimeServiceHelper.InitAsync(newChannelRuntimes, newDeviceRuntimes, _logger).ConfigureAwait(false);

View File

@@ -16,6 +16,7 @@ using MiniExcelLibs;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
@@ -225,23 +226,20 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
/// <inheritdoc />
public void DeleteChannelFromCache()
{
App.CacheService.Remove(ThingsGatewayCacheConst.Cache_Channel);//删除通道缓存
//App.CacheService.Remove(ThingsGatewayCacheConst.Cache_Channel);//删除通道缓存
}
/// <summary>
/// 从缓存/数据库获取全部信息
/// </summary>
/// <returns>列表</returns>
public async Task<List<Channel>> GetAllAsync(SqlSugarClient db = null)
public async Task<List<Channel>> GetFromDBAsync(Expression<Func<Channel, bool>> expression = null, SqlSugarClient db = null)
{
var key = ThingsGatewayCacheConst.Cache_Channel;
var channels = App.CacheService.Get<List<Channel>>(key);
if (channels == null)
{
db ??= GetDB();
channels = await db.Queryable<Channel>().OrderBy(a => a.Id).ToListAsync().ConfigureAwait(false);
App.CacheService.Set(key, channels);
}
db ??= GetDB();
var channels = await db.Queryable<Channel>().WhereIF(expression != null, expression).OrderBy(a => a.Id).ToListAsync().ConfigureAwait(false);
return channels;
}
@@ -263,7 +261,7 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
if (exportFilter.PluginType != null)
{
var pluginInfo = GlobalData.PluginService.GetList(exportFilter.PluginType).Select(a => a.FullName).ToHashSet();
channel = (await GetAllAsync().ConfigureAwait(false)).Where(a => pluginInfo.Contains(a.PluginName)).Select(a => a.Id).ToHashSet();
channel = GlobalData.IdChannels.Where(a => pluginInfo.Contains(a.Value.PluginName)).Select(a => a.Value.Id).ToHashSet();
}
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
var whereQuery = (ISugarQueryable<Channel> a) => a
@@ -285,7 +283,7 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
[OperDesc("SaveChannel", localizerType: typeof(Channel))]
public async Task<bool> SaveChannelAsync(Channel input, ItemChangedType type)
{
if ((await GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Name).TryGetValue(input.Name, out var channel))
if (GlobalData.Channels.TryGetValue(input.Name, out var channel))
{
if (channel.Id != input.Id)
{
@@ -409,7 +407,7 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
{
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
var sheetNames = MiniExcel.GetSheetNames(path);
var channelDicts = (await GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Name);
var channelDicts = GlobalData.Channels;
//导入检验结果
Dictionary<string, ImportPreviewOutputBase> ImportPreviews = new();
//设备页
@@ -429,7 +427,7 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
}
}
public void SetChannelData(HashSet<long>? dataScope, Dictionary<string, Channel> channelDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, string sheetName, IEnumerable<IDictionary<string, object>> rows)
public void SetChannelData(HashSet<long>? dataScope, IReadOnlyDictionary<string, ChannelRuntime> channelDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, string sheetName, IEnumerable<IDictionary<string, object>> rows)
{
#region sheet

View File

@@ -127,7 +127,7 @@ public static class ChannelServiceHelpers
public static async Task<Dictionary<string, ImportPreviewOutputBase>> ImportAsync(USheetDatas uSheetDatas)
{
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
var channelDicts = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Name);
var channelDicts = GlobalData.Channels;
//导入检验结果
Dictionary<string, ImportPreviewOutputBase> ImportPreviews = new();
//设备页

View File

@@ -12,6 +12,8 @@ using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components.Forms;
using System.Linq.Expressions;
namespace ThingsGateway.Gateway.Application;
/// <summary>
@@ -57,7 +59,7 @@ internal interface IChannelService
/// 从缓存/数据库获取全部信息
/// </summary>
/// <returns>通道列表</returns>
Task<List<Channel>> GetAllAsync(SqlSugarClient db = null);
Task<List<Channel>> GetFromDBAsync(Expression<Func<Channel, bool>> expression = null, SqlSugarClient db = null);
/// <summary>
/// 导入通道数据
@@ -92,7 +94,7 @@ internal interface IChannelService
/// <param name="type">保存类型</param>
Task<bool> BatchSaveAsync(List<Channel> input, ItemChangedType type);
void SetChannelData(HashSet<long>? dataScope, Dictionary<string, Channel> channelDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, string sheetName, IEnumerable<IDictionary<string, object>> rows);
void SetChannelData(HashSet<long>? dataScope, IReadOnlyDictionary<string, ChannelRuntime> channelDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, string sheetName, IEnumerable<IDictionary<string, object>> rows);
/// <summary>
/// 保存是否输出日志和日志等级

View File

@@ -16,6 +16,7 @@ using MiniExcelLibs;
using System.Collections.Concurrent;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
@@ -129,11 +130,11 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
//事务
var result = await db.UseTranAsync(async () =>
{
var data = (await GetAllAsync(db).ConfigureAwait(false))
.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
.WhereIf(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
.Where(a => IdhashSet.Contains(a.ChannelId))
.Select(a => a.Id).ToList();
var data = GlobalData.IdDevices
.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
.WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId)
.Where(a => IdhashSet.Contains(a.Value.ChannelId))
.Select(a => a.Value.Id).ToList();
await db.Deleteable<Device>(a => data.Contains(a.Id)).ExecuteCommandAsync().ConfigureAwait(false);
await variableService.DeleteByDeviceIdAsync(data, db).ConfigureAwait(false);
}).ConfigureAwait(false);
@@ -180,30 +181,19 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
/// <inheritdoc />
public void DeleteDeviceFromCache()
{
App.CacheService.Remove(ThingsGatewayCacheConst.Cache_Device);//删除设备缓存
//App.CacheService.Remove(ThingsGatewayCacheConst.Cache_Device);//删除设备缓存
}
/// <summary>
/// 从缓存/数据库获取全部信息
/// </summary>
/// <returns>列表</returns>
public async Task<List<Device>> GetAllAsync(SqlSugarClient db = null)
public async Task<List<Device>> GetFromDBAsync(Expression<Func<Device, bool>> expression = null, SqlSugarClient db = null)
{
var key = ThingsGatewayCacheConst.Cache_Device;
var devices = App.CacheService.Get<List<Device>>(key);
if (devices == null)
{
db ??= GetDB();
devices = await db.Queryable<Device>().OrderBy(a => a.Id).ToListAsync().ConfigureAwait(false);
App.CacheService.Set(key, devices);
}
return devices;
}
db ??= GetDB();
var devices = await db.Queryable<Device>().WhereIF(expression != null, expression).OrderBy(a => a.Id).ToListAsync().ConfigureAwait(false);
public async Task<Device?> GetDeviceByIdAsync(long id)
{
var data = await GetAllAsync().ConfigureAwait(false);
return data?.FirstOrDefault(x => x.Id == id);
return devices;
}
/// <summary>
@@ -222,12 +212,12 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
HashSet<long>? channel = null;
if (!exportFilter.PluginName.IsNullOrWhiteSpace())
{
channel = (await _channelService.GetAllAsync().ConfigureAwait(false)).Where(a => a.PluginName == exportFilter.PluginName).Select(a => a.Id).ToHashSet();
channel = (GlobalData.IdChannels).Where(a => a.Value.PluginName == exportFilter.PluginName).Select(a => a.Value.Id).ToHashSet();
}
if (exportFilter.PluginType != null)
{
var pluginInfo = GlobalData.PluginService.GetList(exportFilter.PluginType).Select(a => a.FullName).ToHashSet();
channel = (await _channelService.GetAllAsync().ConfigureAwait(false)).Where(a => pluginInfo.Contains(a.PluginName)).Select(a => a.Id).ToHashSet();
channel = (GlobalData.IdChannels).Where(a => pluginInfo.Contains(a.Value.PluginName)).Select(a => a.Value.Id).ToHashSet();
}
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
var whereQuery = (ISugarQueryable<Device> a) => a
@@ -244,12 +234,12 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
HashSet<long>? channel = null;
if (!exportFilter.PluginName.IsNullOrWhiteSpace())
{
channel = (await _channelService.GetAllAsync().ConfigureAwait(false)).Where(a => a.PluginName == exportFilter.PluginName).Select(a => a.Id).ToHashSet();
channel = (GlobalData.IdChannels).Where(a => a.Value.PluginName == exportFilter.PluginName).Select(a => a.Value.Id).ToHashSet();
}
if (exportFilter.PluginType != null)
{
var pluginInfo = GlobalData.PluginService.GetList(exportFilter.PluginType).Select(a => a.FullName).ToHashSet();
channel = (await _channelService.GetAllAsync().ConfigureAwait(false)).Where(a => pluginInfo.Contains(a.PluginName)).Select(a => a.Id).ToHashSet();
channel = (GlobalData.IdChannels).Where(a => pluginInfo.Contains(a.Value.PluginName)).Select(a => a.Value.Id).ToHashSet();
}
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
var whereQuery = (IEnumerable<Device> a) => a
@@ -269,7 +259,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
[OperDesc("SaveDevice", localizerType: typeof(Device))]
public async Task<bool> SaveDeviceAsync(Device input, ItemChangedType type)
{
if ((await GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Name).TryGetValue(input.Name, out var device))
if (GlobalData.Devices.TryGetValue(input.Name, out var device))
{
if (device.Id != input.Id)
{
@@ -318,8 +308,8 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
var devices = await GetAsyncEnumerableData(exportFilter).ConfigureAwait(false);
var plugins = await GetAsyncEnumerableData(exportFilter).ConfigureAwait(false);
var devicesSql = await GetEnumerableData(exportFilter).ConfigureAwait(false);
var deviceDicts = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
var channelDicts = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
var deviceDicts = GlobalData.IdDevices;
var channelDicts = GlobalData.IdChannels;
var pluginSheetNames = (await devicesSql.Select(a => a.ChannelId).ToListAsync().ConfigureAwait(false)).Select(a =>
{
channelDicts.TryGetValue(a, out var channel);
@@ -350,8 +340,8 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
[OperDesc("ExportDevice", isRecordPar: false, localizerType: typeof(Device))]
public async Task<MemoryStream> ExportMemoryStream(List<Device>? models, string channelName = null, string plugin = null)
{
var deviceDicts = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
var channelDicts = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
var deviceDicts = GlobalData.IdDevices;
var channelDicts = GlobalData.IdChannels;
var pluginSheetNames = models.Select(a => a.ChannelId).Select(a =>
{
channelDicts.TryGetValue(a, out var channel);
@@ -415,10 +405,10 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
var sheetNames = MiniExcel.GetSheetNames(path);
// 获取所有设备,并将设备名称作为键构建设备字典
var deviceDicts = (await GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Name);
var deviceDicts = GlobalData.Devices;
// 获取所有通道,并将通道名称作为键构建通道字典
var channelDicts = (await _channelService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Name);
var channelDicts = GlobalData.Channels;
// 导入检验结果的预览字典,键为名称,值为导入预览对象
Dictionary<string, ImportPreviewOutputBase> ImportPreviews = new();
@@ -446,7 +436,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
}
}
public void SetDeviceData(HashSet<long>? dataScope, Dictionary<string, Device> deviceDicts, Dictionary<string, Channel> channelDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, ref ImportPreviewOutput<Device> deviceImportPreview, Dictionary<string, PluginInfo> driverPluginNameDict, ConcurrentDictionary<string, (Type, Dictionary<string, PropertyInfo>, Dictionary<string, PropertyInfo>)> propertysDict, string sheetName, IEnumerable<IDictionary<string, object>> rows)
public void SetDeviceData(HashSet<long>? dataScope, IReadOnlyDictionary<string, DeviceRuntime> deviceDicts, IReadOnlyDictionary<string, ChannelRuntime> channelDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, ref ImportPreviewOutput<Device> deviceImportPreview, Dictionary<string, PluginInfo> driverPluginNameDict, ConcurrentDictionary<string, (Type, Dictionary<string, PropertyInfo>, Dictionary<string, PropertyInfo>)> propertysDict, string sheetName, IEnumerable<IDictionary<string, object>> rows)
{
#region sheet

View File

@@ -19,10 +19,10 @@ namespace ThingsGateway.Gateway.Application;
public static class DeviceServiceHelpers
{
public static async Task<USheetDatas> ExportDeviceAsync(IEnumerable<Device> models)
public static USheetDatas ExportDevice(IEnumerable<Device> models)
{
var deviceDicts = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
var channelDicts = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
var deviceDicts = GlobalData.IdDevices;
var channelDicts = GlobalData.IdChannels;
var pluginSheetNames = models.Select(a => a.ChannelId).Select(a =>
{
channelDicts.TryGetValue(a, out var channel);
@@ -35,8 +35,8 @@ public static class DeviceServiceHelpers
public static Dictionary<string, object> ExportSheets(
IEnumerable<Device>? data,
Dictionary<long, Device>? deviceDicts,
Dictionary<long, Channel> channelDicts,
IReadOnlyDictionary<long, DeviceRuntime>? deviceDicts,
IReadOnlyDictionary<long, ChannelRuntime> channelDicts,
HashSet<string> pluginSheetNames,
string? channelName = null)
{
@@ -60,8 +60,8 @@ HashSet<string> pluginSheetNames,
public static Dictionary<string, object> ExportSheets(
IAsyncEnumerable<Device>? data1,
IAsyncEnumerable<Device>? data2,
Dictionary<long, Device>? deviceDicts,
Dictionary<long, Channel> channelDicts,
IReadOnlyDictionary<long, DeviceRuntime>? deviceDicts,
IReadOnlyDictionary<long, ChannelRuntime> channelDicts,
HashSet<string> pluginSheetNames,
string? channelName = null)
{
@@ -82,7 +82,7 @@ string? channelName = null)
return result;
}
static IAsyncEnumerable<Device> FilterPluginDevices(IAsyncEnumerable<Device> data, string plugin, Dictionary<long, Channel> channelDicts)
static IAsyncEnumerable<Device> FilterPluginDevices(IAsyncEnumerable<Device> data, string plugin, IReadOnlyDictionary<long, ChannelRuntime> channelDicts)
{
return data.Where(device =>
{
@@ -99,7 +99,7 @@ string? channelName = null)
}
});
}
static IEnumerable<Device> FilterPluginDevices(IEnumerable<Device> data, string plugin, Dictionary<long, Channel> channelDicts)
static IEnumerable<Device> FilterPluginDevices(IEnumerable<Device> data, string plugin, IReadOnlyDictionary<long, ChannelRuntime> channelDicts)
{
return data.Where(device =>
{
@@ -119,8 +119,8 @@ string? channelName = null)
static IEnumerable<Dictionary<string, object>> GetDeviceSheets(
IEnumerable<Device> data,
Dictionary<long, Device>? deviceDicts,
Dictionary<long, Channel> channelDicts,
IReadOnlyDictionary<long, DeviceRuntime>? deviceDicts,
IReadOnlyDictionary<long, ChannelRuntime> channelDicts,
string? channelName)
{
var type = typeof(Device);
@@ -157,8 +157,8 @@ Dictionary<long, Device>? deviceDicts,
static async IAsyncEnumerable<Dictionary<string, object>> GetDeviceSheets(
IAsyncEnumerable<Device> data,
Dictionary<long, Device>? deviceDicts,
Dictionary<long, Channel> channelDicts,
IReadOnlyDictionary<long, DeviceRuntime>? deviceDicts,
IReadOnlyDictionary<long, ChannelRuntime> channelDicts,
string? channelName)
{
var type = typeof(Device);
@@ -201,8 +201,8 @@ Dictionary<long, Device>? deviceDicts,
Device device,
IEnumerable<PropertyInfo>? propertyInfos,
Type type,
Dictionary<long, Device>? deviceDicts,
Dictionary<long, Channel>? channelDicts,
IReadOnlyDictionary<long, DeviceRuntime>? deviceDicts,
IReadOnlyDictionary<long, ChannelRuntime>? channelDicts,
string? channelName)
{
Dictionary<string, object> devExport = new();
@@ -276,10 +276,10 @@ string? channelName)
{
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
// 获取所有设备,并将设备名称作为键构建设备字典
var deviceDicts = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Name);
var deviceDicts = GlobalData.Devices;
// 获取所有通道,并将通道名称作为键构建通道字典
var channelDicts = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Name);
var channelDicts = GlobalData.Channels;
// 导入检验结果的预览字典,键为名称,值为导入预览对象
Dictionary<string, ImportPreviewOutputBase> ImportPreviews = new();

View File

@@ -13,6 +13,7 @@ using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components.Forms;
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;
namespace ThingsGateway.Gateway.Application;
@@ -73,14 +74,7 @@ internal interface IDeviceService
/// 获取所有设备信息。
/// </summary>
/// <returns>所有设备信息</returns>
Task<List<Device>> GetAllAsync(SqlSugarClient db = null);
/// <summary>
/// 根据ID获取设备信息。
/// </summary>
/// <param name="id">设备ID</param>
/// <returns>设备信息</returns>
Task<Device?> GetDeviceByIdAsync(long id);
Task<List<Device>> GetFromDBAsync(Expression<Func<Device, bool>> expression = null, SqlSugarClient db = null);
/// <summary>
/// 导入设备信息。
@@ -119,7 +113,7 @@ internal interface IDeviceService
/// <returns>保存是否成功的异步任务</returns>
Task<bool> BatchSaveDeviceAsync(List<Device> input, ItemChangedType type);
void SetDeviceData(HashSet<long>? dataScope, Dictionary<string, Device> deviceDicts, Dictionary<string, Channel> channelDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, ref ImportPreviewOutput<Device> deviceImportPreview, Dictionary<string, PluginInfo> driverPluginNameDict, ConcurrentDictionary<string, (Type, Dictionary<string, PropertyInfo>, Dictionary<string, PropertyInfo>)> propertysDict, string sheetName, IEnumerable<IDictionary<string, object>> rows);
void SetDeviceData(HashSet<long>? dataScope, IReadOnlyDictionary<string, DeviceRuntime> deviceDicts, IReadOnlyDictionary<string, ChannelRuntime> channelDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, ref ImportPreviewOutput<Device> deviceImportPreview, Dictionary<string, PluginInfo> driverPluginNameDict, ConcurrentDictionary<string, (Type, Dictionary<string, PropertyInfo>, Dictionary<string, PropertyInfo>)> propertysDict, string sheetName, IEnumerable<IDictionary<string, object>> rows);
/// <summary>
/// 保存是否输出日志和日志等级

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);
@@ -581,13 +584,13 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
await RemoveDeviceAsync(deviceRuntime.Id).ConfigureAwait(false);
//获取主设备
var devices = await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false);//获取设备属性
var devices = GlobalData.IdDevices;
if (deviceRuntime.RedundantEnable && deviceRuntime.RedundantDeviceId != null)
{
if (!GlobalData.ReadOnlyIdDevices.TryGetValue(deviceRuntime.RedundantDeviceId ?? 0, out newDeviceRuntime))
{
var newDev = await GlobalData.DeviceService.GetDeviceByIdAsync(deviceRuntime.RedundantDeviceId ?? 0).ConfigureAwait(false);
devices.TryGetValue(deviceRuntime.RedundantDeviceId ?? 0, out var newDev);
if (newDev == null)
{
LogMessage?.LogWarning($"Device with deviceId {deviceRuntime.RedundantDeviceId} not found");
@@ -608,7 +611,7 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
newDeviceRuntime = GlobalData.ReadOnlyIdDevices.FirstOrDefault(a => a.Value.RedundantDeviceId == deviceRuntime.Id).Value;
if (newDeviceRuntime == null)
{
var newDev = devices.FirstOrDefault(a => a.RedundantDeviceId == deviceRuntime.Id);
var newDev = devices.FirstOrDefault(a => a.Value.RedundantDeviceId == deviceRuntime.Id).Value;
if (newDev == null)
{
LogMessage?.LogWarning($"Device with redundantDeviceId {deviceRuntime.Id} not found");
@@ -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

@@ -39,8 +39,8 @@ internal sealed class GatewayMonitorHostedService : BackgroundService, IGatewayM
try
{
//网关启动时,获取所有通道
var channelRuntimes = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).AdaptListChannelRuntime();
var deviceRuntimes = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).AdaptListDeviceRuntime();
var channelRuntimes = (await GlobalData.ChannelService.GetFromDBAsync().ConfigureAwait(false)).AdaptListChannelRuntime();
var deviceRuntimes = (await GlobalData.DeviceService.GetFromDBAsync().ConfigureAwait(false)).AdaptListDeviceRuntime();
var variableRuntimes = GlobalData.VariableService.GetAllVariableRuntime();
@@ -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

@@ -35,7 +35,7 @@ internal sealed class PluginService : IPluginService
/// </summary>
public const string DirName = "GatewayPlugins";
private const string CacheKeyGetPluginOutputs = $"{ThingsGatewayCacheConst.Cache_Prefix}{nameof(PluginService)}{nameof(GetList)}";
private const string CacheKeyGetPluginOutputs = $"ThingsGateway.Gateway.Application.{nameof(PluginService)}{nameof(GetList)}";
private const string SaveEx = ".save";
private const string DelEx = ".del";
@@ -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

@@ -54,7 +54,7 @@ public static class PluginServiceUtil
{
{ "title", classAttribute.Remark }
};
tc.ComponentParameters.AddItem(
tc.ComponentParameters = tc.ComponentParameters.AddItem(
new("title", classAttribute.Remark)
);
}

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

@@ -151,13 +151,13 @@ internal static class RuntimeServiceHelper
public static async Task<List<ChannelRuntime>> GetNewChannelRuntimesAsync(HashSet<long> ids)
{
var newChannelRuntimes = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).Where(a => ids.Contains(a.Id)).AdaptListChannelRuntime();
var newChannelRuntimes = (await GlobalData.ChannelService.GetFromDBAsync((a => ids.Contains(a.Id))).ConfigureAwait(false)).AdaptListChannelRuntime();
return newChannelRuntimes;
}
public static async Task<List<DeviceRuntime>> GetNewDeviceRuntimesAsync(HashSet<long> deviceids)
{
var newDeviceRuntimes = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).Where(a => deviceids.Contains(a.Id)).AdaptListDeviceRuntime();
var newDeviceRuntimes = (await GlobalData.DeviceService.GetFromDBAsync((a => deviceids.Contains(a.Id))).ConfigureAwait(false)).AdaptListDeviceRuntime();
return newDeviceRuntimes;
}

View File

@@ -106,6 +106,6 @@ internal interface IVariableService
Task<List<Variable>> GetByDeviceIdAsync(List<long> deviceIds);
void DeleteVariableCache();
ImportPreviewOutput<Dictionary<string, Variable>> SetVariableData(HashSet<long>? dataScope, Dictionary<string, Device> deviceDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, ImportPreviewOutput<Dictionary<string, Variable>> deviceImportPreview, Dictionary<string, PluginInfo> driverPluginNameDict, ConcurrentDictionary<string, (Type, Dictionary<string, PropertyInfo>, Dictionary<string, PropertyInfo>)> propertysDict, string sheetName, IEnumerable<IDictionary<string, object>> rows);
ImportPreviewOutput<Dictionary<string, Variable>> SetVariableData(HashSet<long>? dataScope, IReadOnlyDictionary<string, DeviceRuntime> deviceDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, ImportPreviewOutput<Dictionary<string, Variable>> deviceImportPreview, Dictionary<string, PluginInfo> driverPluginNameDict, ConcurrentDictionary<string, (Type, Dictionary<string, PropertyInfo>, Dictionary<string, PropertyInfo>)> propertysDict, string sheetName, IEnumerable<IDictionary<string, object>> rows);
List<VariableRuntime> GetAllVariableRuntime();
}

View File

@@ -375,37 +375,37 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
HashSet<long>? deviceId = null;
if (!exportFilter.PluginName.IsNullOrWhiteSpace())
{
var channel = (await _channelService.GetAllAsync().ConfigureAwait(false)).Where(a => a.PluginName == exportFilter.PluginName).Select(a => a.Id).ToHashSet();
deviceId = (await _deviceService.GetAllAsync().ConfigureAwait(false)).Where(a => channel.Contains(a.ChannelId)).Select(a => a.Id).ToHashSet();
var channel = (GlobalData.IdChannels).Where(a => a.Value.PluginName == exportFilter.PluginName).Select(a => a.Value.Id).ToHashSet();
deviceId = (GlobalData.IdDevices).Where(a => channel.Contains(a.Value.ChannelId)).Select(a => a.Value.Id).ToHashSet();
}
else if (exportFilter.ChannelId != null)
{
deviceId = (await _deviceService.GetAllAsync().ConfigureAwait(false)).Where(a => a.ChannelId == exportFilter.ChannelId).Select(a => a.Id).ToHashSet();
deviceId = (GlobalData.IdDevices).Where(a => a.Value.ChannelId == exportFilter.ChannelId).Select(a => a.Value.Id).ToHashSet();
}
var whereQuery = (ISugarQueryable<Variable> a) => a
.WhereIF(!exportFilter.QueryPageOptions.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(exportFilter.QueryPageOptions.SearchText!))
.WhereIF(exportFilter.PluginType == PluginTypeEnum.Collect, a => a.DeviceId == exportFilter.DeviceId)
.WhereIF(deviceId != null, a => deviceId.Contains(a.DeviceId))
.WhereIF(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
.WhereIF(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
.WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
.WhereIF(exportFilter.PluginType == PluginTypeEnum.Business, u => SqlFunc.JsonLike(u.VariablePropertys, exportFilter.DeviceId.ToString()));
return whereQuery;
}
private async Task<Func<IEnumerable<Variable>, IEnumerable<Variable>>> GetWhereEnumerableFunc(ExportFilter exportFilter)
private async Task<Func<IEnumerable<Variable>, IEnumerable<Variable>>> GetWhereEnumerableFunc(ExportFilter exportFilter, bool sql = false)
{
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
HashSet<long>? deviceId = null;
if (!exportFilter.PluginName.IsNullOrWhiteSpace())
{
var channel = (await _channelService.GetAllAsync().ConfigureAwait(false)).Where(a => a.PluginName == exportFilter.PluginName).Select(a => a.Id).ToHashSet();
deviceId = (await _deviceService.GetAllAsync().ConfigureAwait(false)).Where(a => channel.Contains(a.ChannelId)).Select(a => a.Id).ToHashSet();
var channel = (GlobalData.IdChannels).Where(a => a.Value.PluginName == exportFilter.PluginName).Select(a => a.Value.Id).ToHashSet();
deviceId = (GlobalData.IdDevices).Where(a => channel.Contains(a.Value.ChannelId)).Select(a => a.Value.Id).ToHashSet();
}
else if (exportFilter.ChannelId != null)
{
deviceId = (await _deviceService.GetAllAsync().ConfigureAwait(false)).Where(a => a.ChannelId == exportFilter.ChannelId).Select(a => a.Id).ToHashSet();
deviceId = (GlobalData.IdDevices).Where(a => a.Value.ChannelId == exportFilter.ChannelId).Select(a => a.Value.Id).ToHashSet();
}
var whereQuery = (IEnumerable<Variable> a) => a
.WhereIF(!exportFilter.QueryPageOptions.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(exportFilter.QueryPageOptions.SearchText!))
@@ -415,7 +415,12 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
.WhereIF(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
.WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
.WhereIF(exportFilter.PluginType == PluginTypeEnum.Business, u => SqlFunc.JsonLike(u.VariablePropertys, exportFilter.DeviceId.ToString()));
.WhereIF(sql && exportFilter.PluginType == PluginTypeEnum.Business, u => SqlFunc.JsonLike(u.VariablePropertys, exportFilter.DeviceId.ToString()))
.WhereIF(!sql && exportFilter.PluginType == PluginTypeEnum.Business && exportFilter.DeviceId > 0, u =>
GlobalData.IdVariables.TryGetValue(u.Id, out var runtime) &&
GlobalData.ContainsVariable(exportFilter.DeviceId.Value, runtime)
);
return whereQuery;
}
@@ -442,7 +447,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
public void DeleteVariableCache()
{
App.CacheService.Remove(ThingsGatewayCacheConst.Cache_Variable);
//App.CacheService.Remove(ThingsGatewayCacheConst.Cache_Variable);
}
public List<VariableRuntime> GetAllVariableRuntime()
@@ -461,14 +466,14 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
[OperDesc("ExportVariable", isRecordPar: false, localizerType: typeof(Variable))]
public async Task<MemoryStream> ExportMemoryStream(List<Variable> variables, string deviceName = null)
{
var deviceDicts = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
var channelDicts = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
var deviceDicts = GlobalData.IdDevices;
var channelDicts = GlobalData.IdChannels;
var pluginSheetNames = variables.Where(a => a.VariablePropertys?.Count > 0).SelectMany(a => a.VariablePropertys).Select(a =>
{
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);
@@ -493,14 +498,14 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
//导出
var variables = GlobalData.IdVariables.Select(a => a.Value).GetQuery(exportFilter.QueryPageOptions, whereQuery, exportFilter.FilterKeyValueAction);
var deviceDicts = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
var channelDicts = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
var deviceDicts = GlobalData.IdDevices;
var channelDicts = GlobalData.IdChannels;
var pluginSheetNames = variables.Where(a => a.VariablePropertys?.Count > 0).SelectMany(a => a.VariablePropertys).Select(a =>
{
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);
@@ -512,8 +517,11 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
}
else
{
var data = (await PageAsync(exportFilter).ConfigureAwait(false));
var sheets = await VariableServiceHelpers.ExportCoreAsync(data.Items, sortName: exportFilter.QueryPageOptions.SortName, sortOrder: exportFilter.QueryPageOptions.SortOrder).ConfigureAwait(false);
var whereQuery = await GetWhereEnumerableFunc(exportFilter).ConfigureAwait(false);
//导出
var data = GlobalData.IdVariables.Select(a => a.Value).GetQuery(exportFilter.QueryPageOptions, whereQuery, exportFilter.FilterKeyValueAction);
//var data = (await PageAsync(exportFilter).ConfigureAwait(false));
var sheets = VariableServiceHelpers.ExportCore(data, sortName: exportFilter.QueryPageOptions.SortName, sortOrder: exportFilter.QueryPageOptions.SortOrder);
return sheets;
}
}
@@ -578,7 +586,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
var sheetNames = MiniExcel.GetSheetNames(path);
// 获取所有设备的字典,以设备名称作为键
var deviceDicts = (await _deviceService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Name);
var deviceDicts = GlobalData.Devices;
// 存储导入检验结果的字典
Dictionary<string, ImportPreviewOutputBase> ImportPreviews = new();
@@ -609,7 +617,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
}
}
public ImportPreviewOutput<Dictionary<string, Variable>> SetVariableData(HashSet<long>? dataScope, Dictionary<string, Device> deviceDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, ImportPreviewOutput<Dictionary<string, Variable>> deviceImportPreview, Dictionary<string, PluginInfo> driverPluginNameDict, ConcurrentDictionary<string, (Type, Dictionary<string, PropertyInfo>, Dictionary<string, PropertyInfo>)> propertysDict, string sheetName, IEnumerable<IDictionary<string, object>> rows)
public ImportPreviewOutput<Dictionary<string, Variable>> SetVariableData(HashSet<long>? dataScope, IReadOnlyDictionary<string, DeviceRuntime> deviceDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, ImportPreviewOutput<Dictionary<string, Variable>> deviceImportPreview, Dictionary<string, PluginInfo> driverPluginNameDict, ConcurrentDictionary<string, (Type, Dictionary<string, PropertyInfo>, Dictionary<string, PropertyInfo>)> propertysDict, string sheetName, IEnumerable<IDictionary<string, object>> rows)
{
// 变量页处理
if (sheetName == ExportString.VariableName)

View File

@@ -21,16 +21,16 @@ namespace ThingsGateway.Gateway.Application;
public static class VariableServiceHelpers
{
public static async Task<USheetDatas> ExportVariableAsync(IEnumerable<Variable> variables, string sortName = nameof(Variable.Id), SortOrder sortOrder = SortOrder.Asc)
public static USheetDatas ExportVariable(IEnumerable<Variable> variables, string sortName = nameof(Variable.Id), SortOrder sortOrder = SortOrder.Asc)
{
var deviceDicts = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
var channelDicts = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
var deviceDicts = GlobalData.IdDevices;
var channelDicts = GlobalData.IdChannels;
var pluginSheetNames = variables.Where(a => a.VariablePropertys?.Count > 0).SelectMany(a => a.VariablePropertys).Select(a =>
{
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);
@@ -41,8 +41,8 @@ public static class VariableServiceHelpers
static IAsyncEnumerable<Variable> FilterPluginDevices(
IAsyncEnumerable<Variable> data,
string pluginName,
Dictionary<long, Device> deviceDicts,
Dictionary<long, Channel> channelDicts)
IReadOnlyDictionary<long, DeviceRuntime> deviceDicts,
IReadOnlyDictionary<long, ChannelRuntime> channelDicts)
{
return data.Where(variable =>
{
@@ -65,8 +65,8 @@ Dictionary<long, Device> deviceDicts,
static IEnumerable<Variable> FilterPluginDevices(
IEnumerable<Variable> data,
string pluginName,
Dictionary<long, Device> deviceDicts,
Dictionary<long, Channel> channelDicts)
IReadOnlyDictionary<long, DeviceRuntime> deviceDicts,
IReadOnlyDictionary<long, ChannelRuntime> channelDicts)
{
return data.Where(variable =>
{
@@ -88,8 +88,8 @@ Dictionary<long, Channel> channelDicts)
public static Dictionary<string, object> ExportSheets(
IEnumerable<Variable> data,
Dictionary<long, Device> deviceDicts,
Dictionary<long, Channel> channelDicts,
IReadOnlyDictionary<long, DeviceRuntime> deviceDicts,
IReadOnlyDictionary<long, ChannelRuntime> channelDicts,
Dictionary<string, VariablePropertyBase> pluginDrivers,
string? deviceName = null)
{
@@ -112,7 +112,7 @@ Dictionary<long, Channel> channelDicts)
static IEnumerable<Dictionary<string, object>> GetVariableSheets(
IEnumerable<Variable> data,
Dictionary<long, Device> deviceDicts,
IReadOnlyDictionary<long, DeviceRuntime> deviceDicts,
string? deviceName)
{
var type = typeof(Variable);
@@ -132,7 +132,7 @@ Dictionary<long, Channel> channelDicts)
}
}
private static Dictionary<string, object> GetVariable(Dictionary<long, Device> deviceDicts, string? deviceName, Type type, IOrderedEnumerable<PropertyInfo> propertyInfos, Variable variable)
private static Dictionary<string, object> GetVariable(IReadOnlyDictionary<long, DeviceRuntime> deviceDicts, string? deviceName, Type type, IOrderedEnumerable<PropertyInfo> propertyInfos, Variable variable)
{
var row = new Dictionary<string, object>();
deviceDicts.TryGetValue(variable.DeviceId, out var device);
@@ -153,8 +153,8 @@ Dictionary<long, Channel> channelDicts)
static IEnumerable<Dictionary<string, object>> GetPluginSheets(
IEnumerable<Variable> data,
Dictionary<long, Device> deviceDicts,
Dictionary<long, Channel> channelDicts,
IReadOnlyDictionary<long, DeviceRuntime> deviceDicts,
IReadOnlyDictionary<long, ChannelRuntime> channelDicts,
string plugin,
Dictionary<string, VariablePropertyBase> pluginDrivers,
ConcurrentDictionary<string, (VariablePropertyBase, Dictionary<string, PropertyInfo>)> propertysDict)
@@ -224,8 +224,8 @@ Dictionary<long, Channel> channelDicts)
public static Dictionary<string, object> ExportSheets(
IAsyncEnumerable<Variable> data,
Dictionary<long, Device> deviceDicts,
Dictionary<long, Channel> channelDicts,
IReadOnlyDictionary<long, DeviceRuntime> deviceDicts,
IReadOnlyDictionary<long, ChannelRuntime> channelDicts,
Dictionary<string, VariablePropertyBase> pluginDrivers,
string? deviceName = null)
{
@@ -248,7 +248,7 @@ Dictionary<long, Channel> channelDicts)
static async IAsyncEnumerable<Dictionary<string, object>> GetVariableSheets(
IAsyncEnumerable<Variable> data,
Dictionary<long, Device> deviceDicts,
IReadOnlyDictionary<long, DeviceRuntime> deviceDicts,
string? deviceName)
{
var type = typeof(Variable);
@@ -270,8 +270,8 @@ Dictionary<long, Channel> channelDicts)
static async IAsyncEnumerable<Dictionary<string, object>> GetPluginSheets(
IAsyncEnumerable<Variable> data,
Dictionary<long, Device> deviceDicts,
Dictionary<long, Channel> channelDicts,
IReadOnlyDictionary<long, DeviceRuntime> deviceDicts,
IReadOnlyDictionary<long, ChannelRuntime> channelDicts,
string plugin,
Dictionary<string, VariablePropertyBase> pluginDrivers,
ConcurrentDictionary<string, (VariablePropertyBase, Dictionary<string, PropertyInfo>)> propertysDict)
@@ -314,14 +314,14 @@ Dictionary<long, Channel> channelDicts)
}
}
public static async Task<Dictionary<string, object>> ExportCoreAsync(IEnumerable<Variable> data, string deviceName = null, string sortName = nameof(Variable.Id), SortOrder sortOrder = SortOrder.Asc)
public static Dictionary<string, object> ExportCore(IEnumerable<Variable> data, string deviceName = null, string sortName = nameof(Variable.Id), SortOrder sortOrder = SortOrder.Asc)
{
if (data?.Any() != true)
{
data = new List<Variable>();
}
var deviceDicts = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
var channelDicts = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
var deviceDicts = GlobalData.IdDevices;
var channelDicts = GlobalData.IdChannels;
var driverPluginDicts = GlobalData.PluginService.GetList(PluginTypeEnum.Business).ToDictionary(a => a.FullName);
//总数据
Dictionary<string, object> sheets = new();
@@ -503,7 +503,7 @@ Dictionary<long, Channel> channelDicts)
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
// 获取所有设备的字典,以设备名称作为键
var deviceDicts = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Name);
var deviceDicts = GlobalData.Devices;
// 存储导入检验结果的字典
Dictionary<string, ImportPreviewOutputBase> ImportPreviews = new();

View File

@@ -752,7 +752,7 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
};
var models = await GlobalData.GetCurrentUserDevices().ConfigureAwait(false);
var uSheetDatas = await DeviceServiceHelpers.ExportDeviceAsync(models);
var uSheetDatas = DeviceServiceHelpers.ExportDevice(models);
op.Component = BootstrapDynamicComponent.CreateComponent<USheet>(new Dictionary<string, object?>
{

View File

@@ -275,7 +275,7 @@ public partial class DeviceTable : IDisposable
await ToastService.Warning("online Excel max data count 50000");
return;
}
var uSheetDatas = await DeviceServiceHelpers.ExportDeviceAsync(models);
var uSheetDatas = DeviceServiceHelpers.ExportDevice(models);
op.Component = BootstrapDynamicComponent.CreateComponent<USheet>(new Dictionary<string, object?>
{

View File

@@ -331,7 +331,7 @@ public partial class VariableRuntimeInfo : IDisposable
await ToastService.Warning("online Excel max data count 50000");
return;
}
var uSheetDatas = await VariableServiceHelpers.ExportVariableAsync(models, _option.SortName, _option.SortOrder);
var uSheetDatas = VariableServiceHelpers.ExportVariable(models, _option.SortName, _option.SortOrder);
op.Component = BootstrapDynamicComponent.CreateComponent<USheet>(new Dictionary<string, object?>
{

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

@@ -8,6 +8,8 @@
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Modbus;
/// <summary>
@@ -65,12 +67,15 @@ public class ModbusRtuSend : ISendMessage
}
else if (wf == 15 || wf == 16)
{
var data = ModbusAddress.Data.ArrayExpandToLengthEven().Span;
WriterExtension.WriteValue(ref byteBlock, (ushort)(data.Length + 7), EndianType.Big);
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Station);
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.WriteFunctionCode);
WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.StartAddress, EndianType.Big);
WriterExtension.WriteValue(ref byteBlock, (ushort)Math.Ceiling(wf == 15 ? ModbusAddress.Data.Length * 8 : ModbusAddress.Data.Length / 2.0), EndianType.Big);
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Data.Length);
byteBlock.Write(ModbusAddress.Data.Span);
var len = (ushort)Math.Ceiling(wf == 15 ? ModbusAddress.Data.Length * 8 : ModbusAddress.Data.Length / 2.0);
WriterExtension.WriteValue(ref byteBlock, len, EndianType.Big);
WriterExtension.WriteValue(ref byteBlock, (byte)(len * 2));
byteBlock.Write(data);
}
else
{

View File

@@ -8,6 +8,8 @@
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Modbus;
/// <summary>
@@ -84,13 +86,15 @@ public class ModbusTcpSend : ISendMessage
}
else if (wf == 15 || wf == 16)
{
WriterExtension.WriteValue(ref byteBlock, (ushort)(ModbusAddress.Data.Length + 7), EndianType.Big);
var data = ModbusAddress.Data.ArrayExpandToLengthEven().Span;
WriterExtension.WriteValue(ref byteBlock, (ushort)(data.Length + 7), EndianType.Big);
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Station);
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.WriteFunctionCode);
WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.StartAddress, EndianType.Big);
WriterExtension.WriteValue(ref byteBlock, (ushort)Math.Ceiling(wf == 15 ? ModbusAddress.Data.Length * 8 : ModbusAddress.Data.Length / 2.0), EndianType.Big);
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Data.Length);
byteBlock.Write(ModbusAddress.Data.Span);
var len = (ushort)Math.Ceiling(wf == 15 ? ModbusAddress.Data.Length * 8 : ModbusAddress.Data.Length / 2.0);
WriterExtension.WriteValue(ref byteBlock, len, EndianType.Big);
WriterExtension.WriteValue(ref byteBlock, (byte)(len * 2));
byteBlock.Write(data);
}
else
{

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
@@ -286,7 +286,7 @@ public class OpcUaMaster : IDisposable
}
catch (Exception)
{
m_session.RemoveSubscription(m_subscription);
await m_session.RemoveSubscriptionAsync(m_subscription, cancellationToken).ConfigureAwait(false);
throw;
}
}
@@ -312,7 +312,7 @@ public class OpcUaMaster : IDisposable
else if (_subscriptionDicts.TryGetValue(subscriptionName, out var existingSubscription))
{
// remove
existingSubscription.Delete(true);
await existingSubscription.DeleteAsync(true, cancellationToken).ConfigureAwait(false);
await m_session.RemoveSubscriptionAsync(existingSubscription, cancellationToken).ConfigureAwait(false);
try { existingSubscription.Dispose(); } catch { }
_subscriptionDicts[subscriptionName] = m_subscription;
@@ -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>
@@ -715,7 +732,7 @@ public class OpcUaMaster : IDisposable
{
foreach (var item in _subscriptionDicts)
{
item.Value.Delete(true);
await item.Value.DeleteAsync(true).ConfigureAwait(false);
await m_session.RemoveSubscriptionAsync(item.Value).ConfigureAwait(false);
try { item.Value.Dispose(); } catch { }
}
@@ -731,7 +748,7 @@ public class OpcUaMaster : IDisposable
if (_subscriptionDicts.TryGetValue(subscriptionName, out var subscription))
{
// remove
subscription.Delete(true);
await subscription.DeleteAsync(true).ConfigureAwait(false);
await m_session.RemoveSubscriptionAsync(subscription).ConfigureAwait(false);
try { subscription.Dispose(); } catch { }
_subscriptionDicts.TryRemove(subscriptionName, out _);
@@ -758,7 +775,7 @@ public class OpcUaMaster : IDisposable
var dataValue = JsonUtils.Decode(
m_session.MessageContext,
variableNode.DataType,
TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable),
await TypeInfo.GetBuiltInTypeAsync(variableNode.DataType, m_session.SystemContext.TypeTable, cancellationToken).ConfigureAwait(false),
item.Value.CalculateActualValueRank(),
item.Value
);
@@ -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
@@ -983,7 +1023,7 @@ public class OpcUaMaster : IDisposable
for (int i = 0; i < results.Count; i++)
{
var variableNode = await ReadNodeAsync(nodeIds[i].ToString(), false, StatusCode.IsGood(results[i].StatusCode), cancellationToken).ConfigureAwait(false);
var type = TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable);
var type = await TypeInfo.GetBuiltInTypeAsync(variableNode.DataType, m_session.SystemContext.TypeTable, cancellationToken).ConfigureAwait(false);
var jToken = JsonUtils.Encode(m_session.MessageContext, type, results[i].Value);
jTokens.Add((variableNode.NodeId.ToString(), results[i], jToken));
}
@@ -1078,7 +1118,7 @@ public class OpcUaMaster : IDisposable
var responseHeader = readResponse.ResponseHeader;
VariableNode variableNode = GetVariableNodes(itemsToRead, values, diagnosticInfos, responseHeader).FirstOrDefault();
if (OpcUaProperty.LoadType && variableNode.DataType != NodeId.Null && TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable) == BuiltInType.ExtensionObject)
if (OpcUaProperty.LoadType && variableNode.DataType != NodeId.Null && (await TypeInfo.GetBuiltInTypeAsync(variableNode.DataType, m_session.SystemContext.TypeTable, cancellationToken).ConfigureAwait(false)) == BuiltInType.ExtensionObject)
await typeSystem.LoadType(variableNode.DataType, ct: cancellationToken).ConfigureAwait(false);
if (cache)
@@ -1178,7 +1218,7 @@ public class OpcUaMaster : IDisposable
{
if (cache)
_variableDicts.AddOrUpdate(nodeIdStrs[i], a => node, (a, b) => node);
if (node.DataType != NodeId.Null && TypeInfo.GetBuiltInType(node.DataType, m_session.SystemContext.TypeTable) == BuiltInType.ExtensionObject)
if (node.DataType != NodeId.Null && (await TypeInfo.GetBuiltInTypeAsync(node.DataType, m_session.SystemContext.TypeTable, cancellationToken).ConfigureAwait(false)) == BuiltInType.ExtensionObject)
{
await typeSystem.LoadType(node.DataType, ct: cancellationToken).ConfigureAwait(false);
}
@@ -1376,5 +1416,7 @@ public class OpcUaMaster : IDisposable
}
}
#endregion
}

View File

@@ -4,7 +4,7 @@
<Import Project="..\..\PackNuget.props" />
<Import Project="..\..\Version.props" />
<PropertyGroup>
<TargetFrameworks>net48;netstandard2.1;net6.0;</TargetFrameworks>
<TargetFrameworks>net48;netstandard2.1;net8.0;</TargetFrameworks>
<Description>工业设备通讯协议-OpcUa协议</Description>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<DocumentationFile></DocumentationFile>
@@ -12,7 +12,7 @@
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.5.376.235" />
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.5.376.244" />
</ItemGroup>
<ItemGroup>

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

@@ -37,7 +37,7 @@ public class Dlt645Test
var dltMaster = new Dlt645_2007Master() { Timeout = 10000, Station = "111111111111" };
dltMaster.InitChannel(dltChannel);
await dltChannel.SetupAsync(dltChannel.Config);
await dltChannel.ConnectAsync(dltChannel.ChannelOptions.ConnectTimeout, CancellationToken.None);
await dltMaster.ConnectAsync(CancellationToken.None);
var adapter = dltChannel.ReadOnlyDataHandlingAdapter as SingleStreamDataHandlingAdapter;
var task1 = Task.Run(async () =>

View File

@@ -42,7 +42,7 @@ public class ModbusTest
var modbusMaster = new ModbusMaster() { ModbusType = ModbusTypeEnum.ModbusTcp, Timeout = 10000 };
modbusMaster.InitChannel(modbusChannel);
await modbusChannel.SetupAsync(modbusChannel.Config);
await modbusChannel.ConnectAsync(modbusChannel.ChannelOptions.ConnectTimeout, CancellationToken.None);
await modbusMaster.ConnectAsync(CancellationToken.None);
var adapter = modbusChannel.ReadOnlyDataHandlingAdapter as SingleStreamDataHandlingAdapter;
var task1 = Task.Run(async () =>
@@ -94,7 +94,7 @@ public class ModbusTest
var modbusMaster = new ModbusMaster() { ModbusType = ModbusTypeEnum.ModbusRtu, Timeout = 10000, Station = 1 };
modbusMaster.InitChannel(modbusChannel);
await modbusChannel.SetupAsync(modbusChannel.Config);
await modbusChannel.ConnectAsync(modbusChannel.ChannelOptions.ConnectTimeout, CancellationToken.None);
await modbusMaster.ConnectAsync(CancellationToken.None);
var adapter = modbusChannel.ReadOnlyDataHandlingAdapter as SingleStreamDataHandlingAdapter;
var task1 = Task.Run(async () =>

View File

@@ -41,7 +41,7 @@ public class SiemensS7Test
var siemensS7Master = new SiemensS7Master() { SiemensS7Type = SiemensTypeEnum.S1200, Timeout = 10000 };
siemensS7Master.InitChannel(siemensS7Channel);
await siemensS7Channel.SetupAsync(siemensS7Channel.Config);
await siemensS7Channel.ConnectAsync(siemensS7Channel.ChannelOptions.ConnectTimeout, CancellationToken.None);
await siemensS7Master.ConnectAsync(CancellationToken.None);
var adapter = siemensS7Channel.ReadOnlyDataHandlingAdapter as SingleStreamDataHandlingAdapter;
await siemensS7Master.ConnectAsync(CancellationToken.None);
var task1 = Task.Run(async () =>

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

@@ -10,6 +10,7 @@
"IsWriteMemory": "IsWriteMemory",
"ModbusType": "ModbusType",
"MulStation": "MultipleStations",
"SendDelayTime": "SendDelayTime",
"Station": "DefaultStation"
},
"ThingsGateway.Plugin.Modbus.ModbusSlaveVariableProperty": {

View File

@@ -10,6 +10,7 @@
"IsWriteMemory": "立即写入内存",
"ModbusType": "协议类型",
"MulStation": "多站点",
"SendDelayTime": "发送延时",
"Station": "默认站号"
},
"ThingsGateway.Plugin.Modbus.ModbusSlaveVariableProperty": {

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;
@@ -100,6 +101,7 @@ public class ModbusSlave : BusinessBase
_plc.IsWriteMemory = _driverPropertys.IsWriteMemory;
_plc.MulStation = _driverPropertys.MulStation;
_plc.ModbusType = _driverPropertys.ModbusType;
_plc.SendDelayTime = _driverPropertys.SendDelayTime;
_plc.InitChannel(channel, LogMessage);
await base.InitChannelAsync(channel, cancellationToken).ConfigureAwait(false);
@@ -136,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

@@ -53,4 +53,7 @@ public class ModbusSlaveProperty : BusinessPropertyBase
/// </summary>
[DynamicProperty]
public bool IsWriteMemory { get; set; } = true;
[DynamicProperty]
public int SendDelayTime { get; set; }
}

View File

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

View File

@@ -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);

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()
@@ -154,7 +154,7 @@ public class OpcDaMaster : CollectBase
/// <inheritdoc/>
protected override async ValueTask<Dictionary<string, OperResult>> WriteValuesAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
{
using var writeLock = ReadWriteLock.WriterLock();
using var writeLock = await ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false);
await ValueTask.CompletedTask.ConfigureAwait(false);
var result = _plc.WriteItem(writeInfoLists.ToDictionary(a => a.Key.RegisterAddress!, a => a.Value.GetObjectFromJToken()!));
var results = new ConcurrentDictionary<string, OperResult>(result.ToDictionary<KeyValuePair<string, Tuple<bool, string>>, string, OperResult>(a => writeInfoLists.Keys.FirstOrDefault(b => b.RegisterAddress == a.Key).Name, a =>
@@ -167,7 +167,8 @@ public class OpcDaMaster : CollectBase
));
await Check(writeInfoLists, results, cancellationToken).ConfigureAwait(false);
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug)
LogMessage?.Debug(string.Format("Write result: {0} - {1}", DeviceName, results.Select(a => $"{a.Key} - {a.Key.Length} - {(a.Value.IsSuccess ? "Success" : a.Value.ErrorMessage)}").ToSystemTextJsonString(false)));
return new(results);
}
public override async Task AfterVariablesChangedAsync(CancellationToken cancellationToken)

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()
@@ -275,7 +274,7 @@ public class OpcUaMaster : CollectBase
/// <inheritdoc/>
protected override async ValueTask<Dictionary<string, OperResult>> WriteValuesAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
{
using var writeLock = ReadWriteLock.WriterLock();
using var writeLock = await ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false);
var result = await _plc.WriteNodeAsync(writeInfoLists.ToDictionary(a => a.Key.RegisterAddress!, a => a.Value), cancellationToken).ConfigureAwait(false);
var results = new ConcurrentDictionary<string, OperResult>(result.ToDictionary<KeyValuePair<string, Tuple<bool, string>>, string, OperResult>(a => writeInfoLists.Keys.FirstOrDefault(b => b.RegisterAddress == a.Key)?.Name!
, a =>
@@ -287,7 +286,8 @@ public class OpcUaMaster : CollectBase
}));
await Check(writeInfoLists, results, cancellationToken).ConfigureAwait(false);
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug)
LogMessage?.Debug(string.Format("Write result: {0} - {1}", DeviceName, results.Select(a => $"{a.Key} - {a.Key.Length} - {(a.Value.IsSuccess ? "Success" : a.Value.ErrorMessage)}").ToSystemTextJsonString(false)));
return new(results);
}
@@ -300,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

@@ -22,27 +22,27 @@
</ProjectReference>
<ProjectReference Include="..\ThingsGateway.Foundation.OpcUa\ThingsGateway.Foundation.OpcUa.csproj" />
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Server" Version="1.5.376.235" GeneratePathProperty="true">
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Server" Version="1.5.376.244" GeneratePathProperty="true">
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</PackageReference>
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.5.376.235" GeneratePathProperty="true">
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.5.376.244" GeneratePathProperty="true">
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</PackageReference>
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client" Version="1.5.376.235" GeneratePathProperty="true">
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client" Version="1.5.376.244" GeneratePathProperty="true">
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</PackageReference>
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Core" Version="1.5.376.235" GeneratePathProperty="true">
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Core" Version="1.5.376.244" GeneratePathProperty="true">
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</PackageReference>
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Configuration" Version="1.5.376.235" GeneratePathProperty="true">
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Configuration" Version="1.5.376.244" GeneratePathProperty="true">
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</PackageReference>
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Security.Certificates" Version="1.5.376.235" GeneratePathProperty="true">
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Security.Certificates" Version="1.5.376.244" GeneratePathProperty="true">
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</PackageReference>

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

@@ -15,6 +15,7 @@ using System.Collections.Concurrent;
using ThingsGateway.Debug;
using ThingsGateway.Foundation.SiemensS7;
using ThingsGateway.Gateway.Application;
using ThingsGateway.NewLife.Json.Extension;
using TouchSocket.Core;
using TouchSocket.Sockets;
@@ -59,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;
@@ -94,7 +96,7 @@ public class SiemensS7Master : CollectFoundationBase
protected override async ValueTask<Dictionary<string, OperResult>> WriteValuesAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
{
using var writeLock = ReadWriteLock.WriterLock();
using var writeLock = await ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false);
// 检查协议是否为空,如果为空则抛出异常
if (FoundationDevice == null)
@@ -133,6 +135,9 @@ public class SiemensS7Master : CollectFoundationBase
operResults.TryAdd(writeInfo.Key.Name, r1);
}
}
}
// 使用并发方式遍历写入信息列表,并进行异步写入操作
@@ -153,7 +158,8 @@ public class SiemensS7Master : CollectFoundationBase
}).ConfigureAwait(false);
await Check(writeInfoLists, operResults, cancellationToken).ConfigureAwait(false);
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug)
LogMessage?.Debug(string.Format("Write result: {0} - {1}", DeviceName, operResults.Select(a => $"{a.Key} - {a.Key.Length} - {(a.Value.IsSuccess ? "Success" : a.Value.ErrorMessage)}").ToSystemTextJsonString(false)));
// 返回包含操作结果的字典
return new Dictionary<string, OperResult>(operResults);
}

View File

@@ -215,7 +215,7 @@
// }
// /// <summary>
// /// 写入变量实现设备写入操作注意执行写锁using var writeLock = ReadWriteLock.WriterLock();
// /// 写入变量,实现设备写入操作,注意执行写锁, using var writeLock =await ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false);
// /// </summary>
// protected override ValueTask<Dictionary<string, OperResult>> WriteValuesAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
// {