mirror of
				https://gitee.com/ThingsGateway/ThingsGateway.git
				synced 2025-10-31 23:53:58 +08:00 
			
		
		
		
	Compare commits
	
		
			14 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | d1248811fd | ||
|   | 022d016e8e | ||
|   | f73245e650 | ||
|   | 484461fa05 | ||
|   | 7e0b7aff2a | ||
|   | 6c450dcb09 | ||
|   | 227f44283f | ||
|   | 74f6e79625 | ||
|   | cec43e2ce8 | ||
|   | 7553b258bb | ||
|   | 8bdbdc117e | ||
|   | 0e206be296 | ||
|   | 00b7353433 | ||
|   | 44e7a83593 | 
| @@ -32,7 +32,7 @@ public class TimeTick | ||||
|     /// <summary> | ||||
|     /// 上次触发时间 | ||||
|     /// </summary> | ||||
|     public DateTime LastTime { get; private set; } = DateTime.Now; | ||||
|     public DateTime LastTime { get; private set; } = DateTime.UtcNow; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 是否触发时间刻度 | ||||
| @@ -62,7 +62,7 @@ public class TimeTick | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public DateTime GetNextTime(DateTime currentTime, bool setLastTime = true) | ||||
|     public DateTime GetNextTime(DateTime currentTime, bool setLastTime = false) | ||||
|     { | ||||
|         // 在没有 Cron 表达式的情况下,使用固定间隔 | ||||
|         if (cron == null) | ||||
| @@ -86,7 +86,7 @@ public class TimeTick | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public DateTime GetNextTime(bool setLastTime = true) => GetNextTime(DateTime.UtcNow, setLastTime); | ||||
|     public DateTime GetNextTime(bool setLastTime = false) => GetNextTime(DateTime.UtcNow, setLastTime); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 是否到达设置的时间间隔 | ||||
|   | ||||
| @@ -22,12 +22,34 @@ public static class JSRuntimeExtensions | ||||
|     /// 获取文化信息 | ||||
|     /// </summary> | ||||
|     /// <param name="jsRuntime"></param> | ||||
|     public static ValueTask<string> GetCulture(this IJSRuntime jsRuntime) => jsRuntime.InvokeAsync<string>("getCultureLocalStorage"); | ||||
|     public static async ValueTask<string> GetCulture(this IJSRuntime jsRuntime) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             return await jsRuntime.InvokeAsync<string>("getCultureLocalStorage"); | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 设置文化信息 | ||||
|     /// </summary> | ||||
|     /// <param name="jsRuntime"></param> | ||||
|     /// <param name="cultureName"></param> | ||||
|     public static ValueTask SetCulture(this IJSRuntime jsRuntime, string cultureName) => jsRuntime.InvokeVoidAsync("setCultureLocalStorage", cultureName); | ||||
|     public static async ValueTask SetCulture(this IJSRuntime jsRuntime, string cultureName) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await jsRuntime.InvokeVoidAsync("setCultureLocalStorage", cultureName); | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|  | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
| 	</PropertyGroup> | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="BootstrapBlazor.FontAwesome" Version="9.0.2" /> | ||||
| 		<PackageReference Include="BootstrapBlazor" Version="9.5.10" /> | ||||
| 		<PackageReference Include="BootstrapBlazor" Version="9.6.0" /> | ||||
| 		<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| <Project> | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<PluginVersion>10.5.3</PluginVersion> | ||||
| 		<ProPluginVersion>10.5.3</ProPluginVersion> | ||||
| 		<PluginVersion>10.5.11</PluginVersion> | ||||
| 		<ProPluginVersion>10.5.11</ProPluginVersion> | ||||
| 		<AuthenticationVersion>2.1.7</AuthenticationVersion> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="CS-Script" Version="4.9.5" /> | ||||
| 		<PackageReference Include="CS-Script" Version="4.9.6" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
|   | ||||
| @@ -94,11 +94,11 @@ public class DDPUdpSessionChannel : UdpSessionChannel, IClientChannel, IDtuUdpSe | ||||
|         return WaitLocks.GetOrAdd(key, (a) => new WaitLock(WaitLock.MaxCount)); | ||||
|     } | ||||
|  | ||||
|     public override Task StopAsync() | ||||
|     public override Task<Result> StopAsync(CancellationToken token) | ||||
|     { | ||||
|         WaitLocks.ForEach(a => a.Value.SafeDispose()); | ||||
|         WaitLocks.Clear(); | ||||
|         return base.StopAsync(); | ||||
|         return base.StopAsync(token); | ||||
|     } | ||||
|  | ||||
|     private ConcurrentDictionary<EndPoint, WaitLock> _waitLocks = new(); | ||||
|   | ||||
| @@ -128,17 +128,17 @@ public class OtherChannel : SetupConfigObject, IClientChannel | ||||
|  | ||||
|     public Protocol Protocol => new Protocol("Other"); | ||||
|  | ||||
|     public DateTime LastReceivedTime { get; private set; } | ||||
|     public DateTimeOffset LastReceivedTime { get; private set; } | ||||
|  | ||||
|     public DateTime LastSentTime { get; private set; } | ||||
|     public DateTimeOffset LastSentTime { get; private set; } | ||||
|  | ||||
|     public bool IsClient => true; | ||||
|  | ||||
|     public bool Online => true; | ||||
|  | ||||
|     public Task CloseAsync(string msg) | ||||
|     public Task<Result> CloseAsync(string msg, CancellationToken token) | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|         return Task.FromResult(Result.Success); | ||||
|     } | ||||
|  | ||||
|     public Task ConnectAsync(int millisecondsTimeout, CancellationToken token) | ||||
|   | ||||
| @@ -80,7 +80,7 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin | ||||
|             { | ||||
|                 if (HeartbeatByte.SequenceEqual(e.ByteBlock.AsSegment(0, len))) | ||||
|                 { | ||||
|                     if (DateTime.UtcNow - socket.LastSentTime.ToUniversalTime() < TimeSpan.FromMilliseconds(200)) | ||||
|                     if (DateTimeOffset.Now - socket.LastSentTime < TimeSpan.FromMilliseconds(200)) | ||||
|                     { | ||||
|                         await Task.Delay(200).ConfigureAwait(false); | ||||
|                     } | ||||
|   | ||||
| @@ -81,7 +81,7 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi | ||||
|  | ||||
|                          try | ||||
|                          { | ||||
|                              if (DateTime.UtcNow - tcpClient.LastSentTime.ToUniversalTime() < TimeSpan.FromMilliseconds(200)) | ||||
|                              if (DateTimeOffset.Now - tcpClient.LastSentTime < TimeSpan.FromMilliseconds(200)) | ||||
|                              { | ||||
|                                  await Task.Delay(200).ConfigureAwait(false); | ||||
|                              } | ||||
|   | ||||
| @@ -76,7 +76,7 @@ public static class PluginUtil | ||||
|         .SetOnClose(async (c, t) => | ||||
|         { | ||||
|             await c.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false); | ||||
|             c.SafeClose($"{channelOptions.CheckClearTime}ms Timeout"); | ||||
|             await c.CloseAsync($"{channelOptions.CheckClearTime}ms Timeout").ConfigureAwait(false); | ||||
|         }); | ||||
|             }; | ||||
|  | ||||
|   | ||||
| @@ -74,8 +74,9 @@ public class SerialPortChannel : SerialPortClient, IClientChannel | ||||
|  | ||||
|     //private readonly WaitLock _connectLock = new WaitLock(); | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task CloseAsync(string msg) | ||||
|     public override async Task<Result> CloseAsync(string msg, CancellationToken token) | ||||
|     { | ||||
|  | ||||
|         if (Online) | ||||
|         { | ||||
|             try | ||||
| @@ -83,11 +84,12 @@ public class SerialPortChannel : SerialPortClient, IClientChannel | ||||
|                 //await _connectLock.WaitAsync().ConfigureAwait(false); | ||||
|                 if (Online) | ||||
|                 { | ||||
|                     await base.CloseAsync(msg).ConfigureAwait(false); | ||||
|                     var result = await base.CloseAsync(msg, token).ConfigureAwait(false); | ||||
|                     if (!Online) | ||||
|                     { | ||||
|                         await this.OnChannelEvent(Stoped).ConfigureAwait(false); | ||||
|                     } | ||||
|                     return result; | ||||
|                 } | ||||
|             } | ||||
|             finally | ||||
| @@ -95,6 +97,7 @@ public class SerialPortChannel : SerialPortClient, IClientChannel | ||||
|                 //_connectLock.Release(); | ||||
|             } | ||||
|         } | ||||
|         return Result.Success; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|   | ||||
| @@ -72,7 +72,7 @@ public class TcpClientChannel : TcpClient, IClientChannel | ||||
|  | ||||
|     //private readonly WaitLock _connectLock = new WaitLock(); | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task CloseAsync(string msg) | ||||
|     public override async Task<Result> CloseAsync(string msg, CancellationToken token) | ||||
|     { | ||||
|         if (Online) | ||||
|         { | ||||
| @@ -81,11 +81,12 @@ public class TcpClientChannel : TcpClient, IClientChannel | ||||
|                 //await _connectLock.WaitAsync().ConfigureAwait(false); | ||||
|                 if (Online) | ||||
|                 { | ||||
|                     await base.CloseAsync(msg).ConfigureAwait(false); | ||||
|                     var result = await base.CloseAsync(msg, token).ConfigureAwait(false); | ||||
|                     if (!Online) | ||||
|                     { | ||||
|                         await this.OnChannelEvent(Stoped).ConfigureAwait(false); | ||||
|                     } | ||||
|                     return result; | ||||
|                 } | ||||
|             } | ||||
|             finally | ||||
| @@ -93,6 +94,7 @@ public class TcpClientChannel : TcpClient, IClientChannel | ||||
|                 //_connectLock.Release(); | ||||
|             } | ||||
|         } | ||||
|         return Result.Success; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|   | ||||
| @@ -94,22 +94,22 @@ public abstract class TcpServiceChannelBase<TClient> : TcpService<TClient>, ITcp | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task StopAsync() | ||||
|     public override async Task<Result> StopAsync(CancellationToken token) | ||||
|     { | ||||
|         if (Monitors.Any()) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await _connectLock.WaitAsync().ConfigureAwait(false); | ||||
|                 await _connectLock.WaitAsync(token).ConfigureAwait(false); | ||||
|                 if (Monitors.Any()) | ||||
|                 { | ||||
|  | ||||
|                     await ClearAsync().ConfigureAwait(false); | ||||
|                     var iPHost = Monitors.FirstOrDefault()?.Option.IpHost; | ||||
|                     await base.StopAsync().ConfigureAwait(false); | ||||
|                     var result = await base.StopAsync(token).ConfigureAwait(false); | ||||
|                     if (!Monitors.Any()) | ||||
|                         Logger?.Info($"{iPHost}{DefaultResource.Localizer["ServiceStoped"]}"); | ||||
|  | ||||
|                     return result; | ||||
|                 } | ||||
|             } | ||||
|             finally | ||||
| @@ -120,8 +120,10 @@ public abstract class TcpServiceChannelBase<TClient> : TcpService<TClient>, ITcp | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             await base.StopAsync().ConfigureAwait(false); | ||||
|             var result = await base.StopAsync(token).ConfigureAwait(false); | ||||
|             return result; | ||||
|         } | ||||
|         return Result.Success; ; | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -192,9 +194,9 @@ public class TcpServiceChannel<TClient> : TcpServiceChannelBase<TClient>, IChann | ||||
|     public ChannelEventHandler Stoping { get; set; } = new(); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public Task CloseAsync(string msg) | ||||
|     public Task<Result> CloseAsync(string msg, CancellationToken token) | ||||
|     { | ||||
|         return StopAsync(); | ||||
|         return StopAsync(token); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|   | ||||
| @@ -64,10 +64,10 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel | ||||
|     public virtual WaitLock GetLock(string key) => WaitLock; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task CloseAsync(string msg) | ||||
|     public override Task<Result> CloseAsync(string msg, CancellationToken token) | ||||
|     { | ||||
|         WaitHandlePool.SafeDispose(); | ||||
|         return base.CloseAsync(msg); | ||||
|         return base.CloseAsync(msg, token); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|   | ||||
| @@ -73,9 +73,9 @@ public class UdpSessionChannel : UdpSession, IClientChannel | ||||
|     public ConcurrentDictionary<long, Func<IClientChannel, ReceivedDataEventArgs, bool, Task>> ChannelReceivedWaitDict { get; } = new(); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public Task CloseAsync(string msg) | ||||
|     public Task<Result> CloseAsync(string msg, CancellationToken token) | ||||
|     { | ||||
|         return StopAsync(); | ||||
|         return StopAsync(token); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
| @@ -127,26 +127,28 @@ public class UdpSessionChannel : UdpSession, IClientChannel | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task StopAsync() | ||||
|     public override async Task<Result> StopAsync(CancellationToken token) | ||||
|     { | ||||
|         if (Monitor != null) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await _connectLock.WaitAsync().ConfigureAwait(false); | ||||
|                 await _connectLock.WaitAsync(token).ConfigureAwait(false); | ||||
|                 if (Monitor != null) | ||||
|                 { | ||||
|                     await this.OnChannelEvent(Stoping).ConfigureAwait(false); | ||||
|                     await base.StopAsync().ConfigureAwait(false); | ||||
|                     var result = await base.StopAsync(token).ConfigureAwait(false); | ||||
|                     if (Monitor == null) | ||||
|                     { | ||||
|                         await this.OnChannelEvent(Stoped).ConfigureAwait(false); | ||||
|                         Logger?.Info($"{DefaultResource.Localizer["ServiceStoped"]}"); | ||||
|                     } | ||||
|                     return result; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     await base.StopAsync().ConfigureAwait(false); | ||||
|                     var result = await base.StopAsync(token).ConfigureAwait(false); | ||||
|                     return result; | ||||
|                 } | ||||
|             } | ||||
|             finally | ||||
| @@ -156,7 +158,8 @@ public class UdpSessionChannel : UdpSession, IClientChannel | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             await base.StopAsync().ConfigureAwait(false); | ||||
|             var result = await base.StopAsync(token).ConfigureAwait(false); | ||||
|             return result; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -46,9 +46,9 @@ public abstract class DeviceBase : DisposableObject, IDevice | ||||
|                 return; | ||||
|             if (channel.Collects.Count > 0) | ||||
|             { | ||||
|                 var device = channel.Collects.First(); | ||||
|                 if (device.GetType() != GetType()) | ||||
|                     throw new InvalidOperationException("The channel already exists in the device of another type"); | ||||
|                 //var device = channel.Collects.First(); | ||||
|                 //if (device.GetType() != GetType()) | ||||
|                 //    throw new InvalidOperationException("The channel already exists in the device of another type"); | ||||
|  | ||||
|                 if (!SupportMultipleDevice()) | ||||
|                     throw new InvalidOperationException("The proactive response device does not support multiple devices"); | ||||
| @@ -97,9 +97,17 @@ public abstract class DeviceBase : DisposableObject, IDevice | ||||
|             Channel.Stoping.Add(ChannelStoping); | ||||
|             Channel.Started.Add(ChannelStarted); | ||||
|             Channel.ChannelReceived.Add(ChannelReceived); | ||||
|  | ||||
|  | ||||
|             SetChannel(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected virtual void SetChannel() | ||||
|     { | ||||
|         Channel.ChannelOptions.MaxConcurrentCount = 1; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     ~DeviceBase() | ||||
|     { | ||||
| @@ -221,6 +229,14 @@ public abstract class DeviceBase : DisposableObject, IDevice | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (Channel?.Collects?.Count > 1) | ||||
|             { | ||||
|                 var dataHandlingAdapter = GetDataAdapter(); | ||||
|                 if (adapter.GetType() != dataHandlingAdapter.GetType()) | ||||
|                 { | ||||
|                     clientChannel.SetDataHandlingAdapter(dataHandlingAdapter); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -944,7 +960,7 @@ public abstract class DeviceBase : DisposableObject, IDevice | ||||
|                         if (tcpServiceChannel.TryGetClient($"ID={dtu.DtuId}", out var client)) | ||||
|                         { | ||||
|                             client.WaitHandlePool?.SafeDispose(); | ||||
|                             client.SafeClose(); | ||||
|                             client.Close(); | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
|   | ||||
| @@ -10,8 +10,8 @@ | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="9.0.4" /> | ||||
| 		<PackageReference Include="TouchSocket" Version="3.0.26" /> | ||||
| 		<PackageReference Include="TouchSocket.SerialPorts" Version="3.0.26" /> | ||||
| 		<PackageReference Include="TouchSocket" Version="3.1.1" /> | ||||
| 		<PackageReference Include="TouchSocket.SerialPorts" Version="3.1.1" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
|   | ||||
| @@ -87,12 +87,12 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel | ||||
|         // 触发一次设备状态变化和变量值变化事件 | ||||
|         CollectDevices?.ForEach(a => | ||||
|         { | ||||
|             if (a.Value.DeviceStatus == DeviceStatusEnum.OnLine) | ||||
|             if (a.Value.DeviceStatus == DeviceStatusEnum.OnLine && _businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval) | ||||
|                 DeviceStatusChange(a.Value, a.Value.Adapt<DeviceBasicData>()); | ||||
|         }); | ||||
|         IdVariableRuntimes.ForEach(a => | ||||
|         { | ||||
|             if (a.Value.IsOnline) | ||||
|             if (a.Value.IsOnline && _businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval) | ||||
|                 VariableValueChange(a.Value, a.Value.Adapt<VariableBasicData>()); | ||||
|         }); | ||||
|     } | ||||
|   | ||||
| @@ -85,12 +85,12 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode | ||||
|  | ||||
|         CollectDevices?.ForEach(a => | ||||
|         { | ||||
|             if (a.Value.DeviceStatus == DeviceStatusEnum.OnLine) | ||||
|             if (a.Value.DeviceStatus == DeviceStatusEnum.OnLine && _businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval) | ||||
|                 DeviceStatusChange(a.Value, a.Value.Adapt<DeviceBasicData>()); | ||||
|         }); | ||||
|         IdVariableRuntimes.ForEach(a => | ||||
|         { | ||||
|             if (a.Value.IsOnline) | ||||
|             if (a.Value.IsOnline && _businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval) | ||||
|                 VariableValueChange(a.Value, a.Value.Adapt<VariableBasicData>()); | ||||
|         }); | ||||
|     } | ||||
|   | ||||
| @@ -73,7 +73,7 @@ public abstract class BusinessBaseWithCacheIntervalVariableModel<T> : BusinessBa | ||||
|         // 触发一次变量值变化事件 | ||||
|         IdVariableRuntimes.ForEach(a => | ||||
|         { | ||||
|             if (a.Value.IsOnline) | ||||
|             if (a.Value.IsOnline && _businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval) | ||||
|                 VariableValueChange(a.Value, a.Value.Adapt<VariableBasicData>()); | ||||
|         }); | ||||
|     } | ||||
|   | ||||
| @@ -8,8 +8,6 @@ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using System.Text; | ||||
|  | ||||
| namespace ThingsGateway.Gateway.Application; | ||||
|  | ||||
| public struct TopicArray | ||||
|   | ||||
| @@ -140,7 +140,7 @@ public class Channel : ChannelOptionsBase, IPrimaryIdEntity, IBaseDataEntity, IB | ||||
|     /// <summary> | ||||
|     /// 缓存超时 | ||||
|     /// </summary> | ||||
|     [SugarColumn(ColumnDescription = "缓存超时")] | ||||
|     [SugarColumn(ColumnDescription = "缓存超时", IsNullable = true, DefaultValue = "500")] | ||||
|     [AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)] | ||||
|     [MinValue(100)] | ||||
|     public override int CacheTimeout { get; set; } = 500; | ||||
| @@ -148,7 +148,7 @@ public class Channel : ChannelOptionsBase, IPrimaryIdEntity, IBaseDataEntity, IB | ||||
|     /// <summary> | ||||
|     /// 连接超时 | ||||
|     /// </summary> | ||||
|     [SugarColumn(ColumnDescription = "连接超时")] | ||||
|     [SugarColumn(ColumnDescription = "连接超时", IsNullable = true, DefaultValue = "3000")] | ||||
|     [AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)] | ||||
|     [MinValue(100)] | ||||
|     public override ushort ConnectTimeout { get; set; } = 3000; | ||||
| @@ -156,33 +156,36 @@ public class Channel : ChannelOptionsBase, IPrimaryIdEntity, IBaseDataEntity, IB | ||||
|     /// <summary> | ||||
|     /// 最大并发数 | ||||
|     /// </summary> | ||||
|     [SugarColumn(ColumnDescription = "最大并发数")] | ||||
|     [SugarColumn(ColumnDescription = "最大并发数", IsNullable = true, DefaultValue = "1")] | ||||
|     [AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)] | ||||
|     [MinValue(1)] | ||||
|     public override int MaxConcurrentCount { get; set; } = 1; | ||||
|  | ||||
|     [SugarColumn(ColumnDescription = "最大连接数")] | ||||
|     [SugarColumn(ColumnDescription = "最大连接数", IsNullable = true, DefaultValue = "10000")] | ||||
|     [AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)] | ||||
|     public override int MaxClientCount { get; set; } = 10000; | ||||
|     [SugarColumn(ColumnDescription = "客户端滑动过期时间")] | ||||
|  | ||||
|     [SugarColumn(ColumnDescription = "客户端滑动过期时间", IsNullable = true, DefaultValue = "120000")] | ||||
|     [AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)] | ||||
|     public override int CheckClearTime { get; set; } = 120000; | ||||
|  | ||||
|     [SugarColumn(ColumnDescription = "心跳内容", IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)] | ||||
|     public override string Heartbeat { get; set; } = "Heartbeat"; | ||||
|  | ||||
|     #region dtu终端 | ||||
|  | ||||
|     [SugarColumn(ColumnDescription = "心跳间隔")] | ||||
|     [SugarColumn(ColumnDescription = "心跳间隔", IsNullable = true, DefaultValue = "60000")] | ||||
|     [AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)] | ||||
|     public override int HeartbeatTime { get; set; } = 60000; | ||||
|  | ||||
|     [SugarColumn(ColumnDescription = "DtuId", IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)] | ||||
|     public override string DtuId { get; set; } | ||||
|  | ||||
|     #endregion | ||||
|  | ||||
|     [SugarColumn(ColumnDescription = "Dtu类型")] | ||||
|     [SugarColumn(ColumnDescription = "Dtu类型", IsNullable = true, DefaultValue = "0")] | ||||
|     [AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)] | ||||
|     public override DtuSeviceType DtuSeviceType { get; set; } | ||||
|  | ||||
|   | ||||
| @@ -51,6 +51,13 @@ public class Variable : BaseDataEntity, IValidatableObject | ||||
|     [Required] | ||||
|     public virtual string Name { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 采集组 | ||||
|     /// </summary> | ||||
|     [SugarColumn(ColumnDescription = "采集组", IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 1)] | ||||
|     public virtual string CollectGroup { get; set; } = string.Empty; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 分组名称 | ||||
|     /// </summary> | ||||
|   | ||||
| @@ -97,6 +97,7 @@ | ||||
|     "Name": "Name", | ||||
|     "Description": "Description", | ||||
|     "Group": "Group", | ||||
|     "CollectGroup": "CollectGroup", | ||||
|     "DeviceId": "CollectionDevice", | ||||
|     "DeviceId.MinValue": "{0} cannot be empty", | ||||
|     "DeviceId.Required": "{0} cannot be empty", | ||||
| @@ -452,6 +453,7 @@ | ||||
|     "Name.Required": "{0} cannot be empty", | ||||
|     "Description": "Description", | ||||
|     "Group": "Group", | ||||
|     "CollectGroup": "CollectGroup", | ||||
|     "DeviceId": "CollectionDevice", | ||||
|     "DeviceId.MinValue": "{0} cannot be empty", | ||||
|     "DeviceId.Required": "{0} cannot be empty", | ||||
|   | ||||
| @@ -89,7 +89,8 @@ | ||||
|     "Name": "名称", | ||||
|     "Name.Required": " {0} 不可为空", | ||||
|     "Description": "描述", | ||||
|     "Group": "分组", | ||||
|     "Group": "业务组", | ||||
|     "CollectGroup": "采集组", | ||||
|     "DeviceId": "采集设备", | ||||
|     "DeviceId.MinValue": " {0} 不可为空", | ||||
|     "DeviceId.Required": " {0} 不可为空", | ||||
| @@ -486,7 +487,8 @@ | ||||
|     "Name": "名称", | ||||
|     "Name.Required": " {0} 不可为空", | ||||
|     "Description": "描述", | ||||
|     "Group": "分组", | ||||
|     "Group": "业务组", | ||||
|     "CollectGroup": "采集组", | ||||
|     "DeviceId": "采集设备", | ||||
|     "DeviceId.MinValue": " {0} 不可为空", | ||||
|     "DeviceId.Required": " {0} 不可为空", | ||||
|   | ||||
| @@ -131,7 +131,7 @@ public class ChannelRuntime : Channel, IChannelOptions, IDisposable | ||||
|  | ||||
|     public void Dispose() | ||||
|     { | ||||
|         Config?.SafeDispose(); | ||||
|         //Config?.SafeDispose(); | ||||
|  | ||||
|         GlobalData.Channels.TryRemove(Id, out _); | ||||
|         DeviceThreadManage = null; | ||||
| @@ -146,4 +146,57 @@ public class ChannelRuntime : Channel, IChannelOptions, IDisposable | ||||
|         return $"{Name}[{base.ToString()}]"; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public IChannel GetChannel(TouchSocketConfig config) | ||||
|     { | ||||
|         lock (GlobalData.Channels) | ||||
|         { | ||||
|  | ||||
|             if (DeviceThreadManage?.Channel?.DisposedValue == false) | ||||
|                 return DeviceThreadManage?.Channel; | ||||
|  | ||||
|  | ||||
|             if (ChannelType == ChannelTypeEnum.TcpService | ||||
|                 || ChannelType == ChannelTypeEnum.SerialPort | ||||
|                 || ChannelType == ChannelTypeEnum.UdpSession | ||||
|                 ) | ||||
|             { | ||||
|                 //获取相同配置的Tcp服务或Udp服务或COM | ||||
|                 var same = GlobalData.Channels.FirstOrDefault(a => | ||||
|                  { | ||||
|                      if (a.Value == this) | ||||
|                          return false; | ||||
|                      if (a.Value.DeviceThreadManage?.Channel?.DisposedValue == true || a.Value.DeviceThreadManage?.Channel?.DisposedValue == null) | ||||
|                          return false; | ||||
|  | ||||
|                      if (a.Value.ChannelType == ChannelType) | ||||
|                      { | ||||
|                          if (a.Value.ChannelType == ChannelTypeEnum.TcpService) | ||||
|                              if (a.Value.BindUrl == BindUrl) | ||||
|                                  return true; | ||||
|                          if (a.Value.ChannelType == ChannelTypeEnum.UdpSession) | ||||
|                              if ((!BindUrl.IsNullOrWhiteSpace()) && a.Value.BindUrl == BindUrl) | ||||
|                                  return true; | ||||
|                          if (a.Value.ChannelType == ChannelTypeEnum.SerialPort) | ||||
|                              if (a.Value.PortName == PortName) | ||||
|                                  return true; | ||||
|                      } | ||||
|                      return false; | ||||
|                  }).Value; | ||||
|  | ||||
|                 if (same != null) | ||||
|                 { | ||||
|                     return same.GetChannel(config); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (DeviceThreadManage?.Channel?.DisposedValue == false) | ||||
|                 return DeviceThreadManage?.Channel; | ||||
|  | ||||
|             var ichannel = config.GetChannel(this); | ||||
|             return ichannel; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -26,7 +26,7 @@ namespace ThingsGateway.Gateway.Application; | ||||
| public class DeviceRuntime : Device, IDisposable | ||||
| { | ||||
|     protected volatile DeviceStatusEnum _deviceStatus = DeviceStatusEnum.Default; | ||||
|  | ||||
|      | ||||
|     private string? _lastErrorMessage; | ||||
|  | ||||
|     /// <summary> | ||||
|   | ||||
| @@ -19,5 +19,8 @@ public class VariableMapper : IRegister | ||||
|     { | ||||
|         config.ForType<Variable, VariableRuntime>() | ||||
|         .Map(dest => dest.Value, src => src.InitValue); | ||||
|  | ||||
|         config.ForType<VariableRuntime, VariableRuntime>() | ||||
| .Ignore(dest => dest.DeviceRuntime); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -16,8 +16,6 @@ using System.Collections.Concurrent; | ||||
|  | ||||
| using ThingsGateway.NewLife; | ||||
|  | ||||
| using TouchSocket.Core; | ||||
|  | ||||
| namespace ThingsGateway.Gateway.Application; | ||||
|  | ||||
| internal sealed class ChannelThreadManage : IChannelThreadManage | ||||
| @@ -115,8 +113,6 @@ internal sealed class ChannelThreadManage : IChannelThreadManage | ||||
|     { | ||||
|         await PrivateRemoveChannelsAsync(channelRuntimes.Select(a => a.Id)).ConfigureAwait(false); | ||||
|  | ||||
|         BytePool.Default.Clear(); | ||||
|  | ||||
|         await channelRuntimes.ParallelForEachAsync(async (channelRuntime, token) => | ||||
|         { | ||||
|             try | ||||
|   | ||||
| @@ -117,10 +117,9 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage | ||||
|         // 添加默认日志记录器 | ||||
|         LogMessage.AddLogger(new EasyLogger(logger.Log_Out) { LogLevel = TouchSocket.Core.LogLevel.Trace }); | ||||
|  | ||||
|         var ichannel = config.GetChannel(channelRuntime); | ||||
|  | ||||
|         // 根据配置获取通道实例 | ||||
|         Channel = ichannel; | ||||
|         Channel = channelRuntime.GetChannel(config); | ||||
|  | ||||
|  | ||||
|         //初始设置输出文本日志 | ||||
|         SetLog(CurrentChannel.LogLevel); | ||||
| @@ -134,7 +133,7 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage | ||||
|         CancellationToken = CancellationTokenSource.Token; | ||||
|  | ||||
|         CancellationToken.Register(() => GlobalData.DeviceStatusChangeEvent -= GlobalData_DeviceStatusChangeEvent); | ||||
|       | ||||
|  | ||||
|         _ = Task.Run(() => CheckThreadAsync(CancellationToken)); | ||||
|         _ = Task.Run(() => CheckRedundantAsync(CancellationToken)); | ||||
|     } | ||||
| @@ -901,7 +900,8 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage | ||||
|             await NewDeviceLock.WaitAsync().ConfigureAwait(false); | ||||
|  | ||||
|             await PrivateRemoveDevicesAsync(Drivers.Keys).ConfigureAwait(false); | ||||
|             Channel?.SafeDispose(); | ||||
|             if (Channel?.Collects.Count == 0) | ||||
|                 Channel?.SafeDispose(); | ||||
|  | ||||
|             LogMessage?.LogInformation(Localizer["ChannelDispose", CurrentChannel?.Name ?? string.Empty]); | ||||
|  | ||||
|   | ||||
| @@ -76,6 +76,7 @@ public class Startup : AppStartup | ||||
|             if (configId.Count() > 1) throw new($"Sqlsugar connect configId: {configId.Key} Duplicate!"); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         //遍历配置 | ||||
|         DbContext.DbConfigs?.ForEach(it => | ||||
|         { | ||||
| @@ -84,6 +85,20 @@ public class Startup : AppStartup | ||||
|             if (it.InitTable == true) | ||||
|                 connection.DbMaintenance.CreateDatabase();//创建数据库,如果存在则不创建 | ||||
|         }); | ||||
|  | ||||
|  | ||||
|         //兼容变量名称唯一键处理 | ||||
|         try | ||||
|         { | ||||
|             using var db = DbContext.GetDB<Variable>(); | ||||
|             if (db.DbMaintenance.IsAnyIndex("unique_variable_name")) | ||||
|             { | ||||
|                 var tables = db.DbMaintenance.DropIndex("unique_variable_name"); | ||||
|             } | ||||
|         } | ||||
|         catch { } | ||||
|  | ||||
|  | ||||
|         var fullName = Assembly.GetExecutingAssembly().FullName;//获取程序集全名 | ||||
|         CodeFirstUtils.CodeFirst(fullName!);//CodeFirst | ||||
|  | ||||
| @@ -109,6 +124,9 @@ public class Startup : AppStartup | ||||
|         } | ||||
|         catch { } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|         serviceProvider.GetService<IHostApplicationLifetime>().ApplicationStarted.Register(() => | ||||
|         { | ||||
|             serviceProvider.GetService<ILoggerFactory>().CreateLogger(nameof(ThingsGateway)).LogInformation("ThingsGateway is started..."); | ||||
|   | ||||
| @@ -8,8 +8,8 @@ | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" /> | ||||
| 		<PackageReference Include="Rougamo.Fody" Version="5.0.0" /> | ||||
| 		<PackageReference Include="TouchSocket.Dmtp" Version="3.0.26" /> | ||||
| 		<PackageReference Include="TouchSocket.WebApi.Swagger" Version="3.0.26" /> | ||||
| 		<PackageReference Include="TouchSocket.Dmtp" Version="3.1.1" /> | ||||
| 		<PackageReference Include="TouchSocket.WebApi.Swagger" Version="3.1.1" /> | ||||
| 		<PackageReference Include="ThingsGateway.Authentication" Version="$(AuthenticationVersion)" /> | ||||
|  | ||||
| 	</ItemGroup> | ||||
|   | ||||
| @@ -105,8 +105,8 @@ public class TcpSessionClientDto | ||||
|     public string PluginInfos { get; set; } | ||||
|  | ||||
|     [AutoGenerateColumn(Searchable = true, Filterable = true, Sortable = true)] | ||||
|     public DateTime LastReceivedTime { get; set; } | ||||
|     public DateTimeOffset LastReceivedTime { get; set; } | ||||
|  | ||||
|     [AutoGenerateColumn(Searchable = true, Filterable = true, Sortable = true)] | ||||
|     public DateTime LastSentTime { get; set; } | ||||
|     public DateTimeOffset LastSentTime { get; set; } | ||||
| } | ||||
|   | ||||
| @@ -1299,7 +1299,7 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e => | ||||
|             try | ||||
|             { | ||||
|                 if (Disposed) return; | ||||
|                 await Task.Delay(2000); | ||||
|                 await Task.Delay(1000); | ||||
|                 await OnClickSearch(SearchText); | ||||
|  | ||||
|                 Value = GetValue(Value); | ||||
|   | ||||
| @@ -44,6 +44,7 @@ | ||||
|                     <EditorItem @bind-Field="@context.Name" Readonly=BatchEditEnable /> | ||||
|  | ||||
|                     <EditorItem @bind-Field="@context.Description" /> | ||||
|                     <EditorItem @bind-Field="@context.CollectGroup" /> | ||||
|                     <EditorItem @bind-Field="@context.Group" /> | ||||
|  | ||||
|                     <EditorItem @bind-Field="@context.Unit" /> | ||||
|   | ||||
| @@ -212,8 +212,8 @@ public partial class VariableEditComponent | ||||
|                 { | ||||
|                     var component = new BootstrapDynamicComponent(data.VariablePropertyUIType, new Dictionary<string, object?> | ||||
|                     { | ||||
|                         [nameof(VariableEditComponent.Model)] = Model, | ||||
|                         [nameof(DeviceEditComponent.PluginPropertyEditorItems)] = data.EditorItems, | ||||
|                         [nameof(IPropertyUIBase.Model)] = Model, | ||||
|                         [nameof(IPropertyUIBase.PluginPropertyEditorItems)] = data.EditorItems, | ||||
|                     }); | ||||
|                     VariablePropertyRenderFragments.AddOrUpdate(id, component.Render()); | ||||
|                 } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
| 	</PropertyGroup> | ||||
| 	<ItemGroup> | ||||
| 		<ProjectReference Include="..\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj" /> | ||||
| 		<PackageReference Include="BootstrapBlazor.UniverSheet" Version="9.0.3" /> | ||||
| 		<PackageReference Include="BootstrapBlazor.UniverSheet" Version="9.0.4" /> | ||||
| 		<PackageReference Include="BootstrapBlazor.WinBox" Version="9.0.7" /> | ||||
| 		<PackageReference Include="BootstrapBlazor.CodeEditor" Version="9.0.1" /> | ||||
| 		<ProjectReference Include="..\..\Admin\ThingsGateway.Admin.Razor\ThingsGateway.Admin.Razor.csproj" /> | ||||
|   | ||||
| @@ -15,7 +15,7 @@ using TouchSocket.Rpc; | ||||
|  | ||||
| namespace ThingsGateway.Management; | ||||
|  | ||||
| public partial class ReverseCallbackServer : RpcServer | ||||
| public partial class ReverseCallbackServer : SingletonRpcServer | ||||
| { | ||||
|     [DmtpRpc(MethodInvoke = true)] | ||||
|     public void UpdateGatewayData(List<DeviceDataWithValue> deviceDatas) | ||||
|   | ||||
| @@ -13,7 +13,7 @@ using TouchSocket.Rpc; | ||||
|  | ||||
| namespace ThingsGateway.Management; | ||||
|  | ||||
| public partial class UpgradeRpcServer : RpcServer | ||||
| public partial class UpgradeRpcServer : SingletonRpcServer | ||||
| { | ||||
|     [DmtpRpc(MethodInvoke = true)] | ||||
|     public void Restart() | ||||
|   | ||||
| @@ -19,7 +19,7 @@ public class DeviceChangedTriggerNode : TextNode, ITriggerNode, IDisposable | ||||
|     { | ||||
|         Func = func; | ||||
|         FuncDict.Add(this, func); | ||||
|         if (!DeviceChangedTriggerNodeDict.TryGetValue(Text, out var list)) | ||||
|         if (!DeviceChangedTriggerNodeDict.TryGetValue(Text ?? string.Empty, out var list)) | ||||
|         { | ||||
|             var deviceChangedTriggerNodes = new ConcurrentList<DeviceChangedTriggerNode>(); | ||||
|             deviceChangedTriggerNodes.Add(this); | ||||
| @@ -44,7 +44,7 @@ public class DeviceChangedTriggerNode : TextNode, ITriggerNode, IDisposable | ||||
|  | ||||
|     private static void GlobalData_DeviceStatusChangeEvent(DeviceRuntime deviceRunTime, DeviceBasicData deviceData) | ||||
|     { | ||||
|         if (DeviceChangedTriggerNodeDict.TryGetValue(deviceData.Name, out var deviceChangedTriggerNodes) && deviceChangedTriggerNodes?.Count > 0) | ||||
|         if (DeviceChangedTriggerNodeDict.TryGetValue(deviceData.Name ?? string.Empty, out var deviceChangedTriggerNodes) && deviceChangedTriggerNodes?.Count > 0) | ||||
|         { | ||||
|             if (!DeviceDatas.IsAddingCompleted) | ||||
|             { | ||||
| @@ -63,7 +63,7 @@ public class DeviceChangedTriggerNode : TextNode, ITriggerNode, IDisposable | ||||
|         return DeviceDatas.GetConsumingEnumerable().ParallelForEachAsync((async (deviceDatas, token) => | ||||
|             { | ||||
|  | ||||
|                 if (DeviceChangedTriggerNodeDict.TryGetValue(deviceDatas.Name, out var valueChangedTriggerNodes)) | ||||
|                 if (DeviceChangedTriggerNodeDict.TryGetValue(deviceDatas.Name ?? string.Empty, out var valueChangedTriggerNodes)) | ||||
|                 { | ||||
|                     await valueChangedTriggerNodes.ParallelForEachAsync(async (item, token) => | ||||
|                      { | ||||
| @@ -89,7 +89,7 @@ public class DeviceChangedTriggerNode : TextNode, ITriggerNode, IDisposable | ||||
|     public void Dispose() | ||||
|     { | ||||
|         FuncDict.Remove(this); | ||||
|         if (DeviceChangedTriggerNodeDict.TryGetValue(Text, out var list)) | ||||
|         if (DeviceChangedTriggerNodeDict.TryGetValue(Text ?? string.Empty, out var list)) | ||||
|         { | ||||
|             list.Remove(this); | ||||
|         } | ||||
|   | ||||
| @@ -160,9 +160,19 @@ public class OpcDaMaster : IDisposable | ||||
|     /// <returns></returns> | ||||
|     public Dictionary<string, List<OpcItem>> AddItemsWithSave(List<string> items) | ||||
|     { | ||||
|         int i = 0; | ||||
|         ItemDicts = items.ConvertAll(o => new OpcItem(o)).ChunkTrivialBetter(OpcDaProperty.GroupSize).ToDictionary(a => "default" + (i++)); | ||||
|         return ItemDicts; | ||||
|         lock (this) | ||||
|         { | ||||
|  | ||||
|             int i = ItemDicts.Count; | ||||
|             var addItems = items.ConvertAll(o => new OpcItem(o)).ChunkTrivialBetter(OpcDaProperty.GroupSize).ToDictionary(a => "default" + (i++)); | ||||
|  | ||||
|             foreach (var item in addItems) | ||||
|             { | ||||
|                 ItemDicts.TryAdd(item.Key, item.Value); | ||||
|             } | ||||
|  | ||||
|             return addItems; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|   | ||||
| @@ -251,7 +251,7 @@ public class OpcUaMaster : IDisposable | ||||
|             DisplayName = subscriptionName | ||||
|         }; | ||||
|         List<MonitoredItem> monitoredItems = new(); | ||||
|         var variableNodes = loadType ? await ReadNodesAsync(items, cancellationToken).ConfigureAwait(false) : null; | ||||
|         var variableNodes = loadType ? await ReadNodesAsync(items, false, cancellationToken).ConfigureAwait(false) : null; | ||||
|         for (int i = 0; i < items.Length; i++) | ||||
|         { | ||||
|             try | ||||
| @@ -743,7 +743,7 @@ public class OpcUaMaster : IDisposable | ||||
|                     NodeId = new NodeId(item.Key), | ||||
|                     AttributeId = Attributes.Value, | ||||
|                 }; | ||||
|                 var variableNode = await ReadNodeAsync(item.Key, false, cancellationToken).ConfigureAwait(false); | ||||
|                 var variableNode = await ReadNodeAsync(item.Key, false, false, cancellationToken).ConfigureAwait(false); | ||||
|                 var dataValue = JsonUtils.Decode( | ||||
|                     m_session.MessageContext, | ||||
|                     variableNode.DataType, | ||||
| @@ -793,9 +793,10 @@ public class OpcUaMaster : IDisposable | ||||
|         { | ||||
|             if (m_session != null) | ||||
|             { | ||||
|                 var variableNode = ReadNode(monitoreditem.StartNodeId.ToString(), false); | ||||
|                 foreach (var value in monitoreditem.DequeueValues()) | ||||
|                 { | ||||
|                     var variableNode = ReadNode(monitoreditem.StartNodeId.ToString(), false, StatusCode.IsGood(value.StatusCode)); | ||||
|  | ||||
|                     if (value.Value != null) | ||||
|                     { | ||||
|                         var data = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value); | ||||
| @@ -974,7 +975,7 @@ public class OpcUaMaster : IDisposable | ||||
|         List<(string, DataValue, JToken)> jTokens = new(); | ||||
|         for (int i = 0; i < results.Count; i++) | ||||
|         { | ||||
|             var variableNode = await ReadNodeAsync(nodeIds[i].ToString(), false, cancellationToken).ConfigureAwait(false); | ||||
|             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 jToken = JsonUtils.Encode(m_session.MessageContext, type, results[i].Value); | ||||
|             jTokens.Add((variableNode.NodeId.ToString(), results[i], jToken)); | ||||
| @@ -985,7 +986,7 @@ public class OpcUaMaster : IDisposable | ||||
|     /// <summary> | ||||
|     /// 从服务器或缓存读取节点 | ||||
|     /// </summary> | ||||
|     private VariableNode ReadNode(string nodeIdStr, bool isOnlyServer = true) | ||||
|     private VariableNode ReadNode(string nodeIdStr, bool isOnlyServer = true, bool cache = true) | ||||
|     { | ||||
|         if (!isOnlyServer) | ||||
|         { | ||||
| @@ -1025,14 +1026,15 @@ public class OpcUaMaster : IDisposable | ||||
|  | ||||
|         VariableNode variableNode = GetVariableNodes(itemsToRead, values, diagnosticInfos, responseHeader).FirstOrDefault(); | ||||
|  | ||||
|         _variableDicts.AddOrUpdate(nodeIdStr, a => variableNode, (a, b) => variableNode); | ||||
|         if (cache) | ||||
|             _variableDicts.AddOrUpdate(nodeIdStr, a => variableNode, (a, b) => variableNode); | ||||
|         return variableNode; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 从服务器或缓存读取节点 | ||||
|     /// </summary> | ||||
|     private async Task<VariableNode> ReadNodeAsync(string nodeIdStr, bool isOnlyServer = true, CancellationToken cancellationToken = default) | ||||
|     private async Task<VariableNode> ReadNodeAsync(string nodeIdStr, bool isOnlyServer = true, bool cache = true, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         if (!isOnlyServer) | ||||
|         { | ||||
| @@ -1073,7 +1075,9 @@ public class OpcUaMaster : IDisposable | ||||
|  | ||||
|         if (OpcUaProperty.LoadType && variableNode.DataType != NodeId.Null && TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable) == BuiltInType.ExtensionObject) | ||||
|             await typeSystem.LoadType(variableNode.DataType, ct: cancellationToken).ConfigureAwait(false); | ||||
|         _variableDicts.AddOrUpdate(nodeIdStr, a => variableNode, (a, b) => variableNode); | ||||
|  | ||||
|         if (cache) | ||||
|             _variableDicts.AddOrUpdate(nodeIdStr, a => variableNode, (a, b) => variableNode); | ||||
|         return variableNode; | ||||
|     } | ||||
|  | ||||
| @@ -1127,7 +1131,7 @@ public class OpcUaMaster : IDisposable | ||||
|     /// <summary> | ||||
|     /// 从服务器读取节点 | ||||
|     /// </summary> | ||||
|     private async Task<List<Node>> ReadNodesAsync(string[] nodeIdStrs, CancellationToken cancellationToken = default) | ||||
|     private async Task<List<Node>> ReadNodesAsync(string[] nodeIdStrs, bool cache = false, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         List<Node> result = new(nodeIdStrs.Length); | ||||
|         foreach (var items in nodeIdStrs.ChunkBetter(OpcUaProperty.GroupSize)) | ||||
| @@ -1171,7 +1175,8 @@ public class OpcUaMaster : IDisposable | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     _variableDicts.AddOrUpdate(nodeIdStrs[i], a => node, (a, b) => node); | ||||
|                     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) | ||||
|                     { | ||||
|                         await typeSystem.LoadType(node.DataType, ct: cancellationToken).ConfigureAwait(false); | ||||
|   | ||||
| @@ -19,7 +19,7 @@ | ||||
| 		 | ||||
| 		 | ||||
| 		 | ||||
| 		<PackageReference Include="SqlSugar.TDengineCore" Version="4.18.8" GeneratePathProperty="true"> | ||||
| 		<PackageReference Include="SqlSugar.TDengineCore" Version="4.18.11" GeneratePathProperty="true"> | ||||
| 			<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets> | ||||
| 		</PackageReference> | ||||
|  | ||||
|   | ||||
| @@ -1,32 +1,11 @@ | ||||
| <Project> | ||||
|  | ||||
|  | ||||
|  | ||||
| 	<Target Name="CopyNugetPackages" AfterTargets="Build"> | ||||
| 		<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'"> | ||||
| 			<!-- setting up the variable for convenience --> | ||||
| 			<ApplicationPackageFiles Include="$(PkgMQTTnet_AspNetCore)\lib\net6.0\*.*" /> | ||||
| 			<MQTTnetApplicationPackageFiles Include="$(PkgMQTTnet)\lib\net6.0\*.*" /> | ||||
| 		</ItemGroup> | ||||
| 		<ItemGroup Condition="'$(TargetFramework)' != 'net6.0'"> | ||||
| 			<!-- setting up the variable for convenience --> | ||||
| 			<ApplicationPackageFiles Include="$(PkgMQTTnet_AspNetCore)\lib\net8.0\*.*" /> | ||||
| 			<MQTTnet_ServerApplicationPackageFiles Include="$(PkgMQTTnet_Server)\lib\net8.0\*.*" /> | ||||
| 			<MQTTnetApplicationPackageFiles Include="$(PkgMQTTnet)\lib\net8.0\*.*" /> | ||||
| 		</ItemGroup> | ||||
| 		<PropertyGroup> | ||||
| 			<ApplicationFolder>$(TargetDir)</ApplicationFolder> | ||||
| 		</PropertyGroup> | ||||
| 		<Copy SourceFiles="@(ApplicationPackageFiles)" DestinationFolder="$(ApplicationFolder)%(RecursiveDir)" /> | ||||
| 		<Copy SourceFiles="@(MQTTnet_ServerApplicationPackageFiles)" DestinationFolder="$(ApplicationFolder)%(RecursiveDir)" /> | ||||
| 		<Copy SourceFiles="@(MQTTnetApplicationPackageFiles)" DestinationFolder="$(ApplicationFolder)%(RecursiveDir)" /> | ||||
| 	</Target> | ||||
|  | ||||
|  | ||||
| 	<!--在构建后触发的。它通过在 Nuget 包的 Content 文件夹中包含目标目录中的所有文件和子文件夹来创建 nuget 包--> | ||||
| 	<Target Name="IncludeAllFilesInTargetDir" AfterTargets="Build"> | ||||
|  | ||||
| 		<ItemGroup> | ||||
| 			<Content Include="$(ProjectDir)bin\$(Configuration)\$(TargetFramework)\**\*MQTT*.dll"> | ||||
| 			<Content Include="$(ProjectDir)bin\$(Configuration)\$(TargetFramework)\**\*Http*.dll"> | ||||
| 				<Pack>true</Pack> | ||||
| 				<PackagePath>Content</PackagePath> | ||||
| 			</Content> | ||||
|   | ||||
| @@ -12,8 +12,6 @@ using BootstrapBlazor.Components; | ||||
|  | ||||
| using Mapster; | ||||
|  | ||||
| using System.Text; | ||||
|  | ||||
| using ThingsGateway.Foundation; | ||||
| using ThingsGateway.NewLife.Extension; | ||||
|  | ||||
|   | ||||
| @@ -80,6 +80,11 @@ public class ModbusMaster : CollectFoundationBase | ||||
|     protected override async Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables) | ||||
|     { | ||||
|         await Task.CompletedTask.ConfigureAwait(false); | ||||
|         return _plc.LoadSourceRead<VariableSourceRead>(deviceVariables, _driverPropertys.MaxPack, CurrentDevice.IntervalTime); | ||||
|         List<VariableSourceRead> variableSourceReads = new(); | ||||
|         foreach (var deviceVariable in deviceVariables.GroupBy(a => a.CollectGroup)) | ||||
|         { | ||||
|             variableSourceReads.AddRange(_plc.LoadSourceRead<VariableSourceRead>(deviceVariable, _driverPropertys.MaxPack, CurrentDevice.IntervalTime)); | ||||
|         } | ||||
|         return variableSourceReads; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -122,25 +122,31 @@ public class OpcDaMaster : CollectBase | ||||
|         { | ||||
|             if (deviceVariables.Count > 0) | ||||
|             { | ||||
|                 var result = _plc.AddItemsWithSave(deviceVariables.Where(a => !string.IsNullOrEmpty(a.RegisterAddress)).Select(a => a.RegisterAddress!).ToList()); | ||||
|                 var sourVars = result?.Select( | ||||
|           it => | ||||
|           { | ||||
|               var read = new VariableSourceRead() | ||||
|               { | ||||
|                   TimeTick = new(_driverProperties.UpdateRate.ToString()), | ||||
|                   RegisterAddress = it.Key, | ||||
|               }; | ||||
|               HashSet<string> ids = new(it.Value.Select(b => b.ItemID)); | ||||
|                 List<VariableSourceRead> variableSourceReads = new List<VariableSourceRead>(); | ||||
|                 foreach (var deviceVariableGroups in deviceVariables.GroupBy(a => a.CollectGroup)) | ||||
|                 { | ||||
|  | ||||
|               var variables = deviceVariables.Where(a => ids.Contains(a.RegisterAddress)); | ||||
|               foreach (var v in variables) | ||||
|                     var result = _plc.AddItemsWithSave(deviceVariableGroups.Where(a => !string.IsNullOrEmpty(a.RegisterAddress)).Select(a => a.RegisterAddress!).ToList()); | ||||
|                     var sourVars = result?.Select( | ||||
|               it => | ||||
|               { | ||||
|                   read.AddVariable(v); | ||||
|               } | ||||
|               return read; | ||||
|           }).ToList(); | ||||
|                 return sourVars; | ||||
|                   var read = new VariableSourceRead() | ||||
|                   { | ||||
|                       TimeTick = new(_driverProperties.UpdateRate.ToString()), | ||||
|                       RegisterAddress = it.Key, | ||||
|                   }; | ||||
|                   HashSet<string> ids = new(it.Value.Select(b => b.ItemID)); | ||||
|  | ||||
|                   var variables = deviceVariableGroups.Where(a => ids.Contains(a.RegisterAddress)); | ||||
|                   foreach (var v in variables) | ||||
|                   { | ||||
|                       read.AddVariable(v); | ||||
|                   } | ||||
|                   return read; | ||||
|               }).ToList(); | ||||
|                     variableSourceReads.AddRange(sourVars); | ||||
|                 } | ||||
|                 return variableSourceReads; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|   | ||||
| @@ -187,24 +187,27 @@ public class OpcUaMaster : CollectBase | ||||
|         await Task.CompletedTask.ConfigureAwait(false); | ||||
|         if (deviceVariables.Count > 0) | ||||
|         { | ||||
|             var dataLists = deviceVariables.ChunkBetter(_driverProperties.GroupSize); | ||||
|  | ||||
|             var dataResult = new List<VariableSourceRead>(); | ||||
|             foreach (var variable in dataLists) | ||||
|             List<VariableSourceRead> variableSourceReads = new List<VariableSourceRead>(); | ||||
|             foreach (var deviceVariableGroups in deviceVariables.GroupBy(a => a.CollectGroup)) | ||||
|             { | ||||
|                 var sourVars = new VariableSourceRead() | ||||
|                 { | ||||
|                     TimeTick = new(_driverProperties.UpdateRate.ToString()), | ||||
|                     RegisterAddress = Guid.NewGuid().ToString(), | ||||
|                 }; | ||||
|                 foreach (var item in variable) | ||||
|                 { | ||||
|                     sourVars.AddVariable(item); | ||||
|                 } | ||||
|                 dataResult.Add(sourVars); | ||||
|             } | ||||
|                 var dataLists = deviceVariableGroups.ChunkBetter(_driverProperties.GroupSize); | ||||
|  | ||||
|             return dataResult; | ||||
|                 foreach (var variable in dataLists) | ||||
|                 { | ||||
|                     var sourVars = new VariableSourceRead() | ||||
|                     { | ||||
|                         TimeTick = new(_driverProperties.UpdateRate.ToString()), | ||||
|                         RegisterAddress = Guid.NewGuid().ToString(), | ||||
|                     }; | ||||
|                     foreach (var item in variable) | ||||
|                     { | ||||
|                         sourVars.AddVariable(item); | ||||
|                     } | ||||
|                     variableSourceReads.Add(sourVars); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|             return variableSourceReads; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|   | ||||
| @@ -12,8 +12,6 @@ using Mapster; | ||||
|  | ||||
| using RabbitMQ.Client; | ||||
|  | ||||
| using System.Text; | ||||
|  | ||||
| using ThingsGateway.Foundation; | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
|  | ||||
|   | ||||
| @@ -221,7 +221,12 @@ public class SiemensS7Master : CollectFoundationBase | ||||
|             { | ||||
|  | ||||
|             } | ||||
|             return _plc.LoadSourceRead<VariableSourceRead>(deviceVariables, _plc.OnLine ? _plc.PduLength : _driverPropertys.MaxPack, CurrentDevice.IntervalTime); | ||||
|             List<VariableSourceRead> variableSourceReads = new(); | ||||
|             foreach (var deviceVariable in deviceVariables.GroupBy(a => a.CollectGroup)) | ||||
|             { | ||||
|                 variableSourceReads.AddRange(_plc.LoadSourceRead<VariableSourceRead>(deviceVariable, _driverPropertys.MaxPack, CurrentDevice.IntervalTime)); | ||||
|             } | ||||
|             return variableSourceReads; | ||||
|         } | ||||
|         finally { } | ||||
|     } | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
|  | ||||
|   "ConfigurationScanDirectories": [ "Configuration", "" ], // 扫描配置文件json文件夹(自动合并该文件夹里面所有json文件) | ||||
|   "IgnoreConfigurationFiles": [ "" ], | ||||
|   "ExternalAssemblies": [ "" ] | ||||
|   "ExternalAssemblies": [ "" ], | ||||
|   "DetailedErrors": true | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -162,7 +162,7 @@ public class FileHostService : BackgroundService, IFileHostService | ||||
|                 _log.Exception(ex); | ||||
|                 try | ||||
|                 { | ||||
|                     await TcpDmtpService.StopAsync().ConfigureAwait(false); | ||||
|                     await TcpDmtpService.StopAsync(default).ConfigureAwait(false); | ||||
|                 } | ||||
|                 catch | ||||
|                 { | ||||
|   | ||||
| @@ -4,7 +4,7 @@ using TouchSocket.Rpc; | ||||
|  | ||||
| namespace ThingsGateway.Upgrade; | ||||
|  | ||||
| public partial class FileRpcServer : RpcServer | ||||
| public partial class FileRpcServer : SingletonRpcServer | ||||
| { | ||||
|     private readonly ILog _logger; | ||||
|  | ||||
|   | ||||
| @@ -16,7 +16,7 @@ | ||||
|  | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 	  <PackageReference Include="TouchSocket.Dmtp" Version="3.0.26" /> | ||||
| 	  <PackageReference Include="TouchSocket.Dmtp" Version="3.1.1" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| <Project> | ||||
|   <PropertyGroup> | ||||
|     <Version>10.5.3</Version> | ||||
|     <Version>10.5.11</Version> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user