mirror of
				https://gitee.com/ThingsGateway/ThingsGateway.git
				synced 2025-10-31 07:33:58 +08:00 
			
		
		
		
	Compare commits
	
		
			20 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 6a03e39eeb | ||
|   | 525ec740b5 | ||
|   | b790cf5f4e | ||
|   | d1248811fd | ||
|   | 022d016e8e | ||
|   | f73245e650 | ||
|   | 484461fa05 | ||
|   | 7e0b7aff2a | ||
|   | 6c450dcb09 | ||
|   | 227f44283f | ||
|   | 74f6e79625 | ||
|   | cec43e2ce8 | ||
|   | 7553b258bb | ||
|   | 8bdbdc117e | ||
|   | 0e206be296 | ||
|   | 00b7353433 | ||
|   | 44e7a83593 | ||
|   | dd68d555d4 | ||
|   | 0456296103 | ||
|   | a1b66277ff | 
| @@ -13,7 +13,6 @@ using Microsoft.AspNetCore.Authentication.Cookies; | ||||
| using Microsoft.AspNetCore.Http; | ||||
| using Microsoft.AspNetCore.WebUtilities; | ||||
|  | ||||
| using System.Net; | ||||
| using System.Security.Claims; | ||||
|  | ||||
| using UAParser; | ||||
|   | ||||
| @@ -11,7 +11,6 @@ | ||||
| using Microsoft.AspNetCore.Components.Authorization; | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
|  | ||||
| using System.Net; | ||||
| using System.Security.Claims; | ||||
|  | ||||
| using UAParser; | ||||
|   | ||||
| @@ -9,7 +9,6 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
|  | ||||
| using System.Net; | ||||
| using System.Security.Claims; | ||||
|  | ||||
| using UAParser; | ||||
|   | ||||
| @@ -18,10 +18,10 @@ | ||||
| 	</ItemGroup> | ||||
| 	 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.3" /> | ||||
| 		<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.4" /> | ||||
| 		<PackageReference Include="UAParser" Version="3.1.47" /> | ||||
| 		<PackageReference Include="Rougamo.Fody" Version="5.0.0" /> | ||||
| 		<PackageReference Include="SqlSugarCore" Version="5.1.4.189" /> | ||||
| 		<PackageReference Include="SqlSugarCore" Version="5.1.4.191" /> | ||||
| 	</ItemGroup> | ||||
| 	<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' "> | ||||
| 		<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" /> | ||||
|   | ||||
| @@ -10,6 +10,8 @@ | ||||
|  | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.AspNetCore.Components.Authorization; | ||||
| using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel; | ||||
| using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption; | ||||
| using Microsoft.AspNetCore.HttpOverrides; | ||||
| using Microsoft.AspNetCore.Mvc.Controllers; | ||||
| using Microsoft.AspNetCore.StaticFiles; | ||||
| @@ -17,6 +19,7 @@ using Microsoft.Extensions.Localization; | ||||
| using Microsoft.Extensions.Options; | ||||
|  | ||||
| using Newtonsoft.Json; | ||||
| using System.Security.Cryptography.X509Certificates; | ||||
|  | ||||
| using System.Text; | ||||
| using System.Text.Encodings.Web; | ||||
| @@ -27,6 +30,7 @@ using ThingsGateway.Admin.Razor; | ||||
| using ThingsGateway.Extension; | ||||
| using ThingsGateway.Logging; | ||||
| using ThingsGateway.NewLife.Caching; | ||||
| using Microsoft.AspNetCore.DataProtection; | ||||
|  | ||||
| namespace ThingsGateway.AdminServer; | ||||
|  | ||||
| @@ -291,6 +295,21 @@ public class Startup : AppStartup | ||||
|         services.AddAuthorizationCore(); | ||||
|         services.AddScoped<IAuthorizationHandler, BlazorServerAuthenticationHandler>(); | ||||
|         services.AddScoped<AuthenticationStateProvider, BlazorServerAuthenticationStateProvider>(); | ||||
|  | ||||
| #if NET9_0_OR_GREATER | ||||
|         var certificate = X509CertificateLoader.LoadPkcs12FromFile("ThingsGateway.pfx", "ThingsGateway", X509KeyStorageFlags.EphemeralKeySet); | ||||
| #else | ||||
|         var certificate = new X509Certificate2("ThingsGateway.pfx", "ThingsGateway", X509KeyStorageFlags.EphemeralKeySet); | ||||
| #endif | ||||
|         services.AddDataProtection() | ||||
|             .PersistKeysToFileSystem(new DirectoryInfo("../keys")) | ||||
|             .ProtectKeysWithCertificate(certificate) | ||||
|             .UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration | ||||
|             { | ||||
|                 EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC, | ||||
|                 ValidationAlgorithm = ValidationAlgorithm.HMACSHA256 | ||||
|             }); | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -72,6 +72,9 @@ | ||||
| 		<None Update="pm2-linux.json"> | ||||
| 			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
| 		</None> | ||||
| 		<None Update="ThingsGateway.pfx"> | ||||
| 		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
| 		</None> | ||||
| 		<None Update="thingsgateway.service"> | ||||
| 			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
| 		</None> | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								src/Admin/ThingsGateway.AdminServer/ThingsGateway.pfx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/Admin/ThingsGateway.AdminServer/ThingsGateway.pfx
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -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.9" /> | ||||
| 		<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.2</PluginVersion> | ||||
| 		<ProPluginVersion>10.5.2</ProPluginVersion> | ||||
| 		<PluginVersion>10.5.14</PluginVersion> | ||||
| 		<ProPluginVersion>10.5.14</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); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -546,6 +562,8 @@ public abstract class DeviceBase : DisposableObject, IDevice | ||||
|         finally | ||||
|         { | ||||
|             waitLock.Release(); | ||||
|             if (waitData.WaitResult != null) | ||||
|                 waitData.WaitResult.Sign = sign; | ||||
|             clientChannel.WaitHandlePool.Destroy(waitData); | ||||
|             Channel.ChannelReceivedWaitDict.TryRemove(sign, out _); | ||||
|         } | ||||
| @@ -944,7 +962,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> | ||||
|   | ||||
| @@ -40,9 +40,7 @@ public class AsyncReadWriteLock | ||||
|  | ||||
|     private void ReleaseWriter() | ||||
|     { | ||||
|         Interlocked.Decrement(ref _writerCount); | ||||
|  | ||||
|         if (Interlocked.Read(ref _writerCount) == 0) | ||||
|         if (Interlocked.Decrement(ref _writerCount) == 0) | ||||
|         { | ||||
|             var resetEvent = _readerLock; | ||||
|             _readerLock = new(false); | ||||
|   | ||||
| @@ -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>()); | ||||
|         }); | ||||
|     } | ||||
|   | ||||
| @@ -9,6 +9,9 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using System.Globalization; | ||||
| using System.Text; | ||||
| using System.Text.Encodings.Web; | ||||
| using System.Text.Json; | ||||
| using System.Text.RegularExpressions; | ||||
|  | ||||
| using ThingsGateway.NewLife.Json.Extension; | ||||
| @@ -63,7 +66,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|     protected List<TopicJson> GetAlarms(IEnumerable<AlarmModel> item) | ||||
|     { | ||||
|         IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<AlarmModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel); | ||||
|         List<TopicJson> topicJsonList = new List<TopicJson>(); | ||||
|         var topicJsonList = new List<TopicJson>(); | ||||
|         var topics = Match(_businessPropertyWithCacheIntervalScript.AlarmTopic); | ||||
|         if (topics.Count > 0) | ||||
|         { | ||||
| @@ -128,7 +131,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|     protected List<TopicJson> GetDeviceData(IEnumerable<DevModel> item) | ||||
|     { | ||||
|         IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<DevModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel); | ||||
|         List<TopicJson> topicJsonList = new List<TopicJson>(); | ||||
|         var topicJsonList = new List<TopicJson>(); | ||||
|         var topics = Match(_businessPropertyWithCacheIntervalScript.DeviceTopic); | ||||
|         if (topics.Count > 0) | ||||
|         { | ||||
| @@ -193,7 +196,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|     protected List<TopicJson> GetVariable(IEnumerable<VarModel> item) | ||||
|     { | ||||
|         IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<VarModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptVariableModel); | ||||
|         List<TopicJson> topicJsonList = new List<TopicJson>(); | ||||
|         var topicJsonList = new List<TopicJson>(); | ||||
|         var topics = Match(_businessPropertyWithCacheIntervalScript.VariableTopic); | ||||
|         if (topics.Count > 0) | ||||
|         { | ||||
| @@ -267,7 +270,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|         { | ||||
|             data = item; | ||||
|         } | ||||
|         List<TopicJson> topicJsonList = new List<TopicJson>(); | ||||
|         var topicJsonList = new List<TopicJson>(); | ||||
|         var topics = Match(_businessPropertyWithCacheIntervalScript.VariableTopic); | ||||
|         if (topics.Count > 0) | ||||
|         { | ||||
| @@ -329,6 +332,40 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|         return topicJsonList; | ||||
|     } | ||||
|  | ||||
|     //protected static byte[] Serialize(object value) | ||||
|     //{ | ||||
|     //    var block = new ValueByteBlock(1024 * 64); | ||||
|     //    try | ||||
|     //    { | ||||
|     //        //将数据序列化到内存块 | ||||
|     //        FastBinaryFormatter.Serialize(ref block, value); | ||||
|     //        block.SeekToStart(); | ||||
|     //        return block.Memory.GetArray().Array; | ||||
|     //    } | ||||
|     //    finally | ||||
|     //    { | ||||
|     //        block.Dispose(); | ||||
|     //    } | ||||
|     //} | ||||
|     protected static JsonSerializerOptions NoWriteIndentedJsonSerializerOptions = new JsonSerializerOptions | ||||
|     { | ||||
|         Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, | ||||
|         WriteIndented = false | ||||
|     }; | ||||
|     protected static JsonSerializerOptions WriteIndentedJsonSerializerOptions = new JsonSerializerOptions | ||||
|     { | ||||
|         Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, | ||||
|         WriteIndented = true | ||||
|     }; | ||||
|  | ||||
|     protected static byte[] Serialize(object data, bool writeIndented) | ||||
|     { | ||||
|         if (data == null) return Array.Empty<byte>(); | ||||
|         byte[] payload = JsonSerializer.SerializeToUtf8Bytes(data, data.GetType(), writeIndented ? WriteIndentedJsonSerializerOptions : NoWriteIndentedJsonSerializerOptions); | ||||
|         return payload; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     protected List<TopicArray> GetAlarmTopicArrays(IEnumerable<AlarmModel> item) | ||||
|     { | ||||
|         IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<AlarmModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel); | ||||
| @@ -355,7 +392,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|                     // 上传内容 | ||||
|                     if (_businessPropertyWithCacheIntervalScript.IsAlarmList) | ||||
|                     { | ||||
|                         var json = Serialize(group.Select(a => a).ToList().ToList()); | ||||
|                         var json = Serialize(group.Select(a => a).ToList().ToList(), _businessPropertyWithCacheIntervalScript.JsonFormattingIndented); | ||||
|                         // 将主题和 JSON 内容添加到列表中 | ||||
|                         topicJsonList.Add(new(topic, json)); | ||||
|                     } | ||||
| @@ -364,7 +401,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|                         // 如果不是报警列表,则将每个分组元素分别转换为 JSON 字符串 | ||||
|                         foreach (var gro in group) | ||||
|                         { | ||||
|                             var json = Serialize(gro); | ||||
|                             var json = Serialize(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented); | ||||
|                             // 将主题和 JSON 内容添加到列表中 | ||||
|                             topicJsonList.Add(new(topic, json)); | ||||
|                         } | ||||
| @@ -376,14 +413,14 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|         { | ||||
|             if (_businessPropertyWithCacheIntervalScript.IsAlarmList) | ||||
|             { | ||||
|                 var json = Serialize(data.Select(a => a).ToList()); | ||||
|                 var json = Serialize(data.Select(a => a).ToList(), _businessPropertyWithCacheIntervalScript.JsonFormattingIndented); | ||||
|                 topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 foreach (var group in data) | ||||
|                 { | ||||
|                     var json = Serialize(group); | ||||
|                     var json = Serialize(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented); | ||||
|                     topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json)); | ||||
|                 } | ||||
|             } | ||||
| @@ -392,22 +429,6 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|         return topicJsonList; | ||||
|     } | ||||
|  | ||||
|     protected static ArraySegment<byte> Serialize(object value) | ||||
|     { | ||||
|         var block = new ValueByteBlock(1024 * 64); | ||||
|         try | ||||
|         { | ||||
|             //将数据序列化到内存块 | ||||
|             FastBinaryFormatter.Serialize(ref block, value); | ||||
|             block.SeekToStart(); | ||||
|             return block.Memory.GetArray(); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             block.Dispose(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected List<TopicArray> GetDeviceTopicArray(IEnumerable<DevModel> item) | ||||
|     { | ||||
|         IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<DevModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel); | ||||
| @@ -436,7 +457,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|                         if (_businessPropertyWithCacheIntervalScript.IsDeviceList) | ||||
|                         { | ||||
|                             // 如果是设备列表,则将整个分组转换为 JSON 字符串 | ||||
|                             var json = Serialize(group.Select(a => a).ToList()); | ||||
|                             var json = Serialize(group.Select(a => a).ToList(), _businessPropertyWithCacheIntervalScript.JsonFormattingIndented); | ||||
|                             // 将主题和 JSON 内容添加到列表中 | ||||
|                             topicJsonList.Add(new(topic, json)); | ||||
|                         } | ||||
| @@ -445,7 +466,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|                             // 如果不是设备列表,则将每个分组元素分别转换为 JSON 字符串 | ||||
|                             foreach (var gro in group) | ||||
|                             { | ||||
|                                 var json = Serialize(gro); | ||||
|                                 var json = Serialize(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented); | ||||
|                                 // 将主题和 JSON 内容添加到列表中 | ||||
|                                 topicJsonList.Add(new(topic, json)); | ||||
|                             } | ||||
| @@ -458,14 +479,14 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|         { | ||||
|             if (_businessPropertyWithCacheIntervalScript.IsDeviceList) | ||||
|             { | ||||
|                 var json = Serialize(data.Select(a => a).ToList()); | ||||
|                 var json = Serialize(data.Select(a => a).ToList(), _businessPropertyWithCacheIntervalScript.JsonFormattingIndented); | ||||
|                 topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 foreach (var group in data) | ||||
|                 { | ||||
|                     var json = Serialize(group); | ||||
|                     var json = Serialize(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented); | ||||
|                     topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json)); | ||||
|                 } | ||||
|             } | ||||
| @@ -501,7 +522,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|                         if (_businessPropertyWithCacheIntervalScript.IsVariableList) | ||||
|                         { | ||||
|                             // 如果是变量列表,则将整个分组转换为 JSON 字符串 | ||||
|                             var json = Serialize(group.Select(a => a).ToList()); | ||||
|                             var json = Serialize(group.Select(a => a).ToList(), _businessPropertyWithCacheIntervalScript.JsonFormattingIndented); | ||||
|                             // 将主题和 JSON 内容添加到列表中 | ||||
|                             topicJsonList.Add(new(topic, json)); | ||||
|                         } | ||||
| @@ -510,7 +531,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|                             // 如果不是变量列表,则将每个分组元素分别转换为 JSON 字符串 | ||||
|                             foreach (var gro in group) | ||||
|                             { | ||||
|                                 var json = Serialize(gro); | ||||
|                                 var json = Serialize(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented); | ||||
|                                 // 将主题和 JSON 内容添加到列表中 | ||||
|                                 topicJsonList.Add(new(topic, json)); | ||||
|                             } | ||||
| @@ -523,14 +544,14 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|         { | ||||
|             if (_businessPropertyWithCacheIntervalScript.IsVariableList) | ||||
|             { | ||||
|                 var json = Serialize(data.Select(a => a).ToList()); | ||||
|                 var json = Serialize(data.Select(a => a).ToList(), _businessPropertyWithCacheIntervalScript.JsonFormattingIndented); | ||||
|                 topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 foreach (var group in data) | ||||
|                 { | ||||
|                     var json = Serialize(group); | ||||
|                     var json = Serialize(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented); | ||||
|                     topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json)); | ||||
|                 } | ||||
|             } | ||||
| @@ -538,7 +559,6 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|         return topicJsonList; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     protected List<TopicArray> GetVariableBasicDataTopicArray(IEnumerable<VariableBasicData> item) | ||||
|     { | ||||
|         IEnumerable<VariableBasicData>? data = null; | ||||
| @@ -576,7 +596,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|                         if (_businessPropertyWithCacheIntervalScript.IsVariableList) | ||||
|                         { | ||||
|                             // 如果是变量列表,则将整个分组转换为 JSON 字符串 | ||||
|                             var json = Serialize(group.Select(a => a).ToList()); | ||||
|                             var json = Serialize(group.Select(a => a).ToList(), _businessPropertyWithCacheIntervalScript.JsonFormattingIndented); | ||||
|                             // 将主题和 JSON 内容添加到列表中 | ||||
|                             topicJsonList.Add(new(topic, json)); | ||||
|                         } | ||||
| @@ -585,7 +605,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|                             // 如果不是变量列表,则将每个分组元素分别转换为 JSON 字符串 | ||||
|                             foreach (var gro in group) | ||||
|                             { | ||||
|                                 var json = Serialize(gro); | ||||
|                                 var json = Serialize(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented); | ||||
|                                 // 将主题和 JSON 内容添加到列表中 | ||||
|                                 topicJsonList.Add(new(topic, json)); | ||||
|                             } | ||||
| @@ -598,14 +618,14 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|         { | ||||
|             if (_businessPropertyWithCacheIntervalScript.IsVariableList) | ||||
|             { | ||||
|                 var json = Serialize(data.Select(a => a).ToList()); | ||||
|                 var json = Serialize(data.Select(a => a).ToList(), _businessPropertyWithCacheIntervalScript.JsonFormattingIndented); | ||||
|                 topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 foreach (var group in data) | ||||
|                 { | ||||
|                     var json = Serialize(group); | ||||
|                     var json = Serialize(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented); | ||||
|                     topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json)); | ||||
|                 } | ||||
|             } | ||||
| @@ -614,5 +634,9 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM | ||||
|     } | ||||
|  | ||||
|  | ||||
|     protected string GetString(string topic, byte[] json, int count) | ||||
|     { | ||||
|         return $"Topic:{topic}{Environment.NewLine}PayLoad:{Encoding.UTF8.GetString(json)} {Environment.NewLine} VarModelQueue:{count}"; | ||||
|     } | ||||
|     #endregion 封装方法 | ||||
| } | ||||
|   | ||||
| @@ -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>()); | ||||
|         }); | ||||
|     } | ||||
|   | ||||
| @@ -1,32 +0,0 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using BootstrapBlazor.Components; | ||||
|  | ||||
| namespace ThingsGateway.Gateway.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// <inheritdoc/> | ||||
| /// </summary> | ||||
| public class BusinessPropertyWithCacheIntervalDBScript : BusinessPropertyWithCacheInterval | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 表定义实体脚本 | ||||
|     /// </summary> | ||||
|     [DynamicProperty] | ||||
|     [AutoGenerateColumn(ComponentType = typeof(Textarea), Rows = 3)] | ||||
|     public string? BigTextScriptTableModel { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 数据脚本 | ||||
|     /// </summary> | ||||
|     [AutoGenerateColumn(ComponentType = typeof(Textarea), Rows = 3)] | ||||
|     public string? BigTextScriptDataModel { get; set; } | ||||
| } | ||||
| @@ -12,11 +12,12 @@ namespace ThingsGateway.Gateway.Application; | ||||
|  | ||||
| public struct TopicArray | ||||
| { | ||||
|     public TopicArray(string topic, ArraySegment<byte> json) | ||||
|     public TopicArray(string topic, byte[] json) | ||||
|     { | ||||
|         Topic = topic; Json = json; | ||||
|     } | ||||
|  | ||||
|     public ArraySegment<byte> Json { get; set; } | ||||
|     public byte[] Json { get; set; } | ||||
|     public string Topic { get; set; } | ||||
|  | ||||
| } | ||||
| @@ -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; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -216,16 +216,7 @@ public class ChannelRuntimeService : IChannelRuntimeService | ||||
|             await RuntimeServiceHelper.InitAsync(newChannelRuntimes, newDeviceRuntimes, _logger).ConfigureAwait(false); | ||||
|  | ||||
|  | ||||
|             var startCollectChannelEnable = GlobalData.StartCollectChannelEnable; | ||||
|             var startBusinessChannelEnable = GlobalData.StartBusinessChannelEnable; | ||||
|  | ||||
|             var collectChannelRuntimes = newChannelRuntimes.Where(x => (x.Enable && x.IsCollect == true && startCollectChannelEnable)); | ||||
|  | ||||
|             var businessChannelRuntimes = newChannelRuntimes.Where(x => (x.Enable && x.IsCollect == false && startBusinessChannelEnable)); | ||||
|  | ||||
|             //根据初始冗余属性,筛选启动 | ||||
|             await GlobalData.ChannelThreadManage.RestartChannelAsync(businessChannelRuntimes).ConfigureAwait(false); | ||||
|             await GlobalData.ChannelThreadManage.RestartChannelAsync(collectChannelRuntimes).ConfigureAwait(false); | ||||
|             await GlobalData.ChannelThreadManage.RestartChannelAsync(newChannelRuntimes).ConfigureAwait(false); | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|   | ||||
| @@ -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); | ||||
| @@ -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]); | ||||
|  | ||||
|   | ||||
| @@ -71,16 +71,7 @@ internal sealed class GatewayMonitorHostedService : BackgroundService, IGatewayM | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             var startCollectChannelEnable = GlobalData.StartCollectChannelEnable; | ||||
|             var startBusinessChannelEnable = GlobalData.StartBusinessChannelEnable; | ||||
|  | ||||
|             var collectChannelRuntimes = channelRuntimes.Where(x => (x.Enable && x.IsCollect == true && startCollectChannelEnable)); | ||||
|  | ||||
|             var businessChannelRuntimes = channelRuntimes.Where(x => (x.Enable && x.IsCollect == false && startBusinessChannelEnable)); | ||||
|  | ||||
|             //根据初始冗余属性,筛选启动 | ||||
|             await ChannelThreadManage.RestartChannelAsync(businessChannelRuntimes).ConfigureAwait(false); | ||||
|             await ChannelThreadManage.RestartChannelAsync(collectChannelRuntimes).ConfigureAwait(false); | ||||
|             await ChannelThreadManage.RestartChannelAsync(channelRuntimes).ConfigureAwait(false); | ||||
|  | ||||
|  | ||||
|         } | ||||
|   | ||||
| @@ -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,6 +1299,7 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e => | ||||
|             try | ||||
|             { | ||||
|                 if (Disposed) return; | ||||
|                 await Task.Delay(1000); | ||||
|                 await OnClickSearch(SearchText); | ||||
|  | ||||
|                 Value = GetValue(Value); | ||||
| @@ -1310,7 +1311,6 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e => | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 await Task.Delay(2000); | ||||
|                 _isExecuting = false; | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -129,7 +129,6 @@ public partial class PropertyComponent : IPropertyUIBase | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         var op = new DialogOption() | ||||
|         { | ||||
|             IsScrolling = true, | ||||
| @@ -144,6 +143,147 @@ public partial class PropertyComponent : IPropertyUIBase | ||||
|     { | ||||
|         {nameof(ScriptCheck.Data),data }, | ||||
|         {nameof(ScriptCheck.Script),script }, | ||||
|         {nameof(ScriptCheck.OnGetDemo),()=> | ||||
|                 { | ||||
|                     return | ||||
|                     pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel)? | ||||
|                     """ | ||||
|                     using ThingsGateway.Foundation; | ||||
|                      | ||||
|                     using System.Dynamic; | ||||
|                     using TouchSocket.Core; | ||||
|                     public class S1 : IDynamicModel | ||||
|                     { | ||||
|                         public IEnumerable<dynamic> GetList(IEnumerable<object> datas) | ||||
|                         { | ||||
|                             List<ExpandoObject> deviceObjs = new List<ExpandoObject>(); | ||||
|                             foreach (var v in datas) | ||||
|                             { | ||||
|                                 var device = (DeviceBasicData)v; | ||||
|                                 var expando = new ExpandoObject(); | ||||
|                                 var deviceObj = new ExpandoObject(); | ||||
|  | ||||
|                                 deviceObj.TryAdd(nameof(Device.Description), device.Description); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.ActiveTime), device.ActiveTime); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.DeviceStatus), device.DeviceStatus.ToString()); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.LastErrorMessage), device.LastErrorMessage); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.PluginName), device.PluginName); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.Remark1), device.Remark1); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.Remark2), device.Remark2); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.Remark3), device.Remark3); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.Remark4), device.Remark4); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.Remark5), device.Remark5); | ||||
|  | ||||
|  | ||||
|                                 expando.TryAdd(nameof(Device.Name), deviceObj); | ||||
|  | ||||
|                             } | ||||
|                             return deviceObjs; | ||||
|                         } | ||||
|                     } | ||||
|                      | ||||
|                     """ | ||||
|                     : | ||||
|  | ||||
|                     pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptVariableModel)? | ||||
|  | ||||
|                     """ | ||||
|                     using System.Dynamic; | ||||
|                     using ThingsGateway.Foundation; | ||||
|                     using TouchSocket.Core; | ||||
|                     public class S2 : IDynamicModel | ||||
|                     { | ||||
|                         public IEnumerable<dynamic> GetList(IEnumerable<object> datas) | ||||
|                         { | ||||
|  | ||||
|                             List<ExpandoObject> deviceObjs = new List<ExpandoObject>(); | ||||
|                             //按设备名称分组 | ||||
|                             var groups = datas.Where(a => !string.IsNullOrEmpty(((VariableBasicData)a).DeviceName)).GroupBy(a => ((VariableBasicData)a).DeviceName, a => ((VariableBasicData)a)); | ||||
|                             foreach (var group in groups) | ||||
|                             { | ||||
|                                 //按采集时间分组 | ||||
|                                 var data = group.GroupBy(a => a.CollectTime.DateTimeToUnixTimestamp()); | ||||
|                                 var deviceObj = new ExpandoObject(); | ||||
|                                 List<ExpandoObject> expandos = new List<ExpandoObject>(); | ||||
|                                 foreach (var item in data) | ||||
|                                 { | ||||
|                                     var expando = new ExpandoObject(); | ||||
|                                     expando.TryAdd("ts", item.Key); | ||||
|                                     var variableObj = new ExpandoObject(); | ||||
|                                     foreach (var tag in item) | ||||
|                                     { | ||||
|                                         variableObj.TryAdd(tag.Name, tag.Value); | ||||
|                                     } | ||||
|                                     expando.TryAdd("values", variableObj); | ||||
|  | ||||
|                                     expandos.Add(expando); | ||||
|                                 } | ||||
|                                 deviceObj.TryAdd(group.Key, expandos); | ||||
|                                 deviceObjs.Add(deviceObj); | ||||
|                             } | ||||
|  | ||||
|                             return deviceObjs; | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     """ | ||||
|                     : | ||||
|  | ||||
|                     pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel)? | ||||
|  | ||||
|                     """ | ||||
|                     using System.Dynamic; | ||||
|                     using ThingsGateway.Foundation; | ||||
|                                          | ||||
|                     using TouchSocket.Core; | ||||
|                     public class DeviceScript : IDynamicModel | ||||
|                     { | ||||
|                         public IEnumerable<dynamic> GetList(IEnumerable<object> datas) | ||||
|                         { | ||||
|  | ||||
|                             List<ExpandoObject> deviceObjs = new List<ExpandoObject>(); | ||||
|                             //按设备名称分组 | ||||
|                             var groups = datas.Where(a => !string.IsNullOrEmpty(((AlarmVariable)a).DeviceName)).GroupBy(a => ((AlarmVariable)a).DeviceName, a => ((AlarmVariable)a)); | ||||
|                             foreach (var group in groups) | ||||
|                             { | ||||
|                                 //按采集时间分组 | ||||
|                                 var data = group.GroupBy(a => a.AlarmTime.DateTimeToUnixTimestamp()); | ||||
|                                 var deviceObj = new ExpandoObject(); | ||||
|                                 List<ExpandoObject> expandos = new List<ExpandoObject>(); | ||||
|                                 foreach (var item in data) | ||||
|                                 { | ||||
|                                     var expando = new ExpandoObject(); | ||||
|                                     expando.TryAdd("ts", item.Key); | ||||
|                                     var variableObj = new ExpandoObject(); | ||||
|                                     foreach (var tag in item) | ||||
|                                     { | ||||
|                                         var alarmObj = new ExpandoObject(); | ||||
|                                         alarmObj.TryAdd(nameof(tag.AlarmCode), tag.AlarmCode); | ||||
|                                         alarmObj.TryAdd(nameof(tag.AlarmText), tag.AlarmText); | ||||
|                                         alarmObj.TryAdd(nameof(tag.AlarmType), tag.AlarmType); | ||||
|                                         alarmObj.TryAdd(nameof(tag.AlarmLimit), tag.AlarmLimit); | ||||
|                                         alarmObj.TryAdd(nameof(tag.EventTime), tag.EventTime); | ||||
|                                         alarmObj.TryAdd(nameof(tag.EventType), tag.EventType); | ||||
|  | ||||
|                                         variableObj.TryAdd(tag.Name, alarmObj); | ||||
|                                     } | ||||
|                                     expando.TryAdd("alarms", variableObj); | ||||
|  | ||||
|                                     expandos.Add(expando); | ||||
|                                 } | ||||
|                                 deviceObj.TryAdd(group.Key, expandos); | ||||
|                                 deviceObjs.Add(deviceObj); | ||||
|                             } | ||||
|  | ||||
|                             return deviceObjs; | ||||
|                         } | ||||
|                     } | ||||
|                     """ | ||||
|                     : | ||||
|                     "" | ||||
|                     ; | ||||
|                 } | ||||
|             }, | ||||
|         {nameof(ScriptCheck.ScriptChanged),EventCallback.Factory.Create<string>(this, v => | ||||
|         { | ||||
|                  if (pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel)) | ||||
| @@ -171,3 +311,7 @@ public partial class PropertyComponent : IPropertyUIBase | ||||
|     [Inject] | ||||
|     private DialogService DialogService { get; set; } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -18,6 +18,8 @@ | ||||
|         <Alert Icon="fa-solid fa-circle-check" Color="Color.Success">@(new MarkupString("获取变量类实体,可用方法  <code>GlobalData.GetVariable(\"设备名称1\",\"变量名称1\")</code> "))</Alert> | ||||
|         <Alert Icon="fa-solid fa-circle-check" Color="Color.Success">@(new MarkupString("详细说明查看文档对应内容页面"))</Alert> | ||||
|  | ||||
|         <Button IsAsync OnClick="GetDemo" class="mt-3" Text="Demo" /> | ||||
|  | ||||
|     </div> | ||||
|     <div class="col-6  col-md-6"> | ||||
|         <BootstrapLabel Value=@Localizer["Input"] ShowLabelTooltip="true" /> | ||||
|   | ||||
| @@ -8,6 +8,8 @@ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| using ThingsGateway.Gateway.Application; | ||||
| using ThingsGateway.NewLife.Json.Extension; | ||||
|  | ||||
| @@ -56,4 +58,13 @@ public partial class ScriptCheck | ||||
|     } | ||||
|     [Inject] | ||||
|     private IStringLocalizer<DeviceEditComponent> Localizer { get; set; } | ||||
|  | ||||
|     private async Task GetDemo(Microsoft.AspNetCore.Components.Web.MouseEventArgs args) | ||||
|     { | ||||
|         Script = OnGetDemo?.Invoke(); | ||||
|         await Change(Script); | ||||
|     } | ||||
|  | ||||
|     [Parameter, EditorRequired] | ||||
|     public Func<string> OnGetDemo { get; set; } | ||||
| } | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -4,12 +4,19 @@ | ||||
| @if (WebsiteOption.Value.ShowAuthorize) | ||||
| { | ||||
|  | ||||
|     <Card class="mb-2"> | ||||
|     <Card class="h-100"> | ||||
|         <BodyTemplate> | ||||
|             <div class="ma-1"> | ||||
|                 <span>@Localizer["UUID"]</span> | ||||
|                 <div class="my-2 ms-4 text-truncate"> | ||||
|                     <Textarea Value="@ProAuthentication.UUID" rows="5" /> | ||||
|                 <div class="row g-3 form-inline"> | ||||
|                     <div class="col-12 col-sm-12"> | ||||
|  | ||||
|                         <label class="form-label"> | ||||
|  | ||||
|                             @Localizer["UUID"] | ||||
|                         </label> | ||||
|  | ||||
|                         <Textarea Value="@ProAuthentication.UUID" rows="5" /> | ||||
|                     </div> | ||||
|                 </div> | ||||
|  | ||||
|                 <PopConfirmButton Size="Size.Small" Color="Color.Success" Icon="fa-solid fa-bars" Text="@Localizer["Register"]" IsAsync OnConfirm="Register"> | ||||
| @@ -22,7 +29,7 @@ | ||||
|  | ||||
|             </div> | ||||
|  | ||||
|             <div class="ma-1"> | ||||
|             <div class="ma-1 mt-4"> | ||||
|  | ||||
|                 <div class="row g-3 form-inline"> | ||||
|                     <div class="col-12 col-sm-12"> | ||||
|   | ||||
| @@ -9,21 +9,21 @@ | ||||
| @namespace ThingsGateway.Management | ||||
|  | ||||
|  | ||||
| <div class=@($"{ClassString} row my-2 mx-2") style="min-height:500px;height: 50%;"> | ||||
| <div class=@($"{ClassString} row g-0 h-100 mx-2") style="min-height:500px;"> | ||||
|  | ||||
|     <div class="col-12 h-100"> | ||||
|     <div class="col-12 col-md-6 h-100"> | ||||
|  | ||||
|         <EditComponent Model="Model" OnSave="OnSaveRedundancy"/> | ||||
|         <EditComponent ItemsPerRow=1 Model="Model" OnSave="OnSaveRedundancy" /> | ||||
|  | ||||
|     </div> | ||||
|     <div class="col-12 "> | ||||
|     <div class="col-12 col-md-6 h-100"> | ||||
|  | ||||
|     @if (Logger != null) | ||||
|     { | ||||
|             <LogConsole HeightString="500px" LogLevel=@(Logger.LogLevel) LogLevelChanged="(a)=>{ | ||||
|         @if (Logger != null) | ||||
|         { | ||||
|             <LogConsole LogLevel=@(Logger.LogLevel) LogLevelChanged="(a)=>{ | ||||
| Logger.LogLevel=a; | ||||
| }" LogPath=@LogPath HeaderText=@HeaderText></LogConsole> | ||||
|     } | ||||
|         } | ||||
|     </div> | ||||
| </div> | ||||
|  | ||||
|   | ||||
| @@ -19,7 +19,7 @@ | ||||
|  | ||||
|         <TabItem Text=@RedundancyLocalizer[nameof(RedundancyOptions)]> | ||||
|  | ||||
|             <Card class="mb-2"> | ||||
|             <Card class="h-100"> | ||||
|  | ||||
|                 <HeaderTemplate> | ||||
|  | ||||
| @@ -52,7 +52,7 @@ | ||||
|  | ||||
|         <TabItem Text=@ManagementLocalizer["Restart"]> | ||||
|  | ||||
|             <Card class="mb-2"> | ||||
|             <Card class="h-100"> | ||||
|  | ||||
|                 <BodyTemplate> | ||||
|  | ||||
|   | ||||
| @@ -14,9 +14,7 @@ | ||||
| 
 | ||||
|             <div class="col-12 col-md-8 p-1"> | ||||
| 
 | ||||
|                 <div class="p-1"> | ||||
|                     <Button IsAsync Color="Color.Primary" OnClick="OnUpdate">@ManagementLocalizer["Update"]</Button> | ||||
|                 </div> | ||||
|                  <Button IsAsync Color="Color.Primary" OnClick="OnUpdate">@ManagementLocalizer["Update"]</Button> | ||||
| 
 | ||||
|             </div> | ||||
|             <div class="col-12 col-md-4 p-1"> | ||||
| @@ -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() | ||||
|   | ||||
| @@ -9,7 +9,64 @@ namespace ThingsGateway.RulesEngine; | ||||
| [CategoryNode(Category = "Actuator", ImgUrl = "_content/ThingsGateway.RulesEngine/img/CSharpScript.svg", Desc = nameof(ExecuteScriptNode), LocalizerType = typeof(ThingsGateway.RulesEngine._Imports), WidgetType = typeof(CSharpScriptWidget))] | ||||
| public class ExecuteScriptNode : TextNode, IActuatorNode, IExexcuteExpressionsBase, IDisposable | ||||
| { | ||||
|     public ExecuteScriptNode(string id, Point? position = null) : base(id, position) { Title = "ExecuteScriptNode"; Placeholder = "ExecuteScriptNode.Placeholder"; } | ||||
|     public ExecuteScriptNode(string id, Point? position = null) : base(id, position) | ||||
|     { | ||||
|         Title = "ExecuteScriptNode"; Placeholder = "ExecuteScriptNode.Placeholder"; | ||||
|         Text = | ||||
|             """ | ||||
|             using ThingsGateway.RulesEngine; | ||||
|             using ThingsGateway.Foundation; | ||||
|             using TouchSocket.Core; | ||||
|             using System.Text; | ||||
|  | ||||
|             public class TestEx : IExexcuteExpressions | ||||
|             { | ||||
|  | ||||
|                 public TouchSocket.Core.ILog Logger { get; set; } | ||||
|  | ||||
|                 public async System.Threading.Tasks.Task<NodeOutput> ExecuteAsync(NodeInput input, System.Threading.CancellationToken cancellationToken) | ||||
|                 { | ||||
|  | ||||
|  | ||||
|                     //想上传mqtt,可以自己写mqtt上传代码,或者通过mqtt插件的公开方法上传 | ||||
|  | ||||
|                     //直接获取mqttclient插件类型的第一个设备 | ||||
|                     var driver = GlobalData.ReadOnlyChannels.FirstOrDefault(a => a.Value.PluginName == "ThingsGateway.Plugin.Mqtt.MqttClient").Value?.ReadDeviceRuntimes?.FirstOrDefault().Value?.Driver; | ||||
|                     if (driver != null) | ||||
|                     { | ||||
|                         //找到对应的MqttClient插件设备 | ||||
|                         var mqttClient = (ThingsGateway.Plugin.Mqtt.MqttClient)driver; | ||||
|                         if (mqttClient == null) | ||||
|                             throw new("mqttClient NOT FOUND"); | ||||
|                         var result = await mqttClient.MqttUpAsync("test", Encoding.UTF8.GetBytes("test"),1, default);// 主题 和 负载 | ||||
|                         if (!result.IsSuccess) | ||||
|                             throw new(result.ErrorMessage); | ||||
|                         return new NodeOutput() { Value = result }; | ||||
|                     } | ||||
|                     throw new("mqttClient NOT FOUND"); | ||||
|  | ||||
|  | ||||
|                     //通过设备名称找出mqttClient插件 | ||||
|                     //var driver = GlobalData.ReadOnlyDevices.FirstOrDefault(a => a.Value.Name == "mqttDevice1").Value?.Driver; | ||||
|                     //if (driver != null) | ||||
|                     //{ | ||||
|                     //    //找到对应的MqttClient插件设备 | ||||
|                     //    var mqttClient = (ThingsGateway.Plugin.Mqtt.MqttClient)driver; | ||||
|                     //    if (mqttClient == null) | ||||
|                     //        throw new("mqttClient NOT FOUND"); | ||||
|                     //    var result = await mqttClient.MqttUpAsync("test", "test", default);// 主题 和 负载 | ||||
|                     //    if (!result.IsSuccess) | ||||
|                     //        throw new(result.ErrorMessage); | ||||
|                     //    return new NodeOutput() { Value = result }; | ||||
|                     //} | ||||
|                     //throw new("mqttClient NOT FOUND"); | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|  | ||||
|             """; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private string text; | ||||
|  | ||||
|   | ||||
| @@ -11,7 +11,14 @@ namespace ThingsGateway.RulesEngine; | ||||
| [CategoryNode(Category = "Expression", ImgUrl = "_content/ThingsGateway.RulesEngine/img/CSharpScript.svg", Desc = nameof(ConditionNode), LocalizerType = typeof(ThingsGateway.RulesEngine._Imports), WidgetType = typeof(CSharpScriptWidget))] | ||||
| public class ConditionNode : TextNode, IConditionNode | ||||
| { | ||||
|     public ConditionNode(string id, Point? position = null) : base(id, position) { Title = "ConditionNode"; Placeholder = "ConditionNode.Placeholder"; } | ||||
|     public ConditionNode(string id, Point? position = null) : base(id, position) | ||||
|     { | ||||
|         Title = "ConditionNode"; Placeholder = "ConditionNode.Placeholder"; | ||||
|         Text = "return true;"; | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|     Task<bool> IConditionNode.ExecuteAsync(NodeInput input, CancellationToken cancellationToken) | ||||
|     { | ||||
|   | ||||
| @@ -8,7 +8,12 @@ namespace ThingsGateway.RulesEngine; | ||||
| [CategoryNode(Category = "Expression", ImgUrl = "_content/ThingsGateway.RulesEngine/img/CSharpScript.svg", Desc = nameof(DataNode), LocalizerType = typeof(ThingsGateway.RulesEngine._Imports), WidgetType = typeof(CSharpScriptWidget))] | ||||
| public class DataNode : TextNode, IExpressionNode | ||||
| { | ||||
|     public DataNode(string id, Point? position = null) : base(id, position) { Title = "DataNode"; Placeholder = "DataNode.Placeholder"; } | ||||
|     public DataNode(string id, Point? position = null) : base(id, position) | ||||
|     { | ||||
|         Title = "DataNode"; Placeholder = "DataNode.Placeholder"; | ||||
|         Text = "return 1;"; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     Task<NodeOutput> IExpressionNode.ExecuteAsync(NodeInput input, CancellationToken cancellationToken) | ||||
|     { | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> | ||||
| 		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.5.375.457" /> | ||||
| 		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.5.376.213" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -184,7 +184,7 @@ public static class JsonUtils | ||||
|     /// CreateEncoder | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     private static OPCUAJsonEncoder CreateEncoder( | ||||
|     private static JsonEncoder CreateEncoder( | ||||
|         IServiceMessageContext context, | ||||
|         Stream stream, | ||||
|         bool useReversibleEncoding = false, | ||||
| @@ -193,14 +193,14 @@ public static class JsonUtils | ||||
|         bool includeDefaultNumbers = true | ||||
|         ) | ||||
|     { | ||||
|         return new OPCUAJsonEncoder(context, useReversibleEncoding, topLevelIsArray, stream) | ||||
|         return new JsonEncoder(context, useReversibleEncoding, topLevelIsArray, stream) | ||||
|         { | ||||
|             IncludeDefaultValues = includeDefaultValues, | ||||
|             IncludeDefaultNumberValues = includeDefaultNumbers | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     private static void Encode(OPCUAJsonEncoder encoder, BuiltInType builtInType, string fieldName, object value) | ||||
|     private static void Encode(JsonEncoder encoder, BuiltInType builtInType, string fieldName, object value) | ||||
|     { | ||||
|         bool isArray = (value?.GetType().IsArray ?? false) && (builtInType != BuiltInType.ByteString); | ||||
|         bool isCollection = (value is IList) && (builtInType != BuiltInType.ByteString); | ||||
|   | ||||
| @@ -398,10 +398,10 @@ public partial class SiemensS7Master : DeviceBase | ||||
|  | ||||
|             try | ||||
|             { | ||||
|                 var result2 = await GetResponsedDataAsync(new S7Send(ISO_CR), channel, Timeout).ConfigureAwait(false); | ||||
|                 var result2 = await SendThenReturnMessageBaseAsync(new S7Send(ISO_CR), channel).ConfigureAwait(false); | ||||
|                 if (!result2.IsSuccess) | ||||
|                 { | ||||
|                     Logger?.LogWarning(SiemensS7Resource.Localizer["HandshakeError1", channel.ToString(), result2.ErrorMessage]); | ||||
|                     Logger?.LogWarning(SiemensS7Resource.Localizer["HandshakeError1", channel.ToString(), result2]); | ||||
|                     await channel.CloseAsync().ConfigureAwait(false); | ||||
|                     return true; | ||||
|                 } | ||||
| @@ -409,16 +409,21 @@ public partial class SiemensS7Master : DeviceBase | ||||
|             catch (OperationCanceledException) { } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 Logger?.LogWarning(SiemensS7Resource.Localizer["HandshakeError1", channel.ToString(), ex.Message]); | ||||
|                 Logger?.LogWarning(SiemensS7Resource.Localizer["HandshakeError1", channel.ToString(), ex]); | ||||
|                 await channel.CloseAsync().ConfigureAwait(false); | ||||
|                 return true; | ||||
|             } | ||||
|             try | ||||
|             { | ||||
|                 var result2 = await GetResponsedDataAsync(new S7Send(S7_PN), channel, Timeout).ConfigureAwait(false); | ||||
|                 var result2 = await SendThenReturnMessageBaseAsync(new S7Send(S7_PN), channel).ConfigureAwait(false); | ||||
|                 if (!result2.IsSuccess) | ||||
|                 { | ||||
|                     Logger?.LogWarning(SiemensS7Resource.Localizer["HandshakeError2", channel.ToString(), result2.ErrorMessage]); | ||||
|                     Logger?.LogWarning(SiemensS7Resource.Localizer["HandshakeError2", channel.ToString(), result2]); | ||||
|                     await channel.CloseAsync().ConfigureAwait(false); | ||||
|                     return true; | ||||
|                 } | ||||
|                 if (result2.Content == null) | ||||
|                 { | ||||
|                     await channel.CloseAsync().ConfigureAwait(false); | ||||
|                     return true; | ||||
|                 } | ||||
| @@ -429,7 +434,7 @@ public partial class SiemensS7Master : DeviceBase | ||||
|             catch (OperationCanceledException) { } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 Logger?.LogWarning(SiemensS7Resource.Localizer["HandshakeError2", channel.ToString(), ex.Message]); | ||||
|                 Logger?.LogWarning(SiemensS7Resource.Localizer["HandshakeError2", channel.ToString(), ex]); | ||||
|                 await channel.CloseAsync().ConfigureAwait(false); | ||||
|                 return true; | ||||
|             } | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
|     </PackageReference> | ||||
|     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" /> | ||||
|     <PackageReference Include="xunit" Version="2.9.3" /> | ||||
|     <PackageReference Include="xunit.runner.visualstudio" Version="3.0.2"> | ||||
|     <PackageReference Include="xunit.runner.visualstudio" Version="3.1.0"> | ||||
|       <PrivateAssets>all</PrivateAssets> | ||||
|       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
|     </PackageReference> | ||||
|   | ||||
| @@ -25,11 +25,20 @@ | ||||
|                         <div class="col-12  col-md-12"> | ||||
|                             <BootstrapLabel Value=@SqlDBProducerPropertyLocalizer["BigTextScriptHistoryTable"] ShowLabelTooltip="true" /> | ||||
|                             <CodeEditor @bind-Value=@businessProperty.BigTextScriptHistoryTable Language="csharp" Theme="vs-dark" /> | ||||
|  | ||||
|                             <div class="ms-2 d-flex justify-content-center align-items-center"> | ||||
|                                 <Button IsDisabled=@(!CanWrite) OnClick="()=>CheckScript(businessProperty,nameof(businessProperty.BigTextScriptHistoryTable))"> | ||||
|                                     @RazorLocalizer["Check"] | ||||
|                                 </Button> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                         <div class="col-12  col-md-12"> | ||||
|                             <BootstrapLabel Value=@SqlDBProducerPropertyLocalizer["BigTextScriptRealTable"] ShowLabelTooltip="true" /> | ||||
|                             <CodeEditor @bind-Value=@businessProperty.BigTextScriptRealTable Language="csharp" Theme="vs-dark" /> | ||||
|                             <div class="ms-2 d-flex justify-content-center align-items-center"> | ||||
|                                 <Button IsDisabled=@(!CanWrite) OnClick="()=>CheckScript(businessProperty,nameof(businessProperty.BigTextScriptRealTable))"> | ||||
|                                     @RazorLocalizer["Check"] | ||||
|                                 </Button> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     </EditTemplate> | ||||
|                 </EditorItem> | ||||
|   | ||||
| @@ -8,17 +8,22 @@ | ||||
| // QQ群:605534569 | ||||
| // ------------------------------------------------------------------------------ | ||||
|  | ||||
| #pragma warning disable CA2007 // 考虑对等待的任务调用 ConfigureAwait | ||||
| using BootstrapBlazor.Components; | ||||
|  | ||||
| using Microsoft.AspNetCore.Components; | ||||
| using Microsoft.Extensions.Localization; | ||||
|  | ||||
| using ThingsGateway.Gateway.Razor; | ||||
| using ThingsGateway.Plugin.SqlDB; | ||||
| using ThingsGateway.Razor; | ||||
|  | ||||
| namespace ThingsGateway.Debug | ||||
| { | ||||
|     public partial class SqlDBProducerPropertyRazor : IPropertyUIBase | ||||
|     { | ||||
|         [Inject] | ||||
|         IStringLocalizer<ThingsGateway.Razor._Imports> RazorLocalizer { get; set; } | ||||
|  | ||||
|  | ||||
|         [Parameter, EditorRequired] | ||||
| @@ -39,6 +44,144 @@ namespace ThingsGateway.Debug | ||||
|             return base.OnParametersSetAsync(); | ||||
|         } | ||||
|  | ||||
|         private async Task CheckScript(SqlDBProducerProperty businessProperty, string pname) | ||||
|         { | ||||
|             IEnumerable<object> data = null; | ||||
|             string script = null; | ||||
|             { | ||||
|                 data = new List<VariableBasicData>() { new() { | ||||
|                 Name = "testName", | ||||
|                 DeviceName = "testDevice", | ||||
|                 Value = "1", | ||||
|                 ChangeTime = DateTime.Now, | ||||
|                 CollectTime = DateTime.Now, | ||||
|                 Remark1="1", | ||||
|                 Remark2="2", | ||||
|                 Remark3="3", | ||||
|                 Remark4="4", | ||||
|                 Remark5="5", | ||||
|             } , | ||||
|              new() { | ||||
|                 Name = "testName2", | ||||
|                 DeviceName = "testDevice", | ||||
|                 Value = "1", | ||||
|                 ChangeTime = DateTime.Now, | ||||
|                 CollectTime = DateTime.Now, | ||||
|                 Remark1="1", | ||||
|                 Remark2="2", | ||||
|                 Remark3="3", | ||||
|                 Remark4="4", | ||||
|                 Remark5="5", | ||||
|             } }; | ||||
|                 script = pname == businessProperty.BigTextScriptHistoryTable ? businessProperty.BigTextScriptHistoryTable : businessProperty.BigTextScriptRealTable; | ||||
|  | ||||
|             } | ||||
|  | ||||
|  | ||||
|             var op = new DialogOption() | ||||
|             { | ||||
|                 IsScrolling = true, | ||||
|                 Title = RazorLocalizer["Check"], | ||||
|                 ShowFooter = false, | ||||
|                 ShowCloseButton = false, | ||||
|                 Size = Size.ExtraExtraLarge, | ||||
|                 FullScreenSize = FullScreenSize.None | ||||
|             }; | ||||
|  | ||||
|             op.Component = BootstrapDynamicComponent.CreateComponent<ScriptCheck>(new Dictionary<string, object?> | ||||
|     { | ||||
|         {nameof(ScriptCheck.Data),data }, | ||||
|         {nameof(ScriptCheck.Script),script }, | ||||
|         {nameof(ScriptCheck.OnGetDemo),()=> | ||||
|                 { | ||||
|                     return | ||||
|                     pname == nameof(SqlDBProducerProperty.BigTextScriptHistoryTable)? | ||||
|                     """" | ||||
|                     using ThingsGateway.Foundation; | ||||
|                      | ||||
|                     using System.Dynamic; | ||||
|                      | ||||
|                     using TouchSocket.Core; | ||||
|                     public class S1 : DynamicSQLBase | ||||
|                     { | ||||
|  | ||||
|                         public override async Task DBInit(ISqlSugarClient db, CancellationToken cancellationToken) | ||||
|                         { | ||||
|  | ||||
|                             var sql = $""" | ||||
|  | ||||
|                                     """; | ||||
|                             await db.Ado.ExecuteCommandAsync(sql, default, cancellationToken: cancellationToken).ConfigureAwait(false); | ||||
|                         } | ||||
|                         public override async Task DBInsertable(ISqlSugarClient db, IEnumerable<object> datas, CancellationToken cancellationToken) | ||||
|                         { | ||||
|                             var sql = $""" | ||||
|  | ||||
|                                     """; | ||||
|                             await db.Ado.ExecuteCommandAsync(sql, default, cancellationToken: cancellationToken).ConfigureAwait(false); | ||||
|                         } | ||||
|                     } | ||||
|                      | ||||
|                     """" | ||||
|                     : | ||||
|  | ||||
|                     pname == nameof(SqlDBProducerProperty.BigTextScriptRealTable)? | ||||
|  | ||||
|                     """" | ||||
|  | ||||
|                     using System.Dynamic; | ||||
|                     using ThingsGateway.Foundation; | ||||
|                      | ||||
|  | ||||
|                     using TouchSocket.Core; | ||||
|                     public class S1 : DynamicSQLBase | ||||
|                     { | ||||
|  | ||||
|                         public override async Task DBInit(ISqlSugarClient db, CancellationToken cancellationToken) | ||||
|                         { | ||||
|  | ||||
|                             var sql = $""" | ||||
|  | ||||
|                                     """; | ||||
|                             await db.Ado.ExecuteCommandAsync(sql, default, cancellationToken: cancellationToken).ConfigureAwait(false); | ||||
|                         } | ||||
|                         public override async Task DBInsertable(ISqlSugarClient db, IEnumerable<object> datas, CancellationToken cancellationToken) | ||||
|                         { | ||||
|                             var sql = $""" | ||||
|  | ||||
|                                     """; | ||||
|                             await db.Ado.ExecuteCommandAsync(sql, default, cancellationToken: cancellationToken).ConfigureAwait(false); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     """" | ||||
|                     : | ||||
|                     "" | ||||
|                     ; | ||||
|                 } | ||||
|             }, | ||||
|         {nameof(ScriptCheck.ScriptChanged),EventCallback.Factory.Create<string>(this, v => | ||||
|         { | ||||
|                  if (pname == nameof(SqlDBProducerProperty.BigTextScriptHistoryTable)) | ||||
|     { | ||||
|             businessProperty.BigTextScriptHistoryTable=v; | ||||
|  | ||||
|     } | ||||
| } | ||||
|     else if (pname == nameof(SqlDBProducerProperty.BigTextScriptRealTable)) | ||||
|     { | ||||
|            businessProperty.BigTextScriptRealTable=v; | ||||
|     } | ||||
|  | ||||
|         }) }, | ||||
|  | ||||
|     }); | ||||
|             await DialogService.Show(op); | ||||
|  | ||||
|         } | ||||
|         [Inject] | ||||
|         DialogService DialogService { get; set; } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -8,13 +8,8 @@ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using Mapster; | ||||
|  | ||||
| using Microsoft.Data.Sqlite; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.Reflection; | ||||
| using System.Text.RegularExpressions; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.SqlDB; | ||||
| @@ -25,7 +20,7 @@ public class SqlDBDateSplitTableService : DateSplitTableService | ||||
|  | ||||
|     public SqlDBDateSplitTableService(SqlDBProducerProperty sqlDBProducerProperty) | ||||
|     { | ||||
|         this._sqlDBProducerProperty = sqlDBProducerProperty; | ||||
|         _sqlDBProducerProperty = sqlDBProducerProperty; | ||||
|     } | ||||
|  | ||||
|     #region Core | ||||
|   | ||||
| @@ -166,7 +166,7 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariableModel< | ||||
|                 { | ||||
|                     if (datas?.Count > 0) | ||||
|                     { | ||||
|                         var result = await db.Storageable(datas).As(_driverPropertys.ReadDBTableName).PageSize(5000).ExecuteSqlBulkCopyAsync().ConfigureAwait(false); | ||||
|                         var result = await db.Storageable(datas).As(_driverPropertys.ReadDBTableName).PageSize(5000).ExecuteSqlBulkCopyAsync(cancellationToken).ConfigureAwait(false); | ||||
|                         if (result > 0) | ||||
|                             LogMessage.Trace($"RealTable Data Count:{result}"); | ||||
|                         _initRealData = true; | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
| @@ -135,11 +133,13 @@ public partial class Webhook : BusinessBaseWithCacheIntervalScript<VariableBasic | ||||
|  | ||||
|     private readonly HttpClient client = new HttpClient(); | ||||
|  | ||||
|     private async Task<OperResult> WebhookUpAsync(string topic, string json, int count, CancellationToken cancellationToken) | ||||
|     private async Task<OperResult> WebhookUpAsync(string topic, byte[] payLoad, int count, CancellationToken cancellationToken) | ||||
|     { | ||||
|  | ||||
|         // 设置请求内容 | ||||
|         var content = new StringContent(json, Encoding.UTF8, "application/json"); | ||||
|         //var content = new StringContent(json, Encoding.UTF8, "application/json"); | ||||
|         using var content = new ByteArrayContent(payLoad); | ||||
|         content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); | ||||
|  | ||||
|         try | ||||
|         { | ||||
| @@ -152,7 +152,7 @@ public partial class Webhook : BusinessBaseWithCacheIntervalScript<VariableBasic | ||||
|                 if (_driverPropertys.DetailLog) | ||||
|                 { | ||||
|                     if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                         LogMessage.LogTrace($"Topic:{topic}{Environment.NewLine}PayLoad:{json} {Environment.NewLine} VarModelQueue:{_memoryVarModelQueue.Count}"); | ||||
|                         LogMessage.LogTrace(GetString(topic, payLoad, _memoryVarModelQueue.Count)); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
| @@ -175,11 +175,12 @@ public partial class Webhook : BusinessBaseWithCacheIntervalScript<VariableBasic | ||||
|  | ||||
|     #region private | ||||
|  | ||||
|     private async ValueTask<OperResult> Update(List<TopicJson> topicJsonList, int count, CancellationToken cancellationToken) | ||||
|     private async ValueTask<OperResult> Update(List<TopicArray> topicJsonList, int count, CancellationToken cancellationToken) | ||||
|     { | ||||
|         foreach (var topicJson in topicJsonList) | ||||
|         { | ||||
|             var result = await WebhookUpAsync(topicJson.Topic, topicJson.Json, count, cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|                 return result; | ||||
|             if (success != result.IsSuccess) | ||||
| @@ -202,20 +203,20 @@ public partial class Webhook : BusinessBaseWithCacheIntervalScript<VariableBasic | ||||
|  | ||||
|     private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken) | ||||
|     { | ||||
|         List<TopicJson> topicJsonList = GetAlarms(item); | ||||
|         var topicJsonList = GetAlarmTopicArrays(item); | ||||
|         return Update(topicJsonList, item.Count(), cancellationToken); | ||||
|     } | ||||
|  | ||||
|     private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken) | ||||
|     { | ||||
|  | ||||
|         List<TopicJson> topicJsonList = GetDeviceData(item); | ||||
|         var topicJsonList = GetDeviceTopicArray(item); | ||||
|         return Update(topicJsonList, item.Count(), cancellationToken); | ||||
|     } | ||||
|  | ||||
|     private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken) | ||||
|     { | ||||
|         List<TopicJson> topicJsonList = GetVariableBasicData(item); | ||||
|         var topicJsonList = GetVariableBasicDataTopicArray(item); | ||||
|         return Update(topicJsonList, item.Count(), cancellationToken); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -46,7 +46,7 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<Variabl | ||||
|             _producerconfig.SaslPassword = _driverPropertys.SaslPassword; | ||||
|  | ||||
|         //2、创建生产者 | ||||
|         _producerBuilder = new ProducerBuilder<Null, string>(_producerconfig); | ||||
|         _producerBuilder = new ProducerBuilder<Null, byte[]>(_producerconfig); | ||||
|         //3、错误日志监视 | ||||
|         _producerBuilder.SetErrorHandler((p, msg) => | ||||
|         { | ||||
| @@ -75,6 +75,14 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<Variabl | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             _producer?.Flush(TimeSpan.FromSeconds(10)); | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|  | ||||
|         } | ||||
|         _producer?.SafeDispose(); | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|   | ||||
| @@ -24,8 +24,8 @@ namespace ThingsGateway.Plugin.Kafka; | ||||
| /// </summary> | ||||
| public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<VariableBasicData, DeviceBasicData, AlarmVariable> | ||||
| { | ||||
|     private IProducer<Null, string> _producer; | ||||
|     private ProducerBuilder<Null, string> _producerBuilder; | ||||
|     private IProducer<Null, byte[]> _producer; | ||||
|     private ProducerBuilder<Null, byte[]> _producerBuilder; | ||||
|     private ProducerConfig _producerconfig; | ||||
|     private volatile bool producerSuccess = true; | ||||
|  | ||||
| @@ -127,7 +127,7 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<Variabl | ||||
|  | ||||
|     #region private | ||||
|  | ||||
|     private async ValueTask<OperResult> Update(List<TopicJson> topicJsonList, int count, CancellationToken cancellationToken) | ||||
|     private async ValueTask<OperResult> Update(List<TopicArray> topicJsonList, int count, CancellationToken cancellationToken) | ||||
|     { | ||||
|         foreach (var topicJson in topicJsonList) | ||||
|         { | ||||
| @@ -150,19 +150,19 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<Variabl | ||||
|  | ||||
|     private async ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken) | ||||
|     { | ||||
|         List<TopicJson> topicJsonList = GetAlarms(item); | ||||
|         var topicJsonList = GetAlarmTopicArrays(item); | ||||
|         return await Update(topicJsonList, item.Count(), cancellationToken).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     private async ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken) | ||||
|     { | ||||
|         List<TopicJson> topicJsonList = GetDeviceData(item); | ||||
|         var topicJsonList = GetDeviceTopicArray(item); | ||||
|         return await Update(topicJsonList, item.Count(), cancellationToken).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     private async ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken) | ||||
|     { | ||||
|         List<TopicJson> topicJsonList = GetVariableBasicData(item); | ||||
|         var topicJsonList = GetVariableBasicDataTopicArray(item); | ||||
|         return await Update(topicJsonList, item.Count(), cancellationToken).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
| @@ -203,42 +203,36 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<Variabl | ||||
|     /// <summary> | ||||
|     /// kafka上传,返回上传结果 | ||||
|     /// </summary> | ||||
|     public async ValueTask<OperResult> KafKaUpAsync(string topic, string payLoad, int count, CancellationToken cancellationToken) | ||||
|     public async ValueTask<OperResult> KafKaUpAsync(string topic, byte[] payLoad, int count, CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             using CancellationTokenSource cancellationTokenSource = new(); | ||||
|             using CancellationTokenSource cancellationTokenSource = new(_driverPropertys.Timeout); | ||||
|             using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, cancellationToken); | ||||
|             Task<DeliveryResult<Null, string>> resultTask = _producer.ProduceAsync(topic, new Message<Null, string> { Value = payLoad }, stoppingToken.Token); | ||||
|             var timeOutResult = await Task.WhenAny(resultTask, Task.Delay(_driverPropertys.Timeout, stoppingToken.Token)).ConfigureAwait(false); | ||||
|             if (timeOutResult == resultTask) | ||||
|             var result = await _producer.ProduceAsync(topic, new Message<Null, byte[]> { Value = payLoad }, stoppingToken.Token).ConfigureAwait(false); | ||||
|             if (result.Status != PersistenceStatus.Persisted) | ||||
|             { | ||||
|                 var result = await resultTask.ConfigureAwait(false); | ||||
|                 if (result.Status != PersistenceStatus.Persisted) | ||||
|                 { | ||||
|                     return new OperResult("Upload fail"); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     if (_driverPropertys.DetailLog) | ||||
|                     { | ||||
|                         if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                             LogMessage.LogTrace($"Topic:{topic}{Environment.NewLine}PayLoad:{payLoad} {Environment.NewLine} VarModelQueue:{_memoryVarModelQueue.Count}"); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         LogMessage.LogTrace($"Topic:{topic}{Environment.NewLine}Count:{count} {Environment.NewLine} VarModelQueue:{_memoryVarModelQueue.Count}"); | ||||
|  | ||||
|                     } | ||||
|                     return OperResult.Success; | ||||
|                 } | ||||
|                 return new OperResult("Upload fail"); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 stoppingToken.Cancel(); | ||||
|                 return new OperResult("Upload timeout"); | ||||
|                 if (_driverPropertys.DetailLog) | ||||
|                 { | ||||
|                     if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                         LogMessage.LogTrace(GetString(topic, payLoad, _memoryVarModelQueue.Count)); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     LogMessage.LogTrace($"Topic:{topic}{Environment.NewLine}Count:{count} {Environment.NewLine} VarModelQueue:{_memoryVarModelQueue.Count}"); | ||||
|  | ||||
|                 } | ||||
|                 return OperResult.Success; | ||||
|             } | ||||
|         } | ||||
|         catch (OperationCanceledException) | ||||
|         { | ||||
|             return new OperResult("Timeout"); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|   | ||||
| @@ -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; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -60,31 +60,35 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableBa | ||||
|  | ||||
|     private async ValueTask<OperResult> UpdateThingsBoardDeviceConnect(DeviceBasicData deviceData) | ||||
|     { | ||||
|         List<TopicJson> topicJsonTBList = new(); | ||||
|         var topicJsonTBList = new List<TopicArray>(); | ||||
|  | ||||
|         { | ||||
|             if (deviceData.DeviceStatus == DeviceStatusEnum.OnLine) | ||||
|             { | ||||
|                 var topicJson = new TopicJson() | ||||
|                 var json = new | ||||
|                 { | ||||
|                     device = deviceData.Name, | ||||
|                 }; | ||||
|                 var topicJson = new TopicArray() | ||||
|                 { | ||||
|                     Topic = "v1/gateway/connect", | ||||
|                     Json = new | ||||
|                     { | ||||
|                         device = deviceData.Name, | ||||
|                     }.ToJsonNetString(_driverPropertys.JsonFormattingIndented) | ||||
|                     Json = Serialize(json, _driverPropertys.JsonFormattingIndented) | ||||
|                 }; | ||||
|  | ||||
|                 topicJsonTBList.Add(topicJson); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var topicJson = new TopicJson() | ||||
|                 var json = new | ||||
|                 { | ||||
|                     device = deviceData.Name, | ||||
|                 }; | ||||
|                 var topicJson = new TopicArray() | ||||
|                 { | ||||
|                     Topic = "v1/gateway/disconnect", | ||||
|                     Json = new | ||||
|                     { | ||||
|                         device = deviceData.Name, | ||||
|                     }.ToJsonNetString(_driverPropertys.JsonFormattingIndented) | ||||
|                     Json = Serialize(json, _driverPropertys.JsonFormattingIndented) | ||||
|                 }; | ||||
|  | ||||
|                 topicJsonTBList.Add(topicJson); | ||||
|             } | ||||
|  | ||||
| @@ -202,7 +206,7 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableBa | ||||
|  | ||||
|     #region private | ||||
|  | ||||
|     private async ValueTask<OperResult> Update(List<TopicJson> topicJsonList, int count, CancellationToken cancellationToken) | ||||
|     private async ValueTask<OperResult> Update(List<TopicArray> topicJsonList, int count, CancellationToken cancellationToken) | ||||
|     { | ||||
|         foreach (var topicJson in topicJsonList) | ||||
|         { | ||||
| @@ -227,20 +231,20 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableBa | ||||
|  | ||||
|     private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken) | ||||
|     { | ||||
|         List<TopicJson> topicJsonList = GetAlarms(item); | ||||
|         var topicJsonList = GetAlarmTopicArrays(item); | ||||
|         return Update(topicJsonList, item.Count(), cancellationToken); | ||||
|     } | ||||
|  | ||||
|     private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken) | ||||
|     { | ||||
|  | ||||
|         List<TopicJson> topicJsonList = GetDeviceData(item); | ||||
|         var topicJsonList = GetDeviceTopicArray(item); | ||||
|         return Update(topicJsonList, item.Count(), cancellationToken); | ||||
|     } | ||||
|  | ||||
|     private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken) | ||||
|     { | ||||
|         List<TopicJson> topicJsonList = GetVariableBasicData(item); | ||||
|         var topicJsonList = GetVariableBasicDataTopicArray(item); | ||||
|         return Update(topicJsonList, item.Count(), cancellationToken); | ||||
|     } | ||||
|  | ||||
| @@ -459,7 +463,7 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableBa | ||||
|     /// <summary> | ||||
|     /// 上传mqtt,返回上传结果 | ||||
|     /// </summary> | ||||
|     public async ValueTask<OperResult> MqttUpAsync(string topic, string payLoad, int count, CancellationToken cancellationToken = default) | ||||
|     public async ValueTask<OperResult> MqttUpAsync(string topic, byte[] payLoad, int count, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
| @@ -475,7 +479,7 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableBa | ||||
|                     if (_driverPropertys.DetailLog) | ||||
|                     { | ||||
|                         if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                             LogMessage.LogTrace($"Topic:{topic}{Environment.NewLine}PayLoad:{payLoad} {Environment.NewLine} VarModelQueue:{_memoryVarModelQueue.Count}"); | ||||
|                             LogMessage.LogTrace(GetString(topic, payLoad, _memoryVarModelQueue.Count)); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|   | ||||
| @@ -46,7 +46,6 @@ namespace ThingsGateway.Plugin.Mqtt | ||||
|             if (mqttClientProperty.TLS == true) | ||||
|             { | ||||
|                 { | ||||
|  | ||||
|                     var filePath = Path.Combine("PluginFile", Id, nameof(mqttClientProperty.CAFile)); | ||||
|                     if (!Directory.Exists(filePath))//如果不存在就创建文件夹 | ||||
|                         Directory.CreateDirectory(filePath); | ||||
| @@ -230,7 +229,6 @@ namespace ThingsGateway.Plugin.Mqtt | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|  | ||||
|             var op = new DialogOption() | ||||
|             { | ||||
|                 IsScrolling = true, | ||||
| @@ -245,6 +243,142 @@ namespace ThingsGateway.Plugin.Mqtt | ||||
|     { | ||||
|         {nameof(ScriptCheck.Data),data }, | ||||
|         {nameof(ScriptCheck.Script),script }, | ||||
|         {nameof(ScriptCheck.OnGetDemo),()=> | ||||
|                 { | ||||
|                     return | ||||
|                     pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel)? | ||||
|                     """ | ||||
|  | ||||
|                     using TouchSocket.Core; | ||||
|                     public class S1 : IDynamicModel | ||||
|                     { | ||||
|                         public IEnumerable<dynamic> GetList(IEnumerable<object> datas) | ||||
|                         { | ||||
|                             List<ExpandoObject> deviceObjs = new List<ExpandoObject>(); | ||||
|                             foreach (var v in datas) | ||||
|                             { | ||||
|                                 var device = (DeviceBasicData)v; | ||||
|                                 var expando = new ExpandoObject(); | ||||
|                                 var deviceObj = new ExpandoObject(); | ||||
|  | ||||
|                                 deviceObj.TryAdd(nameof(Device.Description), device.Description); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.ActiveTime), device.ActiveTime); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.DeviceStatus), device.DeviceStatus.ToString()); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.LastErrorMessage), device.LastErrorMessage); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.PluginName), device.PluginName); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.Remark1), device.Remark1); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.Remark2), device.Remark2); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.Remark3), device.Remark3); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.Remark4), device.Remark4); | ||||
|                                 deviceObj.TryAdd(nameof(DeviceBasicData.Remark5), device.Remark5); | ||||
|  | ||||
|  | ||||
|                                 expando.TryAdd(nameof(Device.Name), deviceObj); | ||||
|  | ||||
|                             } | ||||
|                             return deviceObjs; | ||||
|                         } | ||||
|                     } | ||||
|                      | ||||
|                     """ | ||||
|                     : | ||||
|  | ||||
|                     pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptVariableModel)? | ||||
|  | ||||
|                     """ | ||||
|  | ||||
|                     using TouchSocket.Core; | ||||
|                     public class S2 : IDynamicModel | ||||
|                     { | ||||
|                         public IEnumerable<dynamic> GetList(IEnumerable<object> datas) | ||||
|                         { | ||||
|  | ||||
|                             List<ExpandoObject> deviceObjs = new List<ExpandoObject>(); | ||||
|                             //按设备名称分组 | ||||
|                             var groups = datas.Where(a => !string.IsNullOrEmpty(((VariableBasicData)a).DeviceName)).GroupBy(a => ((VariableBasicData)a).DeviceName, a => ((VariableBasicData)a)); | ||||
|                             foreach (var group in groups) | ||||
|                             { | ||||
|                                 //按采集时间分组 | ||||
|                                 var data = group.GroupBy(a => a.CollectTime.DateTimeToUnixTimestamp()); | ||||
|                                 var deviceObj = new ExpandoObject(); | ||||
|                                 List<ExpandoObject> expandos = new List<ExpandoObject>(); | ||||
|                                 foreach (var item in data) | ||||
|                                 { | ||||
|                                     var expando = new ExpandoObject(); | ||||
|                                     expando.TryAdd("ts", item.Key); | ||||
|                                     var variableObj = new ExpandoObject(); | ||||
|                                     foreach (var tag in item) | ||||
|                                     { | ||||
|                                         variableObj.TryAdd(tag.Name, tag.Value); | ||||
|                                     } | ||||
|                                     expando.TryAdd("values", variableObj); | ||||
|  | ||||
|                                     expandos.Add(expando); | ||||
|                                 } | ||||
|                                 deviceObj.TryAdd(group.Key, expandos); | ||||
|                                 deviceObjs.Add(deviceObj); | ||||
|                             } | ||||
|  | ||||
|                             return deviceObjs; | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     """ | ||||
|                     : | ||||
|  | ||||
|                     pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel)? | ||||
|  | ||||
|                     """ | ||||
|                                          | ||||
|                     using TouchSocket.Core; | ||||
|                     public class DeviceScript : IDynamicModel | ||||
|                     { | ||||
|                         public IEnumerable<dynamic> GetList(IEnumerable<object> datas) | ||||
|                         { | ||||
|  | ||||
|                             List<ExpandoObject> deviceObjs = new List<ExpandoObject>(); | ||||
|                             //按设备名称分组 | ||||
|                             var groups = datas.Where(a => !string.IsNullOrEmpty(((AlarmVariable)a).DeviceName)).GroupBy(a => ((AlarmVariable)a).DeviceName, a => ((AlarmVariable)a)); | ||||
|                             foreach (var group in groups) | ||||
|                             { | ||||
|                                 //按采集时间分组 | ||||
|                                 var data = group.GroupBy(a => a.AlarmTime.DateTimeToUnixTimestamp()); | ||||
|                                 var deviceObj = new ExpandoObject(); | ||||
|                                 List<ExpandoObject> expandos = new List<ExpandoObject>(); | ||||
|                                 foreach (var item in data) | ||||
|                                 { | ||||
|                                     var expando = new ExpandoObject(); | ||||
|                                     expando.TryAdd("ts", item.Key); | ||||
|                                     var variableObj = new ExpandoObject(); | ||||
|                                     foreach (var tag in item) | ||||
|                                     { | ||||
|                                         var alarmObj = new ExpandoObject(); | ||||
|                                         alarmObj.TryAdd(nameof(tag.AlarmCode), tag.AlarmCode); | ||||
|                                         alarmObj.TryAdd(nameof(tag.AlarmText), tag.AlarmText); | ||||
|                                         alarmObj.TryAdd(nameof(tag.AlarmType), tag.AlarmType); | ||||
|                                         alarmObj.TryAdd(nameof(tag.AlarmLimit), tag.AlarmLimit); | ||||
|                                         alarmObj.TryAdd(nameof(tag.EventTime), tag.EventTime); | ||||
|                                         alarmObj.TryAdd(nameof(tag.EventType), tag.EventType); | ||||
|  | ||||
|                                         variableObj.TryAdd(tag.Name, alarmObj); | ||||
|                                     } | ||||
|                                     expando.TryAdd("alarms", variableObj); | ||||
|  | ||||
|                                     expandos.Add(expando); | ||||
|                                 } | ||||
|                                 deviceObj.TryAdd(group.Key, expandos); | ||||
|                                 deviceObjs.Add(deviceObj); | ||||
|                             } | ||||
|  | ||||
|                             return deviceObjs; | ||||
|                         } | ||||
|                     } | ||||
|                     """ | ||||
|                     : | ||||
|                     "" | ||||
|                     ; | ||||
|                 } | ||||
|             }, | ||||
|         {nameof(ScriptCheck.ScriptChanged),EventCallback.Factory.Create<string>(this, v => | ||||
|         { | ||||
|                  if (pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel)) | ||||
| @@ -269,6 +403,7 @@ namespace ThingsGateway.Plugin.Mqtt | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         [Inject] | ||||
|         private DialogService DialogService { get; set; } | ||||
|     } | ||||
|   | ||||
| @@ -133,41 +133,6 @@ public partial class MqttCollect : CollectBase | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 上传mqtt,返回上传结果 | ||||
|     /// </summary> | ||||
|     private async ValueTask<OperResult> MqttUpAsync(string topic, string payLoad, CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var isConnect = await TryMqttClientAsync(cancellationToken).ConfigureAwait(false); | ||||
|             if (isConnect.IsSuccess) | ||||
|             { | ||||
|                 var variableMessage = new MqttApplicationMessageBuilder() | ||||
|     .WithTopic(topic).WithRetainFlag(true) | ||||
|     .WithPayload(payLoad).Build(); | ||||
|                 var result = await _mqttClient.PublishAsync(variableMessage, cancellationToken).ConfigureAwait(false); | ||||
|                 if (result.IsSuccess) | ||||
|                 { | ||||
|                     if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                         LogMessage.LogTrace($"Topic:{topic}{Environment.NewLine}PayLoad:{payLoad}"); | ||||
|                     return OperResult.Success; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return new OperResult($"Upload fail{result.ReasonString}"); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return isConnect; | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult($"Upload fail", ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async ValueTask<OperResult> TryMqttClientAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|   | ||||
| @@ -141,7 +141,7 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableBa | ||||
|     } | ||||
|     #region private | ||||
|  | ||||
|     private async ValueTask<OperResult> Update(List<TopicJson> topicJsonList, int count, CancellationToken cancellationToken) | ||||
|     private async ValueTask<OperResult> Update(List<TopicArray> topicJsonList, int count, CancellationToken cancellationToken) | ||||
|     { | ||||
|         foreach (var topicJson in topicJsonList) | ||||
|         { | ||||
| @@ -164,20 +164,20 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableBa | ||||
|  | ||||
|     private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken) | ||||
|     { | ||||
|         List<TopicJson> topicJsonList = GetAlarms(item); | ||||
|         var topicJsonList = GetAlarmTopicArrays(item); | ||||
|  | ||||
|         return Update(topicJsonList, item.Count(), cancellationToken); | ||||
|     } | ||||
|  | ||||
|     private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken) | ||||
|     { | ||||
|         List<TopicJson> topicJsonList = GetDeviceData(item); | ||||
|         var topicJsonList = GetDeviceTopicArray(item); | ||||
|         return Update(topicJsonList, item.Count(), cancellationToken); | ||||
|     } | ||||
|  | ||||
|     private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken) | ||||
|     { | ||||
|         List<TopicJson> topicJsonList = GetVariableBasicData(item); | ||||
|         var topicJsonList = GetVariableBasicDataTopicArray(item); | ||||
|         return Update(topicJsonList, item.Count(), cancellationToken); | ||||
|     } | ||||
|  | ||||
| @@ -261,7 +261,7 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableBa | ||||
|         { | ||||
|             foreach (var item in varData) | ||||
|             { | ||||
|                 List<TopicJson> topicJsonList = GetVariableBasicData(item); | ||||
|                 var topicJsonList = GetVariableBasicData(item); | ||||
|                 foreach (var topicJson in topicJsonList) | ||||
|                 { | ||||
|                     Messages.Add(new MqttApplicationMessageBuilder() | ||||
| @@ -276,7 +276,7 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableBa | ||||
|             { | ||||
|                 foreach (var item in devData) | ||||
|                 { | ||||
|                     List<TopicJson> topicJsonList = GetDeviceData(item); | ||||
|                     var topicJsonList = GetDeviceData(item); | ||||
|                     foreach (var topicJson in topicJsonList) | ||||
|                     { | ||||
|                         Messages.Add(new MqttApplicationMessageBuilder() | ||||
| @@ -290,7 +290,7 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableBa | ||||
|         { | ||||
|             foreach (var item in alramData) | ||||
|             { | ||||
|                 List<TopicJson> topicJsonList = GetAlarms(item); | ||||
|                 var topicJsonList = GetAlarms(item); | ||||
|                 foreach (var topicJson in topicJsonList) | ||||
|                 { | ||||
|                     Messages.Add(new MqttApplicationMessageBuilder() | ||||
| @@ -394,7 +394,7 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableBa | ||||
|     /// <summary> | ||||
|     /// 上传mqtt,返回上传结果 | ||||
|     /// </summary> | ||||
|     public async ValueTask<OperResult> MqttUpAsync(string topic, string payLoad, int count, CancellationToken cancellationToken = default) | ||||
|     public async ValueTask<OperResult> MqttUpAsync(string topic, byte[] payLoad, int count, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
| @@ -407,7 +407,7 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableBa | ||||
|             if (_driverPropertys.DetailLog) | ||||
|             { | ||||
|                 if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                     LogMessage.LogTrace($"Topic:{topic}{Environment.NewLine}PayLoad:{payLoad} {Environment.NewLine} VarModelQueue:{_memoryVarModelQueue.Count} "); | ||||
|                     LogMessage.LogTrace(GetString(topic, payLoad, _memoryVarModelQueue.Count)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|   | ||||
| @@ -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 | ||||
|         { | ||||
|   | ||||
| @@ -202,6 +202,7 @@ public partial class OpcUaServer : BusinessBase | ||||
|                 success = true; | ||||
|             } | ||||
|         } | ||||
|         catch (OperationCanceledException) { } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             if (success) | ||||
|   | ||||
| @@ -22,27 +22,27 @@ | ||||
| 		</ProjectReference> | ||||
| 		<ProjectReference Include="..\ThingsGateway.Foundation.OpcUa\ThingsGateway.Foundation.OpcUa.csproj" /> | ||||
|  | ||||
| 		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Server" Version="1.5.375.457" GeneratePathProperty="true"> | ||||
| 		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Server" Version="1.5.376.213" GeneratePathProperty="true"> | ||||
| 			<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets> | ||||
| 		</PackageReference> | ||||
|  | ||||
| 		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.5.375.457" GeneratePathProperty="true"> | ||||
| 		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.5.376.213" GeneratePathProperty="true"> | ||||
| 			<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets> | ||||
| 		</PackageReference> | ||||
|  | ||||
| 		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client" Version="1.5.375.457" GeneratePathProperty="true"> | ||||
| 		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client" Version="1.5.376.213" GeneratePathProperty="true"> | ||||
| 			<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets> | ||||
| 		</PackageReference> | ||||
|  | ||||
| 		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Core" Version="1.5.375.457" GeneratePathProperty="true"> | ||||
| 		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Core" Version="1.5.376.213" GeneratePathProperty="true"> | ||||
| 			<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets> | ||||
| 		</PackageReference> | ||||
|  | ||||
| 		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Configuration" Version="1.5.375.457" GeneratePathProperty="true"> | ||||
| 		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Configuration" Version="1.5.376.213" GeneratePathProperty="true"> | ||||
| 			<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets> | ||||
| 		</PackageReference> | ||||
|  | ||||
| 		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Security.Certificates" Version="1.5.375.457" GeneratePathProperty="true"> | ||||
| 		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Security.Certificates" Version="1.5.376.213" GeneratePathProperty="true"> | ||||
| 			<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets> | ||||
| 		</PackageReference> | ||||
|  | ||||
|   | ||||
| @@ -12,8 +12,6 @@ using Mapster; | ||||
|  | ||||
| using RabbitMQ.Client; | ||||
|  | ||||
| using System.Text; | ||||
|  | ||||
| using ThingsGateway.Foundation; | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
|  | ||||
| @@ -130,7 +128,7 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScript<Vari | ||||
|  | ||||
|     #region private | ||||
|  | ||||
|     private async ValueTask<OperResult> Update(List<TopicJson> topicJsonList, int count, CancellationToken cancellationToken) | ||||
|     private async ValueTask<OperResult> Update(List<TopicArray> topicJsonList, int count, CancellationToken cancellationToken) | ||||
|     { | ||||
|         foreach (var topicJson in topicJsonList) | ||||
|         { | ||||
| @@ -154,19 +152,19 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScript<Vari | ||||
|  | ||||
|     private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken) | ||||
|     { | ||||
|         List<TopicJson> topicJsonList = GetAlarms(item); | ||||
|         var topicJsonList = GetAlarmTopicArrays(item); | ||||
|         return Update(topicJsonList, item.Count(), cancellationToken); | ||||
|     } | ||||
|  | ||||
|     private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken) | ||||
|     { | ||||
|         List<TopicJson> topicJsonList = GetDeviceData(item); | ||||
|         var topicJsonList = GetDeviceTopicArray(item); | ||||
|         return Update(topicJsonList, item.Count(), cancellationToken); | ||||
|     } | ||||
|  | ||||
|     private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken) | ||||
|     { | ||||
|         List<TopicJson> topicJsonList = GetVariableBasicData(item); | ||||
|         var topicJsonList = GetVariableBasicDataTopicArray(item); | ||||
|         return Update(topicJsonList, item.Count(), cancellationToken); | ||||
|     } | ||||
|  | ||||
| @@ -208,18 +206,18 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScript<Vari | ||||
|     /// <summary> | ||||
|     /// 上传,返回上传结果 | ||||
|     /// </summary> | ||||
|     public async Task<OperResult> Publish(string topic, string payLoad, int count, CancellationToken cancellationToken) | ||||
|     public async Task<OperResult> Publish(string topic, byte[] payLoad, int count, CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (_channel != null) | ||||
|             { | ||||
|                 await _channel.BasicPublishAsync(_driverPropertys.ExchangeName, topic, Encoding.UTF8.GetBytes(payLoad), cancellationToken).ConfigureAwait(false); | ||||
|                 await _channel.BasicPublishAsync(_driverPropertys.ExchangeName, topic, payLoad, cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|                 if (_driverPropertys.DetailLog) | ||||
|                 { | ||||
|                     if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                         LogMessage.LogTrace($"Topic:{topic}{Environment.NewLine}PayLoad:{payLoad} {Environment.NewLine} VarModelQueue:{_memoryVarModelQueue.Count}"); | ||||
|                         LogMessage.LogTrace(GetString(topic, payLoad, _memoryVarModelQueue.Count)); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|   | ||||
| @@ -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 { } | ||||
|     } | ||||
|   | ||||
							
								
								
									
										39
									
								
								src/ThingsGateway.Photino/Properties/launchSettings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/ThingsGateway.Photino/Properties/launchSettings.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| { | ||||
|   "profiles": { | ||||
|     "http": { | ||||
|       "commandName": "Project", | ||||
|       "launchBrowser": true, | ||||
|       "environmentVariables": { | ||||
|         "ASPNETCORE_ENVIRONMENT": "Development" | ||||
|       }, | ||||
|       "dotnetRunMessages": true, | ||||
|       "applicationUrl": "http://*:5000" | ||||
|     }, | ||||
|     "demo": { | ||||
|       "commandName": "Project", | ||||
|       "launchBrowser": true, | ||||
|       "environmentVariables": { | ||||
|         "ASPNETCORE_ENVIRONMENT": "Demo" | ||||
|       }, | ||||
|       "dotnetRunMessages": true, | ||||
|       "applicationUrl": "http://*:5000" | ||||
|     }, | ||||
|  | ||||
|     "IIS Express": { | ||||
|       "commandName": "IISExpress", | ||||
|       "launchBrowser": true, | ||||
|       "environmentVariables": { | ||||
|         "ASPNETCORE_ENVIRONMENT": "Development" | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "$schema": "http://json.schemastore.org/launchsettings.json", | ||||
|   "iisSettings": { | ||||
|     "windowsAuthentication": false, | ||||
|     "anonymousAuthentication": true, | ||||
|     "iisExpress": { | ||||
|       "applicationUrl": "http://*:59494/", | ||||
|       "sslPort": 44372 | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -11,6 +11,9 @@ | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.AspNetCore.Builder; | ||||
| using Microsoft.AspNetCore.Components.Authorization; | ||||
| using Microsoft.AspNetCore.DataProtection; | ||||
| using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption; | ||||
| using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel; | ||||
| using Microsoft.AspNetCore.Hosting; | ||||
| using Microsoft.AspNetCore.Http; | ||||
| using Microsoft.AspNetCore.HttpOverrides; | ||||
| @@ -24,6 +27,7 @@ using Microsoft.Extensions.Options; | ||||
|  | ||||
| using Newtonsoft.Json; | ||||
|  | ||||
| using System.Security.Cryptography.X509Certificates; | ||||
| using System.Text; | ||||
| using System.Text.Encodings.Web; | ||||
| using System.Text.Unicode; | ||||
| @@ -273,6 +277,21 @@ public class Startup : AppStartup | ||||
|  | ||||
|         services.AddSignalR(); | ||||
|         #endregion | ||||
|  | ||||
|  | ||||
| #if NET9_0_OR_GREATER | ||||
|         var certificate = X509CertificateLoader.LoadPkcs12FromFile("ThingsGateway.pfx", "ThingsGateway", X509KeyStorageFlags.EphemeralKeySet); | ||||
| #else | ||||
|         var certificate = new X509Certificate2("ThingsGateway.pfx", "ThingsGateway", X509KeyStorageFlags.EphemeralKeySet); | ||||
| #endif | ||||
|         services.AddDataProtection() | ||||
|             .PersistKeysToFileSystem(new DirectoryInfo("../keys")) | ||||
|             .ProtectKeysWithCertificate(certificate) | ||||
|             .UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration | ||||
|             { | ||||
|                 EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC, | ||||
|                 ValidationAlgorithm = ValidationAlgorithm.HMACSHA256 | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
| 	<Import Project="$(SolutionDir)Version.props" /> | ||||
|  | ||||
|  | ||||
| 	<ItemGroup  Condition=" '$(SolutionName)' != 'ThingsGatewayRelease' " > | ||||
| 	<ItemGroup Condition=" '$(SolutionName)' != 'ThingsGatewayRelease' "> | ||||
| 		<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj" /> | ||||
| 		<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj" /> | ||||
| 		<ProjectReference Include="..\Gateway\ThingsGateway.Management\ThingsGateway.Management.csproj" /> | ||||
| @@ -19,8 +19,8 @@ | ||||
| 	<!--nuget包解压复制文件,上下文动态加载,网关管理和网关冗余--> | ||||
| 	<Import Project="..\ThingsGateway.Server\targets\GatewayOther.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayRelease' " /> | ||||
| 	 | ||||
| 	<ItemGroup  Condition=" '$(SolutionName)' == 'ThingsGatewayRelease' " > | ||||
| 		<PackageReference Include="ThingsGateway.Photino.Blazor" Version="$(Version)"  /> | ||||
| 	<ItemGroup Condition=" '$(SolutionName)' == 'ThingsGatewayRelease' "> | ||||
| 		<PackageReference Include="ThingsGateway.Photino.Blazor" Version="$(Version)" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
| @@ -87,6 +87,9 @@ | ||||
| 		<Compile Include="..\ThingsGateway.Server\Program\SingleFilePublish.cs" Link="Program\SingleFilePublish.cs" /> | ||||
|  | ||||
|  | ||||
| 		<Content Include="..\ThingsGateway.Server\ThingsGateway.pfx" Link="ThingsGateway.pfx"> | ||||
| 		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
| 		</Content> | ||||
|  | ||||
|  | ||||
| 		<Content Include="..\ThingsGateway.Server\wwwroot\favicon.ico" Link="wwwroot\favicon.ico" /> | ||||
| @@ -119,11 +122,23 @@ | ||||
| 		</EmbeddedResource> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 	  <Content Remove="launchSettings.json" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<Content Include="favicon.ico"> | ||||
| 			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
| 		</Content> | ||||
| 	</ItemGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 	  <None Include="Properties\launchSettings.json"> | ||||
| 	    <ExcludeFromSingleFile>true</ExcludeFromSingleFile> | ||||
| 	    <CopyToPublishDirectory>Never</CopyToPublishDirectory> | ||||
| 	  </None> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
| </Project> | ||||
|   | ||||
| @@ -3,8 +3,8 @@ | ||||
|     "MinCycleInterval": 10, //最小循环间隔 | ||||
|     "MaxCycleInterval": 200, //最大循环间隔 | ||||
|     "CheckInterval": 1800000, //检查间隔 | ||||
|     "MaxChannelCount": 1000, //最大通道数量 | ||||
|     "MaxDeviceCount": 1000, //最大设备数量 | ||||
|     "MaxVariableCount": 1000000 //最大变量数量 | ||||
|     "MaxChannelCount": 5000, //最大通道数量 | ||||
|     "MaxDeviceCount": 5000, //最大设备数量 | ||||
|     "MaxVariableCount": 2000000 //最大变量数量 | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -8,14 +8,8 @@ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using CSScripting; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.Reflection; | ||||
|  | ||||
| using ThingsGateway.Extension.Generic; | ||||
|  | ||||
| namespace ThingsGateway.Server; | ||||
|  | ||||
| /// <summary> | ||||
|   | ||||
| @@ -10,6 +10,9 @@ | ||||
|  | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.AspNetCore.Components.Authorization; | ||||
| using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel; | ||||
| using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption; | ||||
| using Microsoft.AspNetCore.DataProtection; | ||||
| using Microsoft.AspNetCore.HttpOverrides; | ||||
| using Microsoft.AspNetCore.Mvc.Controllers; | ||||
| using Microsoft.AspNetCore.StaticFiles; | ||||
| @@ -27,6 +30,7 @@ using ThingsGateway.Admin.Razor; | ||||
| using ThingsGateway.Extension; | ||||
| using ThingsGateway.Logging; | ||||
| using ThingsGateway.NewLife.Caching; | ||||
| using System.Security.Cryptography.X509Certificates; | ||||
|  | ||||
| namespace ThingsGateway.Server; | ||||
|  | ||||
| @@ -291,6 +295,21 @@ public class Startup : AppStartup | ||||
|         services.AddAuthorizationCore(); | ||||
|         services.AddScoped<IAuthorizationHandler, BlazorServerAuthenticationHandler>(); | ||||
|         services.AddScoped<AuthenticationStateProvider, BlazorServerAuthenticationStateProvider>(); | ||||
|  | ||||
|  | ||||
| #if NET9_0_OR_GREATER | ||||
|         var certificate = X509CertificateLoader.LoadPkcs12FromFile("ThingsGateway.pfx", "ThingsGateway", X509KeyStorageFlags.EphemeralKeySet); | ||||
| #else | ||||
|         var certificate = new X509Certificate2("ThingsGateway.pfx", "ThingsGateway",X509KeyStorageFlags.EphemeralKeySet); | ||||
| #endif | ||||
|         services.AddDataProtection() | ||||
|             .PersistKeysToFileSystem(new DirectoryInfo("../keys")) | ||||
|             .ProtectKeysWithCertificate(certificate) | ||||
|             .UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration | ||||
|             { | ||||
|                 EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC, | ||||
|                 ValidationAlgorithm = ValidationAlgorithm.HMACSHA256 | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -95,6 +95,9 @@ | ||||
| 		<Content Include="favicon.ico"> | ||||
| 			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
| 		</Content> | ||||
| 		<None Update="ThingsGateway.pfx"> | ||||
| 		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
| 		</None> | ||||
| 		<None Update="WindowsService"> | ||||
| 			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
| 		</None> | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								src/ThingsGateway.Server/ThingsGateway.pfx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/ThingsGateway.Server/ThingsGateway.pfx
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -3,6 +3,7 @@ | ||||
|  | ||||
|   "ConfigurationScanDirectories": [ "Configuration", "" ], // 扫描配置文件json文件夹(自动合并该文件夹里面所有json文件) | ||||
|   "IgnoreConfigurationFiles": [ "" ], | ||||
|   "ExternalAssemblies": [ "" ] | ||||
|   "ExternalAssemblies": [ "" ], | ||||
|   "DetailedErrors": true | ||||
|  | ||||
| } | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user