mirror of
				https://gitee.com/ThingsGateway/ThingsGateway.git
				synced 2025-11-04 17:43:58 +08:00 
			
		
		
		
	Compare commits
	
		
			12 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					03c0dfef37 | ||
| 
						 | 
					6ef6929c35 | ||
| 
						 | 
					e3c0c173f0 | ||
| 
						 | 
					7d64e058d4 | ||
| 
						 | 
					e97ee9b64b | ||
| 
						 | 
					6a03e39eeb | ||
| 
						 | 
					525ec740b5 | ||
| 
						 | 
					b790cf5f4e | ||
| 
						 | 
					d1248811fd | ||
| 
						 | 
					022d016e8e | ||
| 
						 | 
					f73245e650 | ||
| 
						 | 
					484461fa05 | 
@@ -26,6 +26,7 @@ namespace ThingsGateway.Admin.Application;
 | 
				
			|||||||
[Route("openapi/auth")]
 | 
					[Route("openapi/auth")]
 | 
				
			||||||
[Authorize(AuthenticationSchemes = "Bearer")]
 | 
					[Authorize(AuthenticationSchemes = "Bearer")]
 | 
				
			||||||
[LoggingMonitor]
 | 
					[LoggingMonitor]
 | 
				
			||||||
 | 
					[ApiController]
 | 
				
			||||||
public class OpenApiController : ControllerBase
 | 
					public class OpenApiController : ControllerBase
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    private readonly IAuthService _authService;
 | 
					    private readonly IAuthService _authService;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,16 +15,13 @@ namespace ThingsGateway.Admin.Application;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[Route("api/[controller]/[action]")]
 | 
					[Route("api/[controller]/[action]")]
 | 
				
			||||||
[AllowAnonymous]
 | 
					[AllowAnonymous]
 | 
				
			||||||
 | 
					[ApiController]
 | 
				
			||||||
public class TestController : ControllerBase
 | 
					public class TestController : ControllerBase
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [HttpPost]
 | 
					    [HttpGet]
 | 
				
			||||||
    public Task Test(string data)
 | 
					    public void Test()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        for (int i = 0; i < 3; i++)
 | 
					        GC.Collect();
 | 
				
			||||||
        {
 | 
					        GC.WaitForPendingFinalizers();
 | 
				
			||||||
            GC.Collect();
 | 
					 | 
				
			||||||
            GC.WaitForPendingFinalizers();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return Task.CompletedTask;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,26 +22,19 @@ using System.Globalization;
 | 
				
			|||||||
using System.Reflection;
 | 
					using System.Reflection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using ThingsGateway.Extension;
 | 
					using ThingsGateway.Extension;
 | 
				
			||||||
using ThingsGateway.SpecificationDocument;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace ThingsGateway.Admin.Application;
 | 
					namespace ThingsGateway.Admin.Application;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal sealed class ApiPermissionService : IApiPermissionService
 | 
					internal sealed class ApiPermissionService : IApiPermissionService
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    private readonly IApiDescriptionGroupCollectionProvider _apiDescriptionGroupCollectionProvider;
 | 
					    private readonly IApiDescriptionGroupCollectionProvider _apiDescriptionGroupCollectionProvider;
 | 
				
			||||||
    private readonly SwaggerGeneratorOptions _generatorOptions;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public ApiPermissionService(
 | 
					    public ApiPermissionService(
 | 
				
			||||||
        IOptions<SwaggerGeneratorOptions> generatorOptions,
 | 
					        IOptions<SwaggerGeneratorOptions> generatorOptions,
 | 
				
			||||||
        IApiDescriptionGroupCollectionProvider apiDescriptionGroupCollectionProvider)
 | 
					        IApiDescriptionGroupCollectionProvider apiDescriptionGroupCollectionProvider)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        _generatorOptions = generatorOptions.Value;
 | 
					 | 
				
			||||||
        _apiDescriptionGroupCollectionProvider = apiDescriptionGroupCollectionProvider;
 | 
					        _apiDescriptionGroupCollectionProvider = apiDescriptionGroupCollectionProvider;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    private IEnumerable<string> GetDocumentNames()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return _generatorOptions.SwaggerDocs.Keys;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <inheritdoc />
 | 
					    /// <inheritdoc />
 | 
				
			||||||
    public List<OpenApiPermissionTreeSelector> ApiPermissionTreeSelector()
 | 
					    public List<OpenApiPermissionTreeSelector> ApiPermissionTreeSelector()
 | 
				
			||||||
@@ -53,37 +46,37 @@ internal sealed class ApiPermissionService : IApiPermissionService
 | 
				
			|||||||
            permissions = new();
 | 
					            permissions = new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Dictionary<string, OpenApiPermissionTreeSelector> groupOpenApis = new();
 | 
					            Dictionary<string, OpenApiPermissionTreeSelector> groupOpenApis = new();
 | 
				
			||||||
            foreach (var item in GetDocumentNames())
 | 
					
 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                OpenApiPermissionTreeSelector openApiPermissionTreeSelector = new() { ApiName = item ?? "Default" };
 | 
					 | 
				
			||||||
                groupOpenApis.TryAdd(openApiPermissionTreeSelector.ApiName, openApiPermissionTreeSelector);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            var apiDescriptions = _apiDescriptionGroupCollectionProvider.ApiDescriptionGroups.Items;
 | 
					            var apiDescriptions = _apiDescriptionGroupCollectionProvider.ApiDescriptionGroups.Items;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach (var item1 in apiDescriptions)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                foreach (var item in item1.Items)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    if (item.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        OpenApiPermissionTreeSelector openApiPermissionTreeSelector = new() { ApiName = controllerActionDescriptor.ControllerName ?? "Default" };
 | 
				
			||||||
 | 
					                        groupOpenApis.TryAdd(openApiPermissionTreeSelector.ApiName, openApiPermissionTreeSelector);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // 获取所有需要数据权限的控制器
 | 
					            // 获取所有需要数据权限的控制器
 | 
				
			||||||
            var controllerTypes =
 | 
					            var controllerTypes =
 | 
				
			||||||
                App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(RolePermissionAttribute), false));
 | 
					                App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(RolePermissionAttribute), false));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var groupOpenApi in groupOpenApis)
 | 
					            //foreach (var groupOpenApi in groupOpenApis)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                foreach (var apiDescriptionGroup in apiDescriptions)
 | 
					                foreach (var apiDescriptionGroup in apiDescriptions)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
                    var routes = apiDescriptionGroup.Items.Where(api => api.ActionDescriptor is ControllerActionDescriptor);
 | 
					                    var routes = apiDescriptionGroup.Items.Where(api => api.ActionDescriptor is ControllerActionDescriptor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    OpenApiPermissionTreeSelector openApiPermissionTreeSelector = groupOpenApi.Value;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    Dictionary<string, OpenApiPermissionTreeSelector> openApiPermissionTreeSelectorDict = new();
 | 
					                    Dictionary<string, OpenApiPermissionTreeSelector> openApiPermissionTreeSelectorDict = new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    foreach (var route in routes)
 | 
					                    foreach (var route in routes)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        if (!SpecificationDocumentBuilder.CheckApiDescriptionInCurrentGroup(groupOpenApi.Key, route))
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            continue;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        var actionDesc = (ControllerActionDescriptor)route.ActionDescriptor;
 | 
					                        var actionDesc = (ControllerActionDescriptor)route.ActionDescriptor;
 | 
				
			||||||
                        if (!actionDesc.ControllerTypeInfo.CustomAttributes.Any(a => a.AttributeType == typeof(RolePermissionAttribute)))
 | 
					                        if (!actionDesc.ControllerTypeInfo.CustomAttributes.Any(a => a.AttributeType == typeof(RolePermissionAttribute)))
 | 
				
			||||||
                            continue;
 | 
					                            continue;
 | 
				
			||||||
@@ -116,10 +109,8 @@ internal sealed class ApiPermissionService : IApiPermissionService
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    openApiPermissionTreeSelector.Children.AddRange(openApiPermissionTreeSelectorDict.Values);
 | 
					                    if (openApiPermissionTreeSelectorDict.Values.Any(a => a.Children.Count > 0))
 | 
				
			||||||
 | 
					                        permissions.AddRange(openApiPermissionTreeSelectorDict.Values);
 | 
				
			||||||
                    if (openApiPermissionTreeSelector.Children.Any(a => a.Children.Count > 0))
 | 
					 | 
				
			||||||
                        permissions.Add(openApiPermissionTreeSelector);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@
 | 
				
			|||||||
		<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.4" />
 | 
							<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.4" />
 | 
				
			||||||
		<PackageReference Include="UAParser" Version="3.1.47" />
 | 
							<PackageReference Include="UAParser" Version="3.1.47" />
 | 
				
			||||||
		<PackageReference Include="Rougamo.Fody" Version="5.0.0" />
 | 
							<PackageReference Include="Rougamo.Fody" Version="5.0.0" />
 | 
				
			||||||
		<PackageReference Include="SqlSugarCore" Version="5.1.4.189" />
 | 
							<PackageReference Include="SqlSugarCore" Version="5.1.4.193" />
 | 
				
			||||||
	</ItemGroup>
 | 
						</ItemGroup>
 | 
				
			||||||
	<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
 | 
						<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
 | 
				
			||||||
		<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
 | 
							<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Microsoft.AspNetCore.Authorization;
 | 
				
			||||||
using Microsoft.AspNetCore.Components.Authorization;
 | 
					using Microsoft.AspNetCore.Components.Authorization;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.DataProtection;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
 | 
				
			||||||
using Microsoft.AspNetCore.HttpOverrides;
 | 
					using Microsoft.AspNetCore.HttpOverrides;
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc.Controllers;
 | 
					using Microsoft.AspNetCore.Mvc.Controllers;
 | 
				
			||||||
using Microsoft.AspNetCore.StaticFiles;
 | 
					using Microsoft.AspNetCore.StaticFiles;
 | 
				
			||||||
@@ -18,6 +21,7 @@ using Microsoft.Extensions.Options;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
using Newtonsoft.Json;
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using System.Security.Cryptography.X509Certificates;
 | 
				
			||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
using System.Text.Encodings.Web;
 | 
					using System.Text.Encodings.Web;
 | 
				
			||||||
using System.Text.Unicode;
 | 
					using System.Text.Unicode;
 | 
				
			||||||
@@ -291,6 +295,21 @@ public class Startup : AppStartup
 | 
				
			|||||||
        services.AddAuthorizationCore();
 | 
					        services.AddAuthorizationCore();
 | 
				
			||||||
        services.AddScoped<IAuthorizationHandler, BlazorServerAuthenticationHandler>();
 | 
					        services.AddScoped<IAuthorizationHandler, BlazorServerAuthenticationHandler>();
 | 
				
			||||||
        services.AddScoped<AuthenticationStateProvider, BlazorServerAuthenticationStateProvider>();
 | 
					        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">
 | 
							<None Update="pm2-linux.json">
 | 
				
			||||||
			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
								<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
				
			||||||
		</None>
 | 
							</None>
 | 
				
			||||||
 | 
							<None Update="ThingsGateway.pfx">
 | 
				
			||||||
 | 
							  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
				
			||||||
 | 
							</None>
 | 
				
			||||||
		<None Update="thingsgateway.service">
 | 
							<None Update="thingsgateway.service">
 | 
				
			||||||
			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
								<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
				
			||||||
		</None>
 | 
							</None>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								src/Admin/ThingsGateway.AdminServer/ThingsGateway.pfx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/Admin/ThingsGateway.AdminServer/ThingsGateway.pfx
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -7,7 +7,7 @@
 | 
				
			|||||||
	</PropertyGroup>
 | 
						</PropertyGroup>
 | 
				
			||||||
	<ItemGroup>
 | 
						<ItemGroup>
 | 
				
			||||||
		<PackageReference Include="BootstrapBlazor.FontAwesome" Version="9.0.2" />
 | 
							<PackageReference Include="BootstrapBlazor.FontAwesome" Version="9.0.2" />
 | 
				
			||||||
		<PackageReference Include="BootstrapBlazor" Version="9.5.12" />
 | 
							<PackageReference Include="BootstrapBlazor" Version="9.6.1" />
 | 
				
			||||||
		<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
 | 
							<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
 | 
				
			||||||
	</ItemGroup>
 | 
						</ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
<Project>
 | 
					<Project>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<PropertyGroup>
 | 
						<PropertyGroup>
 | 
				
			||||||
		<PluginVersion>10.5.9</PluginVersion>
 | 
							<PluginVersion>10.5.18</PluginVersion>
 | 
				
			||||||
		<ProPluginVersion>10.5.9</ProPluginVersion>
 | 
							<ProPluginVersion>10.5.18</ProPluginVersion>
 | 
				
			||||||
		<AuthenticationVersion>2.1.7</AuthenticationVersion>
 | 
							<AuthenticationVersion>2.1.7</AuthenticationVersion>
 | 
				
			||||||
	</PropertyGroup>
 | 
						</PropertyGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,7 +42,10 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
 | 
				
			|||||||
        set
 | 
					        set
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _heartbeat = value;
 | 
					            _heartbeat = value;
 | 
				
			||||||
            HeartbeatByte = new ArraySegment<byte>(Encoding.UTF8.GetBytes(value));
 | 
					            if (!_heartbeat.IsNullOrEmpty())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                HeartbeatByte = new ArraySegment<byte>(Encoding.UTF8.GetBytes(value));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    private string _heartbeat;
 | 
					    private string _heartbeat;
 | 
				
			||||||
@@ -59,6 +62,8 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
 | 
				
			|||||||
            return;//此处可判断,如果为服务器,则不用使用心跳。
 | 
					            return;//此处可判断,如果为服务器,则不用使用心跳。
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        HeartbeatTime = Math.Max(HeartbeatTime, 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (DtuId.IsNullOrWhiteSpace()) return;
 | 
					        if (DtuId.IsNullOrWhiteSpace()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (client is ITcpClient tcpClient)
 | 
					        if (client is ITcpClient tcpClient)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -70,7 +70,7 @@ public static class PluginUtil
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            action += a =>
 | 
					            action += a =>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                a.UseCheckClear()
 | 
					                a.UseTcpSessionCheckClear()
 | 
				
			||||||
        .SetCheckClearType(CheckClearType.All)
 | 
					        .SetCheckClearType(CheckClearType.All)
 | 
				
			||||||
        .SetTick(TimeSpan.FromMilliseconds(channelOptions.CheckClearTime))
 | 
					        .SetTick(TimeSpan.FromMilliseconds(channelOptions.CheckClearTime))
 | 
				
			||||||
        .SetOnClose(async (c, t) =>
 | 
					        .SetOnClose(async (c, t) =>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -562,7 +562,7 @@ public abstract class DeviceBase : DisposableObject, IDevice
 | 
				
			|||||||
        finally
 | 
					        finally
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            waitLock.Release();
 | 
					            waitLock.Release();
 | 
				
			||||||
            clientChannel.WaitHandlePool.Destroy(waitData);
 | 
					            clientChannel.WaitHandlePool.Destroy(sign);
 | 
				
			||||||
            Channel.ChannelReceivedWaitDict.TryRemove(sign, out _);
 | 
					            Channel.ChannelReceivedWaitDict.TryRemove(sign, out _);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -960,7 +960,7 @@ public abstract class DeviceBase : DisposableObject, IDevice
 | 
				
			|||||||
                        if (tcpServiceChannel.TryGetClient($"ID={dtu.DtuId}", out var client))
 | 
					                        if (tcpServiceChannel.TryGetClient($"ID={dtu.DtuId}", out var client))
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            client.WaitHandlePool?.SafeDispose();
 | 
					                            client.WaitHandlePool?.SafeDispose();
 | 
				
			||||||
                            client.Close();
 | 
					                            _ = client.CloseAsync();
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -150,6 +150,13 @@ public static class LoggerExtensions
 | 
				
			|||||||
        logger.Log(TouchSocket.Core.LogLevel.Info, null, msg, null);
 | 
					        logger.Log(TouchSocket.Core.LogLevel.Info, null, msg, null);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// <summary>
 | 
				
			||||||
 | 
					    /// 输出提示日志
 | 
				
			||||||
 | 
					    /// </summary>
 | 
				
			||||||
 | 
					    public static void LogDebug(this ILog logger, string msg)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        logger.Log(TouchSocket.Core.LogLevel.Debug, null, msg, null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 输出Trace日志
 | 
					    /// 输出Trace日志
 | 
				
			||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,8 +10,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	<ItemGroup>
 | 
						<ItemGroup>
 | 
				
			||||||
		<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="9.0.4" />
 | 
							<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="9.0.4" />
 | 
				
			||||||
		<PackageReference Include="TouchSocket" Version="3.1.0" />
 | 
							<PackageReference Include="TouchSocket" Version="3.1.2" />
 | 
				
			||||||
		<PackageReference Include="TouchSocket.SerialPorts" Version="3.1.0" />
 | 
							<PackageReference Include="TouchSocket.SerialPorts" Version="3.1.2" />
 | 
				
			||||||
	</ItemGroup>
 | 
						</ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<ItemGroup>
 | 
						<ItemGroup>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,9 +40,7 @@ public class AsyncReadWriteLock
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private void ReleaseWriter()
 | 
					    private void ReleaseWriter()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Interlocked.Decrement(ref _writerCount);
 | 
					        if (Interlocked.Decrement(ref _writerCount) == 0)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (Interlocked.Read(ref _writerCount) == 0)
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var resetEvent = _readerLock;
 | 
					            var resetEvent = _readerLock;
 | 
				
			||||||
            _readerLock = new(false);
 | 
					            _readerLock = new(false);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,6 +36,7 @@ namespace ThingsGateway.Gateway.Application;
 | 
				
			|||||||
[Route("openApi/control")]
 | 
					[Route("openApi/control")]
 | 
				
			||||||
[RolePermission]
 | 
					[RolePermission]
 | 
				
			||||||
[LoggingMonitor]
 | 
					[LoggingMonitor]
 | 
				
			||||||
 | 
					[ApiController]
 | 
				
			||||||
[Authorize(AuthenticationSchemes = "Bearer")]
 | 
					[Authorize(AuthenticationSchemes = "Bearer")]
 | 
				
			||||||
public class ControlController : ControllerBase
 | 
					public class ControlController : ControllerBase
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,7 @@ namespace ThingsGateway.Gateway.Application;
 | 
				
			|||||||
[ApiDescriptionSettings("ThingsGateway.OpenApi", Order = 200)]
 | 
					[ApiDescriptionSettings("ThingsGateway.OpenApi", Order = 200)]
 | 
				
			||||||
[DisplayName("数据状态")]
 | 
					[DisplayName("数据状态")]
 | 
				
			||||||
[Route("openApi/runtimeInfo")]
 | 
					[Route("openApi/runtimeInfo")]
 | 
				
			||||||
 | 
					[ApiController]
 | 
				
			||||||
[RolePermission]
 | 
					[RolePermission]
 | 
				
			||||||
[Authorize(AuthenticationSchemes = "Bearer")]
 | 
					[Authorize(AuthenticationSchemes = "Bearer")]
 | 
				
			||||||
public class RuntimeInfoController : ControllerBase
 | 
					public class RuntimeInfoController : ControllerBase
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,6 +35,7 @@ public abstract class BusinessBaseWithCacheAlarmModel<VarModel, DevModel, AlarmM
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					                LogMessage?.LogInformation($"Add {typeof(DevModel).Name} data to file cache, count {data.Count}");
 | 
				
			||||||
                foreach (var item in data)
 | 
					                foreach (var item in data)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    item.Id = CommonUtils.GetSingleId();
 | 
					                    item.Id = CommonUtils.GetSingleId();
 | 
				
			||||||
@@ -100,6 +101,7 @@ public abstract class BusinessBaseWithCacheAlarmModel<VarModel, DevModel, AlarmM
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                if (_memoryAlarmModelQueue.Count > _businessPropertyWithCache.QueueMaxCount)
 | 
					                if (_memoryAlarmModelQueue.Count > _businessPropertyWithCache.QueueMaxCount)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 | 
					                    LogMessage?.LogWarning($"{typeof(AlarmModel).Name} Queue exceeds limit, clear old data. If it doesn't work as expected, increase [QueueMaxCount] or Enable cache");
 | 
				
			||||||
                    _memoryAlarmModelQueue.Clear();
 | 
					                    _memoryAlarmModelQueue.Clear();
 | 
				
			||||||
                    _memoryAlarmModelQueue.Enqueue(data);
 | 
					                    _memoryAlarmModelQueue.Enqueue(data);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,6 +36,7 @@ public abstract class BusinessBaseWithCacheDeviceModel<VarModel, DevModel> : Bus
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					                LogMessage?.LogInformation($"Add {typeof(DevModel).Name} data to file cache, count {data.Count}");
 | 
				
			||||||
                foreach (var item in data)
 | 
					                foreach (var item in data)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    item.Id = CommonUtils.GetSingleId();
 | 
					                    item.Id = CommonUtils.GetSingleId();
 | 
				
			||||||
@@ -101,6 +102,7 @@ public abstract class BusinessBaseWithCacheDeviceModel<VarModel, DevModel> : Bus
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                if (_memoryDevModelQueue.Count > _businessPropertyWithCache.QueueMaxCount)
 | 
					                if (_memoryDevModelQueue.Count > _businessPropertyWithCache.QueueMaxCount)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 | 
					                    LogMessage?.LogWarning($"{typeof(DevModel).Name} Queue exceeds limit, clear old data. If it doesn't work as expected, increase [QueueMaxCount] or Enable cache");
 | 
				
			||||||
                    _memoryDevModelQueue.Clear();
 | 
					                    _memoryDevModelQueue.Clear();
 | 
				
			||||||
                    _memoryDevModelQueue.Enqueue(data);
 | 
					                    _memoryDevModelQueue.Enqueue(data);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,6 +39,7 @@ public abstract class BusinessBaseWithCacheVariableModel<VarModel> : BusinessBas
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					                LogMessage?.LogInformation($"Add {typeof(VarModel).Name} data to file cache, count {data.Count}");
 | 
				
			||||||
                foreach (var item in data)
 | 
					                foreach (var item in data)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    item.Id = CommonUtils.GetSingleId();
 | 
					                    item.Id = CommonUtils.GetSingleId();
 | 
				
			||||||
@@ -147,6 +148,7 @@ public abstract class BusinessBaseWithCacheVariableModel<VarModel> : BusinessBas
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                if (_memoryVarModelQueue.Count > _businessPropertyWithCache.QueueMaxCount)
 | 
					                if (_memoryVarModelQueue.Count > _businessPropertyWithCache.QueueMaxCount)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 | 
					                    LogMessage?.LogWarning($"{typeof(VarModel).Name} Queue exceeds limit, clear old data. If it doesn't work as expected, increase [QueueMaxCount] or Enable cache");
 | 
				
			||||||
                    _memoryVarModelQueue.Clear();
 | 
					                    _memoryVarModelQueue.Clear();
 | 
				
			||||||
                    _memoryVarModelQueue.Enqueue(data);
 | 
					                    _memoryVarModelQueue.Enqueue(data);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -162,6 +162,8 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (_exTTimerTick.IsTickHappen())
 | 
					                    if (_exTTimerTick.IsTickHappen())
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
 | 
					                        if (LogMessage.LogLevel <= LogLevel.Debug)
 | 
				
			||||||
 | 
					                            LogMessage?.LogDebug($"Interval {typeof(VarModel).Name} data, count {IdVariableRuntimes.Count}");
 | 
				
			||||||
                        // 间隔推送全部变量
 | 
					                        // 间隔推送全部变量
 | 
				
			||||||
                        var variableRuntimes = IdVariableRuntimes.Select(a => a.Value);
 | 
					                        var variableRuntimes = IdVariableRuntimes.Select(a => a.Value);
 | 
				
			||||||
                        VariableTimeInterval(variableRuntimes, variableRuntimes.Adapt<List<VariableBasicData>>());
 | 
					                        VariableTimeInterval(variableRuntimes, variableRuntimes.Adapt<List<VariableBasicData>>());
 | 
				
			||||||
@@ -177,6 +179,8 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
 | 
				
			|||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        if (CollectDevices != null)
 | 
					                        if (CollectDevices != null)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 | 
					                            if (LogMessage.LogLevel <= LogLevel.Debug)
 | 
				
			||||||
 | 
					                                LogMessage?.LogDebug($"Interval {typeof(DevModel).Name} data, count {CollectDevices.Count}");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            // 间隔推送全部设备
 | 
					                            // 间隔推送全部设备
 | 
				
			||||||
                            foreach (var deviceRuntime in CollectDevices.Select(a => a.Value))
 | 
					                            foreach (var deviceRuntime in CollectDevices.Select(a => a.Value))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -152,6 +152,8 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (_exTTimerTick.IsTickHappen())
 | 
					                    if (_exTTimerTick.IsTickHappen())
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
 | 
					                        if (LogMessage.LogLevel <= LogLevel.Debug)
 | 
				
			||||||
 | 
					                            LogMessage?.LogDebug($"Interval  {typeof(VarModel).Name}  data, count {IdVariableRuntimes.Count}");
 | 
				
			||||||
                        // 上传所有变量信息
 | 
					                        // 上传所有变量信息
 | 
				
			||||||
                        var variableRuntimes = IdVariableRuntimes.Select(a => a.Value);
 | 
					                        var variableRuntimes = IdVariableRuntimes.Select(a => a.Value);
 | 
				
			||||||
                        VariableTimeInterval(variableRuntimes, variableRuntimes.Adapt<List<VariableBasicData>>());
 | 
					                        VariableTimeInterval(variableRuntimes, variableRuntimes.Adapt<List<VariableBasicData>>());
 | 
				
			||||||
@@ -168,6 +170,8 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
 | 
				
			|||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        if (CollectDevices != null)
 | 
					                        if (CollectDevices != null)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 | 
					                            if (LogMessage.LogLevel <= LogLevel.Debug)
 | 
				
			||||||
 | 
					                                LogMessage?.LogDebug($"Interval  {typeof(DevModel).Name}  data, count {CollectDevices.Count}");
 | 
				
			||||||
                            // 上传所有设备信息
 | 
					                            // 上传所有设备信息
 | 
				
			||||||
                            foreach (var deviceRuntime in CollectDevices.Select(a => a.Value))
 | 
					                            foreach (var deviceRuntime in CollectDevices.Select(a => a.Value))
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
            List<string> strings = new List<string>();
 | 
					            List<string> strings = new List<string>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // 使用正则表达式查找输入字符串中的所有匹配项
 | 
					            // 使用正则表达式查找输入字符串中的所有匹配项
 | 
				
			||||||
            Regex regex = new Regex(@"\$\{(.+?)\}");
 | 
					            Regex regex = TopicRegex();
 | 
				
			||||||
            MatchCollection matches = regex.Matches(input);
 | 
					            MatchCollection matches = regex.Matches(input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // 遍历匹配结果,将匹配到的字符串添加到列表中
 | 
					            // 遍历匹配结果,将匹配到的字符串添加到列表中
 | 
				
			||||||
@@ -89,10 +89,12 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
                    // 上传内容
 | 
					                    // 上传内容
 | 
				
			||||||
                    if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
 | 
					                    if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        // 如果是报警列表,则将整个分组转换为 JSON 字符串
 | 
					                        // 如果是报警列表,则将整个分组转换为 JSON 字符串
 | 
				
			||||||
                        string json = group.Select(a => a).ToList().ToJsonNetString(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                        var gList = group.Select(a => a).ToList();
 | 
				
			||||||
 | 
					                        string json = gList.ToJsonNetString(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                        // 将主题和 JSON 内容添加到列表中
 | 
					                        // 将主题和 JSON 内容添加到列表中
 | 
				
			||||||
                        topicJsonList.Add(new(topic, json));
 | 
					                        topicJsonList.Add(new(topic, json, gList.Count));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					                    else
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
@@ -101,7 +103,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            string json = JsonExtensions.ToJsonNetString(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                            string json = JsonExtensions.ToJsonNetString(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                            // 将主题和 JSON 内容添加到列表中
 | 
					                            // 将主题和 JSON 内容添加到列表中
 | 
				
			||||||
                            topicJsonList.Add(new(topic, json));
 | 
					                            topicJsonList.Add(new(topic, json, 1));
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -111,15 +113,16 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
 | 
					            if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                string json = data.Select(a => a).ToList().ToJsonNetString(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                var gList = data.Select(a => a).ToList();
 | 
				
			||||||
                topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json));
 | 
					                string json = gList.ToJsonNetString(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
 | 
					                topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json, gList.Count));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var group in data)
 | 
					                foreach (var group in data)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    string json = JsonExtensions.ToJsonNetString(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                    string json = JsonExtensions.ToJsonNetString(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                    topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json));
 | 
					                    topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json, 1));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -156,9 +159,10 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
                        if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
 | 
					                        if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            // 如果是设备列表,则将整个分组转换为 JSON 字符串
 | 
					                            // 如果是设备列表,则将整个分组转换为 JSON 字符串
 | 
				
			||||||
                            string json = group.Select(a => a).ToList().ToJsonNetString(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                            var gList = group.Select(a => a).ToList();
 | 
				
			||||||
 | 
					                            string json = gList.ToJsonNetString(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                            // 将主题和 JSON 内容添加到列表中
 | 
					                            // 将主题和 JSON 内容添加到列表中
 | 
				
			||||||
                            topicJsonList.Add(new(topic, json));
 | 
					                            topicJsonList.Add(new(topic, json, gList.Count));
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        else
 | 
					                        else
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@@ -167,7 +171,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                string json = JsonExtensions.ToJsonNetString(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                                string json = JsonExtensions.ToJsonNetString(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                                // 将主题和 JSON 内容添加到列表中
 | 
					                                // 将主题和 JSON 内容添加到列表中
 | 
				
			||||||
                                topicJsonList.Add(new(topic, json));
 | 
					                                topicJsonList.Add(new(topic, json, 1));
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@@ -178,15 +182,16 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
 | 
					            if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                string json = data.Select(a => a).ToList().ToJsonNetString(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                var gList = data.Select(a => a).ToList();
 | 
				
			||||||
                topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json));
 | 
					                string json = gList.ToJsonNetString(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
 | 
					                topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json, gList.Count));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var group in data)
 | 
					                foreach (var group in data)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    string json = JsonExtensions.ToJsonNetString(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                    string json = JsonExtensions.ToJsonNetString(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                    topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json));
 | 
					                    topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json, 1));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -221,9 +226,10 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
                        if (_businessPropertyWithCacheIntervalScript.IsVariableList)
 | 
					                        if (_businessPropertyWithCacheIntervalScript.IsVariableList)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            // 如果是变量列表,则将整个分组转换为 JSON 字符串
 | 
					                            // 如果是变量列表,则将整个分组转换为 JSON 字符串
 | 
				
			||||||
                            string json = group.Select(a => a).ToList().ToJsonNetString(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                            var gList = group.Select(a => a).ToList();
 | 
				
			||||||
 | 
					                            string json = gList.ToJsonNetString(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                            // 将主题和 JSON 内容添加到列表中
 | 
					                            // 将主题和 JSON 内容添加到列表中
 | 
				
			||||||
                            topicJsonList.Add(new(topic, json));
 | 
					                            topicJsonList.Add(new(topic, json, gList.Count));
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        else
 | 
					                        else
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@@ -232,7 +238,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                string json = JsonExtensions.ToJsonNetString(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                                string json = JsonExtensions.ToJsonNetString(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                                // 将主题和 JSON 内容添加到列表中
 | 
					                                // 将主题和 JSON 内容添加到列表中
 | 
				
			||||||
                                topicJsonList.Add(new(topic, json));
 | 
					                                topicJsonList.Add(new(topic, json, 1));
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@@ -243,15 +249,16 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_businessPropertyWithCacheIntervalScript.IsVariableList)
 | 
					            if (_businessPropertyWithCacheIntervalScript.IsVariableList)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                string json = data.Select(a => a).ToList().ToJsonNetString(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                var gList = data.Select(a => a).ToList();
 | 
				
			||||||
                topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
 | 
					                string json = gList.ToJsonNetString(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
 | 
					                topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, gList.Count));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var group in data)
 | 
					                foreach (var group in data)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    string json = JsonExtensions.ToJsonNetString(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                    string json = JsonExtensions.ToJsonNetString(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                    topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
 | 
					                    topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, 1));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -294,10 +301,11 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
                        // 上传内容
 | 
					                        // 上传内容
 | 
				
			||||||
                        if (_businessPropertyWithCacheIntervalScript.IsVariableList)
 | 
					                        if (_businessPropertyWithCacheIntervalScript.IsVariableList)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            // 如果是变量列表,则将整个分组转换为 JSON 字符串
 | 
					                            // 如果是变量列表,则将整个分组转换为 JSON 字符串
 | 
				
			||||||
                            string json = group.Select(a => a).GroupBy(a => a.DeviceName, b => b).ToDictionary(a => a.Key, b => b.ToList()).ToJsonNetString(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                            string json = group.Select(a => a).GroupBy(a => a.DeviceName, b => b).ToDictionary(a => a.Key, b => b.ToList()).ToJsonNetString(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                            // 将主题和 JSON 内容添加到列表中
 | 
					                            // 将主题和 JSON 内容添加到列表中
 | 
				
			||||||
                            topicJsonList.Add(new(topic, json));
 | 
					                            topicJsonList.Add(new(topic, json, group.Count()));
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        else
 | 
					                        else
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@@ -306,7 +314,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                string json = JsonExtensions.ToJsonNetString(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                                string json = JsonExtensions.ToJsonNetString(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                                // 将主题和 JSON 内容添加到列表中
 | 
					                                // 将主题和 JSON 内容添加到列表中
 | 
				
			||||||
                                topicJsonList.Add(new(topic, json));
 | 
					                                topicJsonList.Add(new(topic, json, 1));
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@@ -318,14 +326,14 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
            if (_businessPropertyWithCacheIntervalScript.IsVariableList)
 | 
					            if (_businessPropertyWithCacheIntervalScript.IsVariableList)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                string json = data.Select(a => a).GroupBy(a => a.DeviceName, b => b).ToDictionary(a => a.Key, b => b.ToList()).ToJsonNetString(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                string json = data.Select(a => a).GroupBy(a => a.DeviceName, b => b).ToDictionary(a => a.Key, b => b.ToList()).ToJsonNetString(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
 | 
					                topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, data.Count()));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var group in data)
 | 
					                foreach (var group in data)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    string json = JsonExtensions.ToJsonNetString(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                    string json = JsonExtensions.ToJsonNetString(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                    topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
 | 
					                    topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, 1));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -369,7 +377,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
    protected List<TopicArray> GetAlarmTopicArrays(IEnumerable<AlarmModel> item)
 | 
					    protected List<TopicArray> GetAlarmTopicArrays(IEnumerable<AlarmModel> item)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<AlarmModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel);
 | 
					        IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<AlarmModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel);
 | 
				
			||||||
        List<TopicArray> topicJsonList = new List<TopicArray>();
 | 
					        List<TopicArray> topicArrayList = new List<TopicArray>();
 | 
				
			||||||
        var topics = Match(_businessPropertyWithCacheIntervalScript.AlarmTopic);
 | 
					        var topics = Match(_businessPropertyWithCacheIntervalScript.AlarmTopic);
 | 
				
			||||||
        if (topics.Count > 0)
 | 
					        if (topics.Count > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -392,9 +400,10 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
                    // 上传内容
 | 
					                    // 上传内容
 | 
				
			||||||
                    if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
 | 
					                    if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        var json = Serialize(group.Select(a => a).ToList().ToList(), _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                        var gList = group.Select(a => a).ToList();
 | 
				
			||||||
 | 
					                        var json = Serialize(gList, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                        // 将主题和 JSON 内容添加到列表中
 | 
					                        // 将主题和 JSON 内容添加到列表中
 | 
				
			||||||
                        topicJsonList.Add(new(topic, json));
 | 
					                        topicArrayList.Add(new(topic, json, gList.Count));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					                    else
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
@@ -403,7 +412,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            var json = Serialize(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                            var json = Serialize(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                            // 将主题和 JSON 内容添加到列表中
 | 
					                            // 将主题和 JSON 内容添加到列表中
 | 
				
			||||||
                            topicJsonList.Add(new(topic, json));
 | 
					                            topicArrayList.Add(new(topic, json, 1));
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -413,26 +422,27 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
 | 
					            if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var json = Serialize(data.Select(a => a).ToList(), _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                var gList = data.Select(a => a).ToList();
 | 
				
			||||||
                topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json));
 | 
					                var json = Serialize(gList, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
 | 
					                topicArrayList.Add(new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json, gList.Count));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var group in data)
 | 
					                foreach (var group in data)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var json = Serialize(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                    var json = Serialize(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                    topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json));
 | 
					                    topicArrayList.Add(new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json, 1));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return topicJsonList;
 | 
					        return topicArrayList;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected List<TopicArray> GetDeviceTopicArray(IEnumerable<DevModel> item)
 | 
					    protected List<TopicArray> GetDeviceTopicArray(IEnumerable<DevModel> item)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<DevModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel);
 | 
					        IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<DevModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel);
 | 
				
			||||||
        List<TopicArray> topicJsonList = new List<TopicArray>();
 | 
					        List<TopicArray> topicArrayList = new List<TopicArray>();
 | 
				
			||||||
        var topics = Match(_businessPropertyWithCacheIntervalScript.DeviceTopic);
 | 
					        var topics = Match(_businessPropertyWithCacheIntervalScript.DeviceTopic);
 | 
				
			||||||
        if (topics.Count > 0)
 | 
					        if (topics.Count > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -457,9 +467,10 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
                        if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
 | 
					                        if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            // 如果是设备列表,则将整个分组转换为 JSON 字符串
 | 
					                            // 如果是设备列表,则将整个分组转换为 JSON 字符串
 | 
				
			||||||
                            var json = Serialize(group.Select(a => a).ToList(), _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                            var gList = group.Select(a => a).ToList();
 | 
				
			||||||
 | 
					                            var json = Serialize(gList, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                            // 将主题和 JSON 内容添加到列表中
 | 
					                            // 将主题和 JSON 内容添加到列表中
 | 
				
			||||||
                            topicJsonList.Add(new(topic, json));
 | 
					                            topicArrayList.Add(new(topic, json, gList.Count));
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        else
 | 
					                        else
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@@ -468,7 +479,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                var json = Serialize(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                                var json = Serialize(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                                // 将主题和 JSON 内容添加到列表中
 | 
					                                // 将主题和 JSON 内容添加到列表中
 | 
				
			||||||
                                topicJsonList.Add(new(topic, json));
 | 
					                                topicArrayList.Add(new(topic, json, 1));
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@@ -479,25 +490,26 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
 | 
					            if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var json = Serialize(data.Select(a => a).ToList(), _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                var gList = data.Select(a => a).ToList();
 | 
				
			||||||
                topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json));
 | 
					                var json = Serialize(gList, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
 | 
					                topicArrayList.Add(new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json, gList.Count));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var group in data)
 | 
					                foreach (var group in data)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var json = Serialize(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                    var json = Serialize(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                    topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json));
 | 
					                    topicArrayList.Add(new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json, 1));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return topicJsonList;
 | 
					        return topicArrayList;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected List<TopicArray> GetVariableTopicArray(IEnumerable<VarModel> item)
 | 
					    protected List<TopicArray> GetVariableTopicArray(IEnumerable<VarModel> item)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<VarModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptVariableModel);
 | 
					        IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<VarModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptVariableModel);
 | 
				
			||||||
        List<TopicArray> topicJsonList = new List<TopicArray>();
 | 
					        List<TopicArray> topicArrayList = new List<TopicArray>();
 | 
				
			||||||
        var topics = Match(_businessPropertyWithCacheIntervalScript.VariableTopic);
 | 
					        var topics = Match(_businessPropertyWithCacheIntervalScript.VariableTopic);
 | 
				
			||||||
        if (topics.Count > 0)
 | 
					        if (topics.Count > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -522,9 +534,10 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
                        if (_businessPropertyWithCacheIntervalScript.IsVariableList)
 | 
					                        if (_businessPropertyWithCacheIntervalScript.IsVariableList)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            // 如果是变量列表,则将整个分组转换为 JSON 字符串
 | 
					                            // 如果是变量列表,则将整个分组转换为 JSON 字符串
 | 
				
			||||||
                            var json = Serialize(group.Select(a => a).ToList(), _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                            var gList = group.Select(a => a).ToList();
 | 
				
			||||||
 | 
					                            var json = Serialize(gList, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                            // 将主题和 JSON 内容添加到列表中
 | 
					                            // 将主题和 JSON 内容添加到列表中
 | 
				
			||||||
                            topicJsonList.Add(new(topic, json));
 | 
					                            topicArrayList.Add(new(topic, json, gList.Count));
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        else
 | 
					                        else
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@@ -533,7 +546,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                var json = Serialize(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                                var json = Serialize(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                                // 将主题和 JSON 内容添加到列表中
 | 
					                                // 将主题和 JSON 内容添加到列表中
 | 
				
			||||||
                                topicJsonList.Add(new(topic, json));
 | 
					                                topicArrayList.Add(new(topic, json, 1));
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@@ -544,19 +557,20 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_businessPropertyWithCacheIntervalScript.IsVariableList)
 | 
					            if (_businessPropertyWithCacheIntervalScript.IsVariableList)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var json = Serialize(data.Select(a => a).ToList(), _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                var gList = data.Select(a => a).ToList();
 | 
				
			||||||
                topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
 | 
					                var json = Serialize(gList, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
 | 
					                topicArrayList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, gList.Count));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var group in data)
 | 
					                foreach (var group in data)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var json = Serialize(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                    var json = Serialize(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                    topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
 | 
					                    topicArrayList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, 1));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return topicJsonList;
 | 
					        return topicArrayList;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected List<TopicArray> GetVariableBasicDataTopicArray(IEnumerable<VariableBasicData> item)
 | 
					    protected List<TopicArray> GetVariableBasicDataTopicArray(IEnumerable<VariableBasicData> item)
 | 
				
			||||||
@@ -571,7 +585,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
            data = item;
 | 
					            data = item;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        List<TopicArray> topicJsonList = new List<TopicArray>();
 | 
					        List<TopicArray> topicArrayList = new List<TopicArray>();
 | 
				
			||||||
        var topics = Match(_businessPropertyWithCacheIntervalScript.VariableTopic);
 | 
					        var topics = Match(_businessPropertyWithCacheIntervalScript.VariableTopic);
 | 
				
			||||||
        if (topics.Count > 0)
 | 
					        if (topics.Count > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -596,9 +610,10 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
                        if (_businessPropertyWithCacheIntervalScript.IsVariableList)
 | 
					                        if (_businessPropertyWithCacheIntervalScript.IsVariableList)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            // 如果是变量列表,则将整个分组转换为 JSON 字符串
 | 
					                            // 如果是变量列表,则将整个分组转换为 JSON 字符串
 | 
				
			||||||
                            var json = Serialize(group.Select(a => a).ToList(), _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                            var gList = group.Select(a => a).ToList();
 | 
				
			||||||
 | 
					                            var json = Serialize(gList, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                            // 将主题和 JSON 内容添加到列表中
 | 
					                            // 将主题和 JSON 内容添加到列表中
 | 
				
			||||||
                            topicJsonList.Add(new(topic, json));
 | 
					                            topicArrayList.Add(new(topic, json, gList.Count));
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        else
 | 
					                        else
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@@ -607,7 +622,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                var json = Serialize(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                                var json = Serialize(gro, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                                // 将主题和 JSON 内容添加到列表中
 | 
					                                // 将主题和 JSON 内容添加到列表中
 | 
				
			||||||
                                topicJsonList.Add(new(topic, json));
 | 
					                                topicArrayList.Add(new(topic, json, 1));
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@@ -618,25 +633,40 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_businessPropertyWithCacheIntervalScript.IsVariableList)
 | 
					            if (_businessPropertyWithCacheIntervalScript.IsVariableList)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var json = Serialize(data.Select(a => a).ToList(), _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                var gList = data.Select(a => a).ToList();
 | 
				
			||||||
                topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
 | 
					                var json = Serialize(gList, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
 | 
					                topicArrayList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, gList.Count));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var group in data)
 | 
					                foreach (var group in data)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var json = Serialize(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
					                    var json = Serialize(group, _businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
 | 
				
			||||||
                    topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
 | 
					                    topicArrayList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, 1));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return topicJsonList;
 | 
					        return topicArrayList;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected string GetString(string topic, byte[] json, int count)
 | 
					    protected string GetDetailLogString(TopicArray topicArray, int queueCount)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $"Topic:{topic}{Environment.NewLine}PayLoad:{Encoding.UTF8.GetString(json)} {Environment.NewLine} VarModelQueue:{count}";
 | 
					        if (queueCount > 0)
 | 
				
			||||||
 | 
					            return $"Up Topic:{topicArray.Topic}{Environment.NewLine}PayLoad:{Encoding.UTF8.GetString(topicArray.Json)} {Environment.NewLine} VarModelQueue:{queueCount}";
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            return $"Up Topic:{topicArray.Topic}{Environment.NewLine}PayLoad:{Encoding.UTF8.GetString(topicArray.Json)}";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected string GetCountLogString(TopicArray topicArray, int queueCount)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (queueCount > 0)
 | 
				
			||||||
 | 
					            return $"Up Topic:{topicArray.Topic}{Environment.NewLine}Count:{topicArray.Count} {Environment.NewLine} VarModelQueue:{queueCount}";
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            return $"Up Topic:{topicArray.Topic}{Environment.NewLine}Count:{topicArray.Count}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    [GeneratedRegex(@"\$\{(.+?)\}")]
 | 
				
			||||||
 | 
					    private static partial Regex TopicRegex();
 | 
				
			||||||
    #endregion 封装方法
 | 
					    #endregion 封装方法
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,10 +18,10 @@ using TouchSocket.Core;
 | 
				
			|||||||
namespace ThingsGateway.Gateway.Application;
 | 
					namespace ThingsGateway.Gateway.Application;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// <summary>
 | 
					/// <summary>
 | 
				
			||||||
/// 抽象类 <see cref="BusinessBaseWithCacheIntervalVariableModel{T}"/>,表示具有缓存间隔功能的业务基类,其中 T 代表变量模型。
 | 
					/// 抽象类 <see cref="BusinessBaseWithCacheIntervalVariableModel{VarModel}"/>,表示具有缓存间隔功能的业务基类,其中 T 代表变量模型。
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
/// <typeparam name="T">变量模型类型</typeparam>
 | 
					/// <typeparam name="VarModel">变量模型类型</typeparam>
 | 
				
			||||||
public abstract class BusinessBaseWithCacheIntervalVariableModel<T> : BusinessBaseWithCacheVariableModel<T>
 | 
					public abstract class BusinessBaseWithCacheIntervalVariableModel<VarModel> : BusinessBaseWithCacheVariableModel<VarModel>
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 用于定时触发的时间间隔。
 | 
					    /// 用于定时触发的时间间隔。
 | 
				
			||||||
@@ -110,6 +110,8 @@ public abstract class BusinessBaseWithCacheIntervalVariableModel<T> : BusinessBa
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (_exTTimerTick.IsTickHappen())
 | 
					                    if (_exTTimerTick.IsTickHappen())
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
 | 
					                        if (LogMessage.LogLevel <= LogLevel.Debug)
 | 
				
			||||||
 | 
					                            LogMessage?.LogDebug($"Interval  {typeof(VarModel).Name}  data, count {IdVariableRuntimes.Count}");
 | 
				
			||||||
                        //间隔推送全部变量
 | 
					                        //间隔推送全部变量
 | 
				
			||||||
                        var variableRuntimes = IdVariableRuntimes.Select(a => a.Value);
 | 
					                        var variableRuntimes = IdVariableRuntimes.Select(a => a.Value);
 | 
				
			||||||
                        VariableTimeInterval(variableRuntimes, variableRuntimes.Adapt<List<VariableBasicData>>());
 | 
					                        VariableTimeInterval(variableRuntimes, variableRuntimes.Adapt<List<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 struct TopicArray
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public TopicArray(string topic, byte[] json)
 | 
					    public TopicArray(string topic, byte[] json, int count)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Topic = topic; Json = json;
 | 
					        Topic = topic; Json = json; Count = count;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int Count { get; set; } = 1;
 | 
				
			||||||
    public byte[] Json { get; set; }
 | 
					    public byte[] Json { get; set; }
 | 
				
			||||||
    public string Topic { get; set; }
 | 
					    public string Topic { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,10 +12,11 @@ namespace ThingsGateway.Gateway.Application;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public struct TopicJson
 | 
					public struct TopicJson
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public TopicJson(string topic, string json)
 | 
					    public TopicJson(string topic, string json, int count)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Topic = topic; Json = json;
 | 
					        Topic = topic; Json = json; Count = count;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public int Count { get; set; } = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public string Json { get; set; }
 | 
					    public string Json { get; set; }
 | 
				
			||||||
    public string Topic { get; set; }
 | 
					    public string Topic { get; set; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,6 +51,13 @@ public class Variable : BaseDataEntity, IValidatableObject
 | 
				
			|||||||
    [Required]
 | 
					    [Required]
 | 
				
			||||||
    public virtual string Name { get; set; }
 | 
					    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>
 | 
				
			||||||
    /// 分组名称
 | 
					    /// 分组名称
 | 
				
			||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -97,6 +97,7 @@
 | 
				
			|||||||
    "Name": "Name",
 | 
					    "Name": "Name",
 | 
				
			||||||
    "Description": "Description",
 | 
					    "Description": "Description",
 | 
				
			||||||
    "Group": "Group",
 | 
					    "Group": "Group",
 | 
				
			||||||
 | 
					    "CollectGroup": "CollectGroup",
 | 
				
			||||||
    "DeviceId": "CollectionDevice",
 | 
					    "DeviceId": "CollectionDevice",
 | 
				
			||||||
    "DeviceId.MinValue": "{0} cannot be empty",
 | 
					    "DeviceId.MinValue": "{0} cannot be empty",
 | 
				
			||||||
    "DeviceId.Required": "{0} cannot be empty",
 | 
					    "DeviceId.Required": "{0} cannot be empty",
 | 
				
			||||||
@@ -452,6 +453,7 @@
 | 
				
			|||||||
    "Name.Required": "{0} cannot be empty",
 | 
					    "Name.Required": "{0} cannot be empty",
 | 
				
			||||||
    "Description": "Description",
 | 
					    "Description": "Description",
 | 
				
			||||||
    "Group": "Group",
 | 
					    "Group": "Group",
 | 
				
			||||||
 | 
					    "CollectGroup": "CollectGroup",
 | 
				
			||||||
    "DeviceId": "CollectionDevice",
 | 
					    "DeviceId": "CollectionDevice",
 | 
				
			||||||
    "DeviceId.MinValue": "{0} cannot be empty",
 | 
					    "DeviceId.MinValue": "{0} cannot be empty",
 | 
				
			||||||
    "DeviceId.Required": "{0} cannot be empty",
 | 
					    "DeviceId.Required": "{0} cannot be empty",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -89,7 +89,8 @@
 | 
				
			|||||||
    "Name": "名称",
 | 
					    "Name": "名称",
 | 
				
			||||||
    "Name.Required": " {0} 不可为空",
 | 
					    "Name.Required": " {0} 不可为空",
 | 
				
			||||||
    "Description": "描述",
 | 
					    "Description": "描述",
 | 
				
			||||||
    "Group": "分组",
 | 
					    "Group": "业务组",
 | 
				
			||||||
 | 
					    "CollectGroup": "采集组",
 | 
				
			||||||
    "DeviceId": "采集设备",
 | 
					    "DeviceId": "采集设备",
 | 
				
			||||||
    "DeviceId.MinValue": " {0} 不可为空",
 | 
					    "DeviceId.MinValue": " {0} 不可为空",
 | 
				
			||||||
    "DeviceId.Required": " {0} 不可为空",
 | 
					    "DeviceId.Required": " {0} 不可为空",
 | 
				
			||||||
@@ -486,7 +487,8 @@
 | 
				
			|||||||
    "Name": "名称",
 | 
					    "Name": "名称",
 | 
				
			||||||
    "Name.Required": " {0} 不可为空",
 | 
					    "Name.Required": " {0} 不可为空",
 | 
				
			||||||
    "Description": "描述",
 | 
					    "Description": "描述",
 | 
				
			||||||
    "Group": "分组",
 | 
					    "Group": "业务组",
 | 
				
			||||||
 | 
					    "CollectGroup": "采集组",
 | 
				
			||||||
    "DeviceId": "采集设备",
 | 
					    "DeviceId": "采集设备",
 | 
				
			||||||
    "DeviceId.MinValue": " {0} 不可为空",
 | 
					    "DeviceId.MinValue": " {0} 不可为空",
 | 
				
			||||||
    "DeviceId.Required": " {0} 不可为空",
 | 
					    "DeviceId.Required": " {0} 不可为空",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,7 +26,7 @@ namespace ThingsGateway.Gateway.Application;
 | 
				
			|||||||
public class DeviceRuntime : Device, IDisposable
 | 
					public class DeviceRuntime : Device, IDisposable
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    protected volatile DeviceStatusEnum _deviceStatus = DeviceStatusEnum.Default;
 | 
					    protected volatile DeviceStatusEnum _deviceStatus = DeviceStatusEnum.Default;
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
    private string? _lastErrorMessage;
 | 
					    private string? _lastErrorMessage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,8 +8,8 @@
 | 
				
			|||||||
	<ItemGroup>
 | 
						<ItemGroup>
 | 
				
			||||||
		<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
 | 
							<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
 | 
				
			||||||
		<PackageReference Include="Rougamo.Fody" Version="5.0.0" />
 | 
							<PackageReference Include="Rougamo.Fody" Version="5.0.0" />
 | 
				
			||||||
		<PackageReference Include="TouchSocket.Dmtp" Version="3.1.0" />
 | 
							<PackageReference Include="TouchSocket.Dmtp" Version="3.1.2" />
 | 
				
			||||||
		<PackageReference Include="TouchSocket.WebApi.Swagger" Version="3.1.0" />
 | 
							<PackageReference Include="TouchSocket.WebApi.Swagger" Version="3.1.2" />
 | 
				
			||||||
		<PackageReference Include="ThingsGateway.Authentication" Version="$(AuthenticationVersion)" />
 | 
							<PackageReference Include="ThingsGateway.Authentication" Version="$(AuthenticationVersion)" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	</ItemGroup>
 | 
						</ItemGroup>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -129,7 +129,6 @@ public partial class PropertyComponent : IPropertyUIBase
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
        var op = new DialogOption()
 | 
					        var op = new DialogOption()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            IsScrolling = true,
 | 
					            IsScrolling = true,
 | 
				
			||||||
@@ -144,6 +143,147 @@ public partial class PropertyComponent : IPropertyUIBase
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        {nameof(ScriptCheck.Data),data },
 | 
					        {nameof(ScriptCheck.Data),data },
 | 
				
			||||||
        {nameof(ScriptCheck.Script),script },
 | 
					        {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 =>
 | 
					        {nameof(ScriptCheck.ScriptChanged),EventCallback.Factory.Create<string>(this, v =>
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
                 if (pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel))
 | 
					                 if (pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel))
 | 
				
			||||||
@@ -171,3 +311,7 @@ public partial class PropertyComponent : IPropertyUIBase
 | 
				
			|||||||
    [Inject]
 | 
					    [Inject]
 | 
				
			||||||
    private DialogService DialogService { get; set; }
 | 
					    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("获取变量类实体,可用方法  <code>GlobalData.GetVariable(\"设备名称1\",\"变量名称1\")</code> "))</Alert>
 | 
				
			||||||
        <Alert Icon="fa-solid fa-circle-check" Color="Color.Success">@(new MarkupString("详细说明查看文档对应内容页面"))</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>
 | 
				
			||||||
    <div class="col-6  col-md-6">
 | 
					    <div class="col-6  col-md-6">
 | 
				
			||||||
        <BootstrapLabel Value=@Localizer["Input"] ShowLabelTooltip="true" />
 | 
					        <BootstrapLabel Value=@Localizer["Input"] ShowLabelTooltip="true" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,4 +56,13 @@ public partial class ScriptCheck
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    [Inject]
 | 
					    [Inject]
 | 
				
			||||||
    private IStringLocalizer<DeviceEditComponent> Localizer { get; set; }
 | 
					    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.Name" Readonly=BatchEditEnable />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <EditorItem @bind-Field="@context.Description" />
 | 
					                    <EditorItem @bind-Field="@context.Description" />
 | 
				
			||||||
 | 
					                    <EditorItem @bind-Field="@context.CollectGroup" />
 | 
				
			||||||
                    <EditorItem @bind-Field="@context.Group" />
 | 
					                    <EditorItem @bind-Field="@context.Group" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <EditorItem @bind-Field="@context.Unit" />
 | 
					                    <EditorItem @bind-Field="@context.Unit" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@
 | 
				
			|||||||
	</PropertyGroup>
 | 
						</PropertyGroup>
 | 
				
			||||||
	<ItemGroup>
 | 
						<ItemGroup>
 | 
				
			||||||
		<ProjectReference Include="..\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj" />
 | 
							<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.WinBox" Version="9.0.7" />
 | 
				
			||||||
		<PackageReference Include="BootstrapBlazor.CodeEditor" Version="9.0.1" />
 | 
							<PackageReference Include="BootstrapBlazor.CodeEditor" Version="9.0.1" />
 | 
				
			||||||
		<ProjectReference Include="..\..\Admin\ThingsGateway.Admin.Razor\ThingsGateway.Admin.Razor.csproj" />
 | 
							<ProjectReference Include="..\..\Admin\ThingsGateway.Admin.Razor\ThingsGateway.Admin.Razor.csproj" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,11 +19,12 @@ namespace ThingsGateway.Management;
 | 
				
			|||||||
[Route("openApi/autoUpdate")]
 | 
					[Route("openApi/autoUpdate")]
 | 
				
			||||||
[RolePermission]
 | 
					[RolePermission]
 | 
				
			||||||
[LoggingMonitor]
 | 
					[LoggingMonitor]
 | 
				
			||||||
 | 
					[ApiController]
 | 
				
			||||||
[Authorize(AuthenticationSchemes = "Bearer")]
 | 
					[Authorize(AuthenticationSchemes = "Bearer")]
 | 
				
			||||||
public class AutoUpdateControler : ControllerBase
 | 
					public class AutoUpdateController : ControllerBase
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    private IUpdateZipFileHostedService _updateZipFileService;
 | 
					    private IUpdateZipFileHostedService _updateZipFileService;
 | 
				
			||||||
    public AutoUpdateControler(IUpdateZipFileHostedService updateZipFileService)
 | 
					    public AutoUpdateController(IUpdateZipFileHostedService updateZipFileService)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        _updateZipFileService = updateZipFileService;
 | 
					        _updateZipFileService = updateZipFileService;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -14,8 +14,8 @@
 | 
				
			|||||||
    "Password": "Password"
 | 
					    "Password": "Password"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  "ThingsGateway.Management.AutoUpdateControler": {
 | 
					  "ThingsGateway.Management.AutoUpdateController": {
 | 
				
			||||||
    "AutoUpdateControler": "AutoUpdate",
 | 
					    "AutoUpdateController": "AutoUpdate",
 | 
				
			||||||
    "Update": "Update"
 | 
					    "Update": "Update"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,8 +13,8 @@
 | 
				
			|||||||
    "Unregister": "取消注册",
 | 
					    "Unregister": "取消注册",
 | 
				
			||||||
    "Password": "注册码"
 | 
					    "Password": "注册码"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "ThingsGateway.Management.AutoUpdateControler": {
 | 
					  "ThingsGateway.Management.AutoUpdateController": {
 | 
				
			||||||
    "AutoUpdateControler": "程序更新",
 | 
					    "AutoUpdateController": "程序更新",
 | 
				
			||||||
    "Update": "更新"
 | 
					    "Update": "更新"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "ThingsGateway.Upgrade.UpdateZipFile": {
 | 
					  "ThingsGateway.Upgrade.UpdateZipFile": {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,7 +55,7 @@ internal sealed class RedundancyHostedService : BackgroundService, IRedundancyHo
 | 
				
			|||||||
    public string LogPath { get; }
 | 
					    public string LogPath { get; }
 | 
				
			||||||
    private TcpDmtpClient TcpDmtpClient;
 | 
					    private TcpDmtpClient TcpDmtpClient;
 | 
				
			||||||
    private TcpDmtpService TcpDmtpService;
 | 
					    private TcpDmtpService TcpDmtpService;
 | 
				
			||||||
    private TcpDmtpClient GetTcpDmtpClient(RedundancyOptions redundancy)
 | 
					    private async Task<TcpDmtpClient> GetTcpDmtpClient(RedundancyOptions redundancy)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        _log = new LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
 | 
					        _log = new LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
 | 
				
			||||||
        _log?.AddLogger(new EasyLogger(Log_Out) { LogLevel = TouchSocket.Core.LogLevel.Trace });
 | 
					        _log?.AddLogger(new EasyLogger(Log_Out) { LogLevel = TouchSocket.Core.LogLevel.Trace });
 | 
				
			||||||
@@ -81,11 +81,11 @@ internal sealed class RedundancyHostedService : BackgroundService, IRedundancyHo
 | 
				
			|||||||
                   .SetMaxFailCount(redundancy.MaxErrorCount);
 | 
					                   .SetMaxFailCount(redundancy.MaxErrorCount);
 | 
				
			||||||
               });
 | 
					               });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tcpDmtpClient.Setup(config);
 | 
					        await tcpDmtpClient.SetupAsync(config).ConfigureAwait(false);
 | 
				
			||||||
        return tcpDmtpClient;
 | 
					        return tcpDmtpClient;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private TcpDmtpService GetTcpDmtpService(RedundancyOptions redundancy)
 | 
					    private async Task<TcpDmtpService> GetTcpDmtpService(RedundancyOptions redundancy)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        _log = new LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
 | 
					        _log = new LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
 | 
				
			||||||
        _log?.AddLogger(new EasyLogger(Log_Out) { LogLevel = TouchSocket.Core.LogLevel.Trace });
 | 
					        _log?.AddLogger(new EasyLogger(Log_Out) { LogLevel = TouchSocket.Core.LogLevel.Trace });
 | 
				
			||||||
@@ -111,7 +111,7 @@ internal sealed class RedundancyHostedService : BackgroundService, IRedundancyHo
 | 
				
			|||||||
                   .SetMaxFailCount(redundancy.MaxErrorCount);
 | 
					                   .SetMaxFailCount(redundancy.MaxErrorCount);
 | 
				
			||||||
               });
 | 
					               });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tcpDmtpService.Setup(config);
 | 
					        await tcpDmtpService.SetupAsync(config).ConfigureAwait(false);
 | 
				
			||||||
        return tcpDmtpService;
 | 
					        return tcpDmtpService;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -325,13 +325,13 @@ internal sealed class RedundancyHostedService : BackgroundService, IRedundancyHo
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            if (RedundancyOptions.IsMaster)
 | 
					            if (RedundancyOptions.IsMaster)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                TcpDmtpService = GetTcpDmtpService(RedundancyOptions);
 | 
					                TcpDmtpService = await GetTcpDmtpService(RedundancyOptions).ConfigureAwait(false);
 | 
				
			||||||
                await TcpDmtpService.StartAsync().ConfigureAwait(false);//启动
 | 
					                await TcpDmtpService.StartAsync().ConfigureAwait(false);//启动
 | 
				
			||||||
                await ActiveAsync().ConfigureAwait(false);
 | 
					                await ActiveAsync().ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                TcpDmtpClient = GetTcpDmtpClient(RedundancyOptions);
 | 
					                TcpDmtpClient = await GetTcpDmtpClient(RedundancyOptions).ConfigureAwait(false);
 | 
				
			||||||
                await StandbyAsync().ConfigureAwait(false);
 | 
					                await StandbyAsync().ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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))]
 | 
					[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 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;
 | 
					    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))]
 | 
					[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 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)
 | 
					    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))]
 | 
					[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 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)
 | 
					    Task<NodeOutput> IExpressionNode.ExecuteAsync(NodeInput input, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -160,9 +160,19 @@ public class OpcDaMaster : IDisposable
 | 
				
			|||||||
    /// <returns></returns>
 | 
					    /// <returns></returns>
 | 
				
			||||||
    public Dictionary<string, List<OpcItem>> AddItemsWithSave(List<string> items)
 | 
					    public Dictionary<string, List<OpcItem>> AddItemsWithSave(List<string> items)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        int i = 0;
 | 
					        lock (this)
 | 
				
			||||||
        ItemDicts = items.ConvertAll(o => new OpcItem(o)).ChunkTrivialBetter(OpcDaProperty.GroupSize).ToDictionary(a => "default" + (i++));
 | 
					        {
 | 
				
			||||||
        return ItemDicts;
 | 
					
 | 
				
			||||||
 | 
					            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>
 | 
					    /// <summary>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -398,10 +398,10 @@ public partial class SiemensS7Master : DeviceBase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            try
 | 
					            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)
 | 
					                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);
 | 
					                    await channel.CloseAsync().ConfigureAwait(false);
 | 
				
			||||||
                    return true;
 | 
					                    return true;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -409,16 +409,21 @@ public partial class SiemensS7Master : DeviceBase
 | 
				
			|||||||
            catch (OperationCanceledException) { }
 | 
					            catch (OperationCanceledException) { }
 | 
				
			||||||
            catch (Exception ex)
 | 
					            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);
 | 
					                await channel.CloseAsync().ConfigureAwait(false);
 | 
				
			||||||
                return true;
 | 
					                return true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            try
 | 
					            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)
 | 
					                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);
 | 
					                    await channel.CloseAsync().ConfigureAwait(false);
 | 
				
			||||||
                    return true;
 | 
					                    return true;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -429,7 +434,7 @@ public partial class SiemensS7Master : DeviceBase
 | 
				
			|||||||
            catch (OperationCanceledException) { }
 | 
					            catch (OperationCanceledException) { }
 | 
				
			||||||
            catch (Exception ex)
 | 
					            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);
 | 
					                await channel.CloseAsync().ConfigureAwait(false);
 | 
				
			||||||
                return true;
 | 
					                return true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,7 @@
 | 
				
			|||||||
    </PackageReference>
 | 
					    </PackageReference>
 | 
				
			||||||
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
 | 
					    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
 | 
				
			||||||
    <PackageReference Include="xunit" Version="2.9.3" />
 | 
					    <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>
 | 
					      <PrivateAssets>all</PrivateAssets>
 | 
				
			||||||
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
 | 
					      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
 | 
				
			||||||
    </PackageReference>
 | 
					    </PackageReference>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,8 +12,6 @@ using BootstrapBlazor.Components;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
using Mapster;
 | 
					using Mapster;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using Newtonsoft.Json.Linq;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using SqlSugar;
 | 
					using SqlSugar;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using ThingsGateway.Admin.Application;
 | 
					using ThingsGateway.Admin.Application;
 | 
				
			||||||
@@ -68,7 +66,7 @@ public partial class QuestDBProducer : BusinessBaseWithCacheIntervalVariableMode
 | 
				
			|||||||
        _config.ForType<VariableRuntime, QuestDBHistoryValue>()
 | 
					        _config.ForType<VariableRuntime, QuestDBHistoryValue>()
 | 
				
			||||||
            //.Map(dest => dest.Id, src => CommonUtils.GetSingleId())
 | 
					            //.Map(dest => dest.Id, src => CommonUtils.GetSingleId())
 | 
				
			||||||
            .Map(dest => dest.Id, src => src.Id)//Id更改为变量Id
 | 
					            .Map(dest => dest.Id, src => src.Id)//Id更改为变量Id
 | 
				
			||||||
            .Map(dest => dest.Value, src => src.Value != null ? src.Value.GetType() == typeof(string) ? src.Value.ToString() : JToken.FromObject(src.Value).ToString() : string.Empty)
 | 
					            .Map(dest => dest.Value, src => src.Value == null ? string.Empty : src.Value.ToString() ?? string.Empty)
 | 
				
			||||||
            .Map(dest => dest.CollectTime, (src) => src.CollectTime < DateTime.MinValue ? utcTime : src.CollectTime!.Value.ToUniversalTime())//注意sqlsugar插入时无时区,直接utc时间
 | 
					            .Map(dest => dest.CollectTime, (src) => src.CollectTime < DateTime.MinValue ? utcTime : src.CollectTime!.Value.ToUniversalTime())//注意sqlsugar插入时无时区,直接utc时间
 | 
				
			||||||
            .Map(dest => dest.CreateTime, (src) => DateTime.UtcNow)
 | 
					            .Map(dest => dest.CreateTime, (src) => DateTime.UtcNow)
 | 
				
			||||||
            ;//注意sqlsugar插入时无时区,直接utc时间
 | 
					            ;//注意sqlsugar插入时无时区,直接utc时间
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,11 +25,20 @@
 | 
				
			|||||||
                        <div class="col-12  col-md-12">
 | 
					                        <div class="col-12  col-md-12">
 | 
				
			||||||
                            <BootstrapLabel Value=@SqlDBProducerPropertyLocalizer["BigTextScriptHistoryTable"] ShowLabelTooltip="true" />
 | 
					                            <BootstrapLabel Value=@SqlDBProducerPropertyLocalizer["BigTextScriptHistoryTable"] ShowLabelTooltip="true" />
 | 
				
			||||||
                            <CodeEditor @bind-Value=@businessProperty.BigTextScriptHistoryTable Language="csharp" Theme="vs-dark" />
 | 
					                            <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>
 | 
				
			||||||
                        <div class="col-12  col-md-12">
 | 
					                        <div class="col-12  col-md-12">
 | 
				
			||||||
                            <BootstrapLabel Value=@SqlDBProducerPropertyLocalizer["BigTextScriptRealTable"] ShowLabelTooltip="true" />
 | 
					                            <BootstrapLabel Value=@SqlDBProducerPropertyLocalizer["BigTextScriptRealTable"] ShowLabelTooltip="true" />
 | 
				
			||||||
                            <CodeEditor @bind-Value=@businessProperty.BigTextScriptRealTable Language="csharp" Theme="vs-dark" />
 | 
					                            <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>
 | 
					                        </div>
 | 
				
			||||||
                    </EditTemplate>
 | 
					                    </EditTemplate>
 | 
				
			||||||
                </EditorItem>
 | 
					                </EditorItem>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,17 +8,22 @@
 | 
				
			|||||||
// QQ群:605534569
 | 
					// QQ群:605534569
 | 
				
			||||||
// ------------------------------------------------------------------------------
 | 
					// ------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma warning disable CA2007 // 考虑对等待的任务调用 ConfigureAwait
 | 
				
			||||||
using BootstrapBlazor.Components;
 | 
					using BootstrapBlazor.Components;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using Microsoft.AspNetCore.Components;
 | 
					using Microsoft.AspNetCore.Components;
 | 
				
			||||||
using Microsoft.Extensions.Localization;
 | 
					using Microsoft.Extensions.Localization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using ThingsGateway.Gateway.Razor;
 | 
				
			||||||
 | 
					using ThingsGateway.Plugin.SqlDB;
 | 
				
			||||||
using ThingsGateway.Razor;
 | 
					using ThingsGateway.Razor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace ThingsGateway.Debug
 | 
					namespace ThingsGateway.Debug
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public partial class SqlDBProducerPropertyRazor : IPropertyUIBase
 | 
					    public partial class SqlDBProducerPropertyRazor : IPropertyUIBase
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        [Inject]
 | 
				
			||||||
 | 
					        IStringLocalizer<ThingsGateway.Razor._Imports> RazorLocalizer { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter, EditorRequired]
 | 
					        [Parameter, EditorRequired]
 | 
				
			||||||
@@ -39,6 +44,144 @@ namespace ThingsGateway.Debug
 | 
				
			|||||||
            return base.OnParametersSetAsync();
 | 
					            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; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,8 +12,6 @@ using BootstrapBlazor.Components;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
using Mapster;
 | 
					using Mapster;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using Newtonsoft.Json.Linq;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using SqlSugar;
 | 
					using SqlSugar;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using ThingsGateway.Admin.Application;
 | 
					using ThingsGateway.Admin.Application;
 | 
				
			||||||
@@ -156,17 +154,11 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariableModel<
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        _config = new TypeAdapterConfig();
 | 
					        _config = new TypeAdapterConfig();
 | 
				
			||||||
        _config.ForType<VariableRuntime, SQLHistoryValue>()
 | 
					        _config.ForType<VariableRuntime, SQLHistoryValue>()
 | 
				
			||||||
            .Map(dest => dest.Id, (src) => CommonUtils.GetSingleId())
 | 
					            //.Map(dest => dest.Id, (src) =>CommonUtils.GetSingleId())
 | 
				
			||||||
            //.Map(dest => dest.Id, src => src.Id)//Id更改为变量Id
 | 
					            .Map(dest => dest.Id, src => src.Id)//Id更改为变量Id
 | 
				
			||||||
            .Map(dest => dest.Value, src => src.Value != null ? src.Value.GetType() == typeof(string) ? src.Value.ToString() : JToken.FromObject(src.Value).ToString() : string.Empty)
 | 
					            .Map(dest => dest.Value, src => src.Value == null ? string.Empty : src.Value.ToString() ?? string.Empty)
 | 
				
			||||||
            .Map(dest => dest.CreateTime, (src) => DateTime.Now);
 | 
					            .Map(dest => dest.CreateTime, (src) => DateTime.Now);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        _config.ForType<VariableRuntime, SQLRealValue>()
 | 
					 | 
				
			||||||
    //.Map(dest => dest.Id, (src) =>CommonUtils.GetSingleId())
 | 
					 | 
				
			||||||
    .Map(dest => dest.Id, src => src.Id)//Id更改为变量Id
 | 
					 | 
				
			||||||
    .Map(dest => dest.Value, src => src.Value != null ? src.Value.GetType() == typeof(string) ? src.Value.ToString() : JToken.FromObject(src.Value).ToString() : string.Empty);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        _exRealTimerTick = new(_driverPropertys.RealTableBusinessInterval);
 | 
					        _exRealTimerTick = new(_driverPropertys.RealTableBusinessInterval);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await base.InitChannelAsync(channel, cancellationToken).ConfigureAwait(false);
 | 
					        await base.InitChannelAsync(channel, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
@@ -228,7 +220,7 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariableModel<
 | 
				
			|||||||
                        var groups = varList.GroupBy(a => a.Group);
 | 
					                        var groups = varList.GroupBy(a => a.Group);
 | 
				
			||||||
                        foreach (var item in groups)
 | 
					                        foreach (var item in groups)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            var result = await UpdateAsync(item.Adapt<List<SQLRealValue>>(_config), cancellationToken).ConfigureAwait(false);
 | 
					                            var result = await UpdateAsync(item.Adapt<List<SQLRealValue>>(), cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
                            if (success != result.IsSuccess)
 | 
					                            if (success != result.IsSuccess)
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                if (!result.IsSuccess)
 | 
					                                if (!result.IsSuccess)
 | 
				
			||||||
@@ -239,7 +231,7 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariableModel<
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					                    else
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        var result = await UpdateAsync(varList.Adapt<List<SQLRealValue>>(_config), cancellationToken).ConfigureAwait(false);
 | 
					                        var result = await UpdateAsync(varList.Adapt<List<SQLRealValue>>(), cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
                        if (success != result.IsSuccess)
 | 
					                        if (success != result.IsSuccess)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            if (!result.IsSuccess)
 | 
					                            if (!result.IsSuccess)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		<PackageReference Include="SqlSugar.TDengineCore" Version="4.18.8" GeneratePathProperty="true">
 | 
							<PackageReference Include="SqlSugar.TDengineCore" Version="4.18.31" GeneratePathProperty="true">
 | 
				
			||||||
			<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
 | 
								<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
 | 
				
			||||||
		</PackageReference>
 | 
							</PackageReference>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,8 @@ using BootstrapBlazor.Components;
 | 
				
			|||||||
using Mapster;
 | 
					using Mapster;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using ThingsGateway.Foundation;
 | 
					using ThingsGateway.Foundation;
 | 
				
			||||||
using ThingsGateway.NewLife.Extension;
 | 
					
 | 
				
			||||||
 | 
					using TouchSocket.Core;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace ThingsGateway.Plugin.Webhook;
 | 
					namespace ThingsGateway.Plugin.Webhook;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -133,18 +134,18 @@ public partial class Webhook : BusinessBaseWithCacheIntervalScript<VariableBasic
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private readonly HttpClient client = new HttpClient();
 | 
					    private readonly HttpClient client = new HttpClient();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private async Task<OperResult> WebhookUpAsync(string topic, byte[] payLoad, int count, CancellationToken cancellationToken)
 | 
					    private async Task<OperResult> WebhookUpAsync(TopicArray topicArray, 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);
 | 
					        using var content = new ByteArrayContent(topicArray.Json);
 | 
				
			||||||
        content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
 | 
					        content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try
 | 
					        try
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            // 发送POST请求
 | 
					            // 发送POST请求
 | 
				
			||||||
            HttpResponseMessage response = await client.PostAsync(topic, content, cancellationToken).ConfigureAwait(false);
 | 
					            HttpResponseMessage response = await client.PostAsync(topicArray.Topic, content, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // 检查响应状态
 | 
					            // 检查响应状态
 | 
				
			||||||
            if (response.IsSuccessStatusCode)
 | 
					            if (response.IsSuccessStatusCode)
 | 
				
			||||||
@@ -152,12 +153,14 @@ public partial class Webhook : BusinessBaseWithCacheIntervalScript<VariableBasic
 | 
				
			|||||||
                if (_driverPropertys.DetailLog)
 | 
					                if (_driverPropertys.DetailLog)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Trace)
 | 
					                    if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Trace)
 | 
				
			||||||
                        LogMessage.LogTrace(GetString(topic, payLoad, _memoryVarModelQueue.Count));
 | 
					                        LogMessage.LogTrace(GetDetailLogString(topicArray, _memoryVarModelQueue.Count));
 | 
				
			||||||
 | 
					                    else if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Debug)
 | 
				
			||||||
 | 
					                        LogMessage.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    LogMessage.LogTrace($"Topic:{topic}{Environment.NewLine}Count:{count} {Environment.NewLine} VarModelQueue:{_memoryVarModelQueue.Count}");
 | 
					                    if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Debug)
 | 
				
			||||||
 | 
					                        LogMessage.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                return new();
 | 
					                return new();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -175,11 +178,11 @@ public partial class Webhook : BusinessBaseWithCacheIntervalScript<VariableBasic
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    #region private
 | 
					    #region private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private async ValueTask<OperResult> Update(List<TopicArray> topicJsonList, int count, CancellationToken cancellationToken)
 | 
					    private async ValueTask<OperResult> Update(List<TopicArray> topicArrayList, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        foreach (var topicJson in topicJsonList)
 | 
					        foreach (var topicArray in topicArrayList)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var result = await WebhookUpAsync(topicJson.Topic, topicJson.Json, count, cancellationToken).ConfigureAwait(false);
 | 
					            var result = await WebhookUpAsync(topicArray, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (cancellationToken.IsCancellationRequested)
 | 
					            if (cancellationToken.IsCancellationRequested)
 | 
				
			||||||
                return result;
 | 
					                return result;
 | 
				
			||||||
@@ -203,21 +206,21 @@ public partial class Webhook : BusinessBaseWithCacheIntervalScript<VariableBasic
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken)
 | 
					    private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var topicJsonList = GetAlarmTopicArrays(item);
 | 
					        var topicArrayList = GetAlarmTopicArrays(item);
 | 
				
			||||||
        return Update(topicJsonList, item.Count(), cancellationToken);
 | 
					        return Update(topicArrayList, cancellationToken);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken)
 | 
					    private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var topicJsonList = GetDeviceTopicArray(item);
 | 
					        var topicArrayList = GetDeviceTopicArray(item);
 | 
				
			||||||
        return Update(topicJsonList, item.Count(), cancellationToken);
 | 
					        return Update(topicArrayList, cancellationToken);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
 | 
					    private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var topicJsonList = GetVariableBasicDataTopicArray(item);
 | 
					        var topicArrayList = GetVariableBasicDataTopicArray(item);
 | 
				
			||||||
        return Update(topicJsonList, item.Count(), cancellationToken);
 | 
					        return Update(topicArrayList, cancellationToken);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -127,11 +127,11 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<Variabl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    #region private
 | 
					    #region private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private async ValueTask<OperResult> Update(List<TopicArray> topicJsonList, int count, CancellationToken cancellationToken)
 | 
					    private async ValueTask<OperResult> Update(List<TopicArray> topicArrayList, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        foreach (var topicJson in topicJsonList)
 | 
					        foreach (var topicArray in topicArrayList)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var result = await KafKaUpAsync(topicJson.Topic, topicJson.Json, count, cancellationToken).ConfigureAwait(false);
 | 
					            var result = await KafKaUpAsync(topicArray, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
            if (success != result.IsSuccess)
 | 
					            if (success != result.IsSuccess)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!result.IsSuccess)
 | 
					                if (!result.IsSuccess)
 | 
				
			||||||
@@ -150,20 +150,20 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<Variabl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private async ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken)
 | 
					    private async ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var topicJsonList = GetAlarmTopicArrays(item);
 | 
					        var topicArrayList = GetAlarmTopicArrays(item);
 | 
				
			||||||
        return await Update(topicJsonList, item.Count(), cancellationToken).ConfigureAwait(false);
 | 
					        return await Update(topicArrayList, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private async ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken)
 | 
					    private async ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var topicJsonList = GetDeviceTopicArray(item);
 | 
					        var topicArrayList = GetDeviceTopicArray(item);
 | 
				
			||||||
        return await Update(topicJsonList, item.Count(), cancellationToken).ConfigureAwait(false);
 | 
					        return await Update(topicArrayList, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private async ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
 | 
					    private async ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var topicJsonList = GetVariableBasicDataTopicArray(item);
 | 
					        var topicArrayList = GetVariableBasicDataTopicArray(item);
 | 
				
			||||||
        return await Update(topicJsonList, item.Count(), cancellationToken).ConfigureAwait(false);
 | 
					        return await Update(topicArrayList, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #endregion private
 | 
					    #endregion private
 | 
				
			||||||
@@ -203,13 +203,13 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<Variabl
 | 
				
			|||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// kafka上传,返回上传结果
 | 
					    /// kafka上传,返回上传结果
 | 
				
			||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    public async ValueTask<OperResult> KafKaUpAsync(string topic, byte[] payLoad, int count, CancellationToken cancellationToken)
 | 
					    public async ValueTask<OperResult> KafKaUpAsync(TopicArray topicArray, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try
 | 
					        try
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            using CancellationTokenSource cancellationTokenSource = new(_driverPropertys.Timeout);
 | 
					            using CancellationTokenSource cancellationTokenSource = new(_driverPropertys.Timeout);
 | 
				
			||||||
            using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, cancellationToken);
 | 
					            using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, cancellationToken);
 | 
				
			||||||
            var result = await _producer.ProduceAsync(topic, new Message<Null, byte[]> { Value = payLoad }, stoppingToken.Token).ConfigureAwait(false);
 | 
					            var result = await _producer.ProduceAsync(topicArray.Topic, new Message<Null, byte[]> { Value = topicArray.Json }, stoppingToken.Token).ConfigureAwait(false);
 | 
				
			||||||
            if (result.Status != PersistenceStatus.Persisted)
 | 
					            if (result.Status != PersistenceStatus.Persisted)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return new OperResult("Upload fail");
 | 
					                return new OperResult("Upload fail");
 | 
				
			||||||
@@ -219,12 +219,14 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<Variabl
 | 
				
			|||||||
                if (_driverPropertys.DetailLog)
 | 
					                if (_driverPropertys.DetailLog)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Trace)
 | 
					                    if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Trace)
 | 
				
			||||||
                        LogMessage.LogTrace(GetString(topic, payLoad, _memoryVarModelQueue.Count));
 | 
					                        LogMessage.LogTrace(GetDetailLogString(topicArray, _memoryVarModelQueue.Count));
 | 
				
			||||||
 | 
					                    else if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Debug)
 | 
				
			||||||
 | 
					                        LogMessage.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    LogMessage.LogTrace($"Topic:{topic}{Environment.NewLine}Count:{count} {Environment.NewLine} VarModelQueue:{_memoryVarModelQueue.Count}");
 | 
					                    if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Debug)
 | 
				
			||||||
 | 
					                        LogMessage.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                return OperResult.Success;
 | 
					                return OperResult.Success;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -80,6 +80,11 @@ public class ModbusMaster : CollectFoundationBase
 | 
				
			|||||||
    protected override async Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables)
 | 
					    protected override async Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        await Task.CompletedTask.ConfigureAwait(false);
 | 
					        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;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,13 +69,13 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableBa
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    device = deviceData.Name,
 | 
					                    device = deviceData.Name,
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                var topicJson = new TopicArray()
 | 
					                var topicArray = new TopicArray()
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Topic = "v1/gateway/connect",
 | 
					                    Topic = "v1/gateway/connect",
 | 
				
			||||||
                    Json = Serialize(json, _driverPropertys.JsonFormattingIndented)
 | 
					                    Json = Serialize(json, _driverPropertys.JsonFormattingIndented)
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                topicJsonTBList.Add(topicJson);
 | 
					                topicJsonTBList.Add(topicArray);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -83,17 +83,17 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableBa
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    device = deviceData.Name,
 | 
					                    device = deviceData.Name,
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                var topicJson = new TopicArray()
 | 
					                var topicArray = new TopicArray()
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Topic = "v1/gateway/disconnect",
 | 
					                    Topic = "v1/gateway/disconnect",
 | 
				
			||||||
                    Json = Serialize(json, _driverPropertys.JsonFormattingIndented)
 | 
					                    Json = Serialize(json, _driverPropertys.JsonFormattingIndented)
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                topicJsonTBList.Add(topicJson);
 | 
					                topicJsonTBList.Add(topicArray);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        var result = await Update(topicJsonTBList, 1, default).ConfigureAwait(false);
 | 
					        var result = await Update(topicJsonTBList, default).ConfigureAwait(false);
 | 
				
			||||||
        if (success != result.IsSuccess)
 | 
					        if (success != result.IsSuccess)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (!result.IsSuccess)
 | 
					            if (!result.IsSuccess)
 | 
				
			||||||
@@ -206,11 +206,11 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableBa
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    #region private
 | 
					    #region private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private async ValueTask<OperResult> Update(List<TopicArray> topicJsonList, int count, CancellationToken cancellationToken)
 | 
					    private async ValueTask<OperResult> Update(List<TopicArray> topicArrayList, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        foreach (var topicJson in topicJsonList)
 | 
					        foreach (TopicArray topicArray in topicArrayList)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var result = await MqttUpAsync(topicJson.Topic, topicJson.Json, count, cancellationToken).ConfigureAwait(false);
 | 
					            var result = await MqttUpAsync(topicArray, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
            if (cancellationToken.IsCancellationRequested)
 | 
					            if (cancellationToken.IsCancellationRequested)
 | 
				
			||||||
                return result;
 | 
					                return result;
 | 
				
			||||||
            if (success != result.IsSuccess)
 | 
					            if (success != result.IsSuccess)
 | 
				
			||||||
@@ -231,21 +231,21 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableBa
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken)
 | 
					    private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var topicJsonList = GetAlarmTopicArrays(item);
 | 
					        var topicArrayList = GetAlarmTopicArrays(item);
 | 
				
			||||||
        return Update(topicJsonList, item.Count(), cancellationToken);
 | 
					        return Update(topicArrayList, cancellationToken);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken)
 | 
					    private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var topicJsonList = GetDeviceTopicArray(item);
 | 
					        var topicArrayList = GetDeviceTopicArray(item);
 | 
				
			||||||
        return Update(topicJsonList, item.Count(), cancellationToken);
 | 
					        return Update(topicArrayList, cancellationToken);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
 | 
					    private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var topicJsonList = GetVariableBasicDataTopicArray(item);
 | 
					        var topicArrayList = GetVariableBasicDataTopicArray(item);
 | 
				
			||||||
        return Update(topicJsonList, item.Count(), cancellationToken);
 | 
					        return Update(topicArrayList, cancellationToken);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #endregion private
 | 
					    #endregion private
 | 
				
			||||||
@@ -463,7 +463,7 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableBa
 | 
				
			|||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 上传mqtt,返回上传结果
 | 
					    /// 上传mqtt,返回上传结果
 | 
				
			||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    public async ValueTask<OperResult> MqttUpAsync(string topic, byte[] payLoad, int count, CancellationToken cancellationToken = default)
 | 
					    public async ValueTask<OperResult> MqttUpAsync(TopicArray topicArray, CancellationToken cancellationToken = default)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try
 | 
					        try
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -471,20 +471,22 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableBa
 | 
				
			|||||||
            if (isConnect.IsSuccess)
 | 
					            if (isConnect.IsSuccess)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var variableMessage = new MqttApplicationMessageBuilder()
 | 
					                var variableMessage = new MqttApplicationMessageBuilder()
 | 
				
			||||||
    .WithTopic(topic).WithRetainFlag(true)
 | 
					    .WithTopic(topicArray.Topic).WithRetainFlag(true)
 | 
				
			||||||
    .WithPayload(payLoad).Build();
 | 
					    .WithPayload(topicArray.Json).Build();
 | 
				
			||||||
                var result = await _mqttClient.PublishAsync(variableMessage, cancellationToken).ConfigureAwait(false);
 | 
					                var result = await _mqttClient.PublishAsync(variableMessage, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
                if (result.IsSuccess)
 | 
					                if (result.IsSuccess)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (_driverPropertys.DetailLog)
 | 
					                    if (_driverPropertys.DetailLog)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Trace)
 | 
					                        if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Trace)
 | 
				
			||||||
                            LogMessage.LogTrace(GetString(topic, payLoad, _memoryVarModelQueue.Count));
 | 
					                            LogMessage.LogTrace(GetDetailLogString(topicArray, _memoryVarModelQueue.Count));
 | 
				
			||||||
 | 
					                        else if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Debug)
 | 
				
			||||||
 | 
					                            LogMessage.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					                    else
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        LogMessage.LogTrace($"Topic:{topic}{Environment.NewLine}Count:{count} {Environment.NewLine} VarModelQueue:{_memoryVarModelQueue.Count}");
 | 
					                        if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Debug)
 | 
				
			||||||
 | 
					                            LogMessage.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    return OperResult.Success;
 | 
					                    return OperResult.Success;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,7 +46,6 @@ namespace ThingsGateway.Plugin.Mqtt
 | 
				
			|||||||
            if (mqttClientProperty.TLS == true)
 | 
					            if (mqttClientProperty.TLS == true)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 | 
					 | 
				
			||||||
                    var filePath = Path.Combine("PluginFile", Id, nameof(mqttClientProperty.CAFile));
 | 
					                    var filePath = Path.Combine("PluginFile", Id, nameof(mqttClientProperty.CAFile));
 | 
				
			||||||
                    if (!Directory.Exists(filePath))//如果不存在就创建文件夹
 | 
					                    if (!Directory.Exists(filePath))//如果不存在就创建文件夹
 | 
				
			||||||
                        Directory.CreateDirectory(filePath);
 | 
					                        Directory.CreateDirectory(filePath);
 | 
				
			||||||
@@ -230,7 +229,6 @@ namespace ThingsGateway.Plugin.Mqtt
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
            var op = new DialogOption()
 | 
					            var op = new DialogOption()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                IsScrolling = true,
 | 
					                IsScrolling = true,
 | 
				
			||||||
@@ -245,6 +243,142 @@ namespace ThingsGateway.Plugin.Mqtt
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        {nameof(ScriptCheck.Data),data },
 | 
					        {nameof(ScriptCheck.Data),data },
 | 
				
			||||||
        {nameof(ScriptCheck.Script),script },
 | 
					        {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 =>
 | 
					        {nameof(ScriptCheck.ScriptChanged),EventCallback.Factory.Create<string>(this, v =>
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
                 if (pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel))
 | 
					                 if (pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel))
 | 
				
			||||||
@@ -269,6 +403,7 @@ namespace ThingsGateway.Plugin.Mqtt
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Inject]
 | 
					        [Inject]
 | 
				
			||||||
        private DialogService DialogService { get; set; }
 | 
					        private DialogService DialogService { get; set; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -141,11 +141,11 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableBa
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    #region private
 | 
					    #region private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private async ValueTask<OperResult> Update(List<TopicArray> topicJsonList, int count, CancellationToken cancellationToken)
 | 
					    private async ValueTask<OperResult> Update(List<TopicArray> topicArrayList, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        foreach (var topicJson in topicJsonList)
 | 
					        foreach (var topicArray in topicArrayList)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var result = await MqttUpAsync(topicJson.Topic, topicJson.Json, count, cancellationToken).ConfigureAwait(false);
 | 
					            var result = await MqttUpAsync(topicArray, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
            if (success != result.IsSuccess)
 | 
					            if (success != result.IsSuccess)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!result.IsSuccess)
 | 
					                if (!result.IsSuccess)
 | 
				
			||||||
@@ -164,21 +164,21 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableBa
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken)
 | 
					    private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var topicJsonList = GetAlarmTopicArrays(item);
 | 
					        var topicArrayList = GetAlarmTopicArrays(item);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return Update(topicJsonList, item.Count(), cancellationToken);
 | 
					        return Update(topicArrayList, cancellationToken);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken)
 | 
					    private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var topicJsonList = GetDeviceTopicArray(item);
 | 
					        var topicArrayList = GetDeviceTopicArray(item);
 | 
				
			||||||
        return Update(topicJsonList, item.Count(), cancellationToken);
 | 
					        return Update(topicArrayList, cancellationToken);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
 | 
					    private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var topicJsonList = GetVariableBasicDataTopicArray(item);
 | 
					        var topicArrayList = GetVariableBasicDataTopicArray(item);
 | 
				
			||||||
        return Update(topicJsonList, item.Count(), cancellationToken);
 | 
					        return Update(topicArrayList, cancellationToken);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #endregion private
 | 
					    #endregion private
 | 
				
			||||||
@@ -261,12 +261,12 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableBa
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            foreach (var item in varData)
 | 
					            foreach (var item in varData)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var topicJsonList = GetVariableBasicData(item);
 | 
					                var topicArrayList = GetVariableBasicDataTopicArray(item);
 | 
				
			||||||
                foreach (var topicJson in topicJsonList)
 | 
					                foreach (var topicArray in topicArrayList)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Messages.Add(new MqttApplicationMessageBuilder()
 | 
					                    Messages.Add(new MqttApplicationMessageBuilder()
 | 
				
			||||||
    .WithTopic(topicJson.Topic)
 | 
					    .WithTopic(topicArray.Topic)
 | 
				
			||||||
    .WithPayload(topicJson.Json).Build());
 | 
					    .WithPayload(topicArray.Json).Build());
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -276,12 +276,12 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableBa
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var item in devData)
 | 
					                foreach (var item in devData)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var topicJsonList = GetDeviceData(item);
 | 
					                    var topicArrayList = GetDeviceTopicArray(item);
 | 
				
			||||||
                    foreach (var topicJson in topicJsonList)
 | 
					                    foreach (var topicArray in topicArrayList)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        Messages.Add(new MqttApplicationMessageBuilder()
 | 
					                        Messages.Add(new MqttApplicationMessageBuilder()
 | 
				
			||||||
        .WithTopic(topicJson.Topic)
 | 
					        .WithTopic(topicArray.Topic)
 | 
				
			||||||
        .WithPayload(topicJson.Json).Build());
 | 
					        .WithPayload(topicArray.Json).Build());
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -290,12 +290,12 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableBa
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            foreach (var item in alramData)
 | 
					            foreach (var item in alramData)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var topicJsonList = GetAlarms(item);
 | 
					                var topicArrayList = GetAlarmTopicArrays(item);
 | 
				
			||||||
                foreach (var topicJson in topicJsonList)
 | 
					                foreach (var topicArray in topicArrayList)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Messages.Add(new MqttApplicationMessageBuilder()
 | 
					                    Messages.Add(new MqttApplicationMessageBuilder()
 | 
				
			||||||
    .WithTopic(topicJson.Topic)
 | 
					    .WithTopic(topicArray.Topic)
 | 
				
			||||||
    .WithPayload(topicJson.Json).Build());
 | 
					    .WithPayload(topicArray.Json).Build());
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -394,25 +394,27 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableBa
 | 
				
			|||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 上传mqtt,返回上传结果
 | 
					    /// 上传mqtt,返回上传结果
 | 
				
			||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    public async ValueTask<OperResult> MqttUpAsync(string topic, byte[] payLoad, int count, CancellationToken cancellationToken = default)
 | 
					    public async ValueTask<OperResult> MqttUpAsync(TopicArray topicArray, CancellationToken cancellationToken = default)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try
 | 
					        try
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var message = new MqttApplicationMessageBuilder()
 | 
					            var message = new MqttApplicationMessageBuilder()
 | 
				
			||||||
.WithTopic(topic)
 | 
					.WithTopic(topicArray.Topic)
 | 
				
			||||||
.WithPayload(payLoad).Build();
 | 
					.WithPayload(topicArray.Json).Build();
 | 
				
			||||||
            await _mqttServer.InjectApplicationMessage(
 | 
					            await _mqttServer.InjectApplicationMessage(
 | 
				
			||||||
                    new InjectedMqttApplicationMessage(message), cancellationToken).ConfigureAwait(false);
 | 
					                    new InjectedMqttApplicationMessage(message), cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (_driverPropertys.DetailLog)
 | 
					            if (_driverPropertys.DetailLog)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Trace)
 | 
					                if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Trace)
 | 
				
			||||||
                    LogMessage.LogTrace(GetString(topic, payLoad, _memoryVarModelQueue.Count));
 | 
					                    LogMessage.LogTrace(GetDetailLogString(topicArray, _memoryVarModelQueue.Count));
 | 
				
			||||||
 | 
					                else if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Debug)
 | 
				
			||||||
 | 
					                    LogMessage.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                LogMessage.LogTrace($"Topic:{topic}{Environment.NewLine}Count:{count} {Environment.NewLine} VarModelQueue:{_memoryVarModelQueue.Count}");
 | 
					                if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Debug)
 | 
				
			||||||
 | 
					                    LogMessage.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return OperResult.Success;
 | 
					            return OperResult.Success;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -122,25 +122,31 @@ public class OpcDaMaster : CollectBase
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            if (deviceVariables.Count > 0)
 | 
					            if (deviceVariables.Count > 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var result = _plc.AddItemsWithSave(deviceVariables.Where(a => !string.IsNullOrEmpty(a.RegisterAddress)).Select(a => a.RegisterAddress!).ToList());
 | 
					                List<VariableSourceRead> variableSourceReads = new List<VariableSourceRead>();
 | 
				
			||||||
                var sourVars = result?.Select(
 | 
					                foreach (var deviceVariableGroups in deviceVariables.GroupBy(a => a.CollectGroup))
 | 
				
			||||||
          it =>
 | 
					                {
 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
              var read = new VariableSourceRead()
 | 
					 | 
				
			||||||
              {
 | 
					 | 
				
			||||||
                  TimeTick = new(_driverProperties.UpdateRate.ToString()),
 | 
					 | 
				
			||||||
                  RegisterAddress = it.Key,
 | 
					 | 
				
			||||||
              };
 | 
					 | 
				
			||||||
              HashSet<string> ids = new(it.Value.Select(b => b.ItemID));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
              var variables = deviceVariables.Where(a => ids.Contains(a.RegisterAddress));
 | 
					                    var result = _plc.AddItemsWithSave(deviceVariableGroups.Where(a => !string.IsNullOrEmpty(a.RegisterAddress)).Select(a => a.RegisterAddress!).ToList());
 | 
				
			||||||
              foreach (var v in variables)
 | 
					                    var sourVars = result?.Select(
 | 
				
			||||||
 | 
					              it =>
 | 
				
			||||||
              {
 | 
					              {
 | 
				
			||||||
                  read.AddVariable(v);
 | 
					                  var read = new VariableSourceRead()
 | 
				
			||||||
              }
 | 
					                  {
 | 
				
			||||||
              return read;
 | 
					                      TimeTick = new(_driverProperties.UpdateRate.ToString()),
 | 
				
			||||||
          }).ToList();
 | 
					                      RegisterAddress = it.Key,
 | 
				
			||||||
                return sourVars;
 | 
					                  };
 | 
				
			||||||
 | 
					                  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
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -187,24 +187,27 @@ public class OpcUaMaster : CollectBase
 | 
				
			|||||||
        await Task.CompletedTask.ConfigureAwait(false);
 | 
					        await Task.CompletedTask.ConfigureAwait(false);
 | 
				
			||||||
        if (deviceVariables.Count > 0)
 | 
					        if (deviceVariables.Count > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var dataLists = deviceVariables.ChunkBetter(_driverProperties.GroupSize);
 | 
					            List<VariableSourceRead> variableSourceReads = new List<VariableSourceRead>();
 | 
				
			||||||
 | 
					            foreach (var deviceVariableGroups in deviceVariables.GroupBy(a => a.CollectGroup))
 | 
				
			||||||
            var dataResult = new List<VariableSourceRead>();
 | 
					 | 
				
			||||||
            foreach (var variable in dataLists)
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var sourVars = new VariableSourceRead()
 | 
					                var dataLists = deviceVariableGroups.ChunkBetter(_driverProperties.GroupSize);
 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    TimeTick = new(_driverProperties.UpdateRate.ToString()),
 | 
					 | 
				
			||||||
                    RegisterAddress = Guid.NewGuid().ToString(),
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
                foreach (var item in variable)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    sourVars.AddVariable(item);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                dataResult.Add(sourVars);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            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
 | 
					        else
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -202,6 +202,7 @@ public partial class OpcUaServer : BusinessBase
 | 
				
			|||||||
                success = true;
 | 
					                success = true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        catch (OperationCanceledException) { }
 | 
				
			||||||
        catch (Exception ex)
 | 
					        catch (Exception ex)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (success)
 | 
					            if (success)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -128,11 +128,11 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScript<Vari
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    #region private
 | 
					    #region private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private async ValueTask<OperResult> Update(List<TopicArray> topicJsonList, int count, CancellationToken cancellationToken)
 | 
					    private async ValueTask<OperResult> Update(List<TopicArray> topicArrayList, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        foreach (var topicJson in topicJsonList)
 | 
					        foreach (var topicArray in topicArrayList)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var result = await Publish(topicJson.Topic, topicJson.Json, count, cancellationToken).ConfigureAwait(false);
 | 
					            var result = await RabbitMQUpAsync(topicArray, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
            if (success != result.IsSuccess)
 | 
					            if (success != result.IsSuccess)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!result.IsSuccess)
 | 
					                if (!result.IsSuccess)
 | 
				
			||||||
@@ -152,20 +152,20 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScript<Vari
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken)
 | 
					    private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var topicJsonList = GetAlarmTopicArrays(item);
 | 
					        var topicArrayList = GetAlarmTopicArrays(item);
 | 
				
			||||||
        return Update(topicJsonList, item.Count(), cancellationToken);
 | 
					        return Update(topicArrayList, cancellationToken);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken)
 | 
					    private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var topicJsonList = GetDeviceTopicArray(item);
 | 
					        var topicArrayList = GetDeviceTopicArray(item);
 | 
				
			||||||
        return Update(topicJsonList, item.Count(), cancellationToken);
 | 
					        return Update(topicArrayList, cancellationToken);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
 | 
					    private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var topicJsonList = GetVariableBasicDataTopicArray(item);
 | 
					        var topicArrayList = GetVariableBasicDataTopicArray(item);
 | 
				
			||||||
        return Update(topicJsonList, item.Count(), cancellationToken);
 | 
					        return Update(topicArrayList, cancellationToken);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #endregion private
 | 
					    #endregion private
 | 
				
			||||||
@@ -206,23 +206,25 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScript<Vari
 | 
				
			|||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 上传,返回上传结果
 | 
					    /// 上传,返回上传结果
 | 
				
			||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    public async Task<OperResult> Publish(string topic, byte[] payLoad, int count, CancellationToken cancellationToken)
 | 
					    public async Task<OperResult> RabbitMQUpAsync(TopicArray topicArray, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try
 | 
					        try
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_channel != null)
 | 
					            if (_channel != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await _channel.BasicPublishAsync(_driverPropertys.ExchangeName, topic, payLoad, cancellationToken).ConfigureAwait(false);
 | 
					                await _channel.BasicPublishAsync(_driverPropertys.ExchangeName, topicArray.Topic, topicArray.Json, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (_driverPropertys.DetailLog)
 | 
					                if (_driverPropertys.DetailLog)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Trace)
 | 
					                    if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Trace)
 | 
				
			||||||
                        LogMessage.LogTrace(GetString(topic, payLoad, _memoryVarModelQueue.Count));
 | 
					                        LogMessage.LogTrace(GetDetailLogString(topicArray, _memoryVarModelQueue.Count));
 | 
				
			||||||
 | 
					                    else if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Debug)
 | 
				
			||||||
 | 
					                        LogMessage.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    LogMessage.LogTrace($"Topic:{topic}{Environment.NewLine}Count:{count} {Environment.NewLine} VarModelQueue:{_memoryVarModelQueue.Count}");
 | 
					                    if (LogMessage.LogLevel <= TouchSocket.Core.LogLevel.Debug)
 | 
				
			||||||
 | 
					                        LogMessage.LogDebug(GetCountLogString(topicArray, _memoryVarModelQueue.Count));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                return OperResult.Success;
 | 
					                return OperResult.Success;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 { }
 | 
					        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.Authorization;
 | 
				
			||||||
using Microsoft.AspNetCore.Builder;
 | 
					using Microsoft.AspNetCore.Builder;
 | 
				
			||||||
using Microsoft.AspNetCore.Components.Authorization;
 | 
					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.Hosting;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
using Microsoft.AspNetCore.HttpOverrides;
 | 
					using Microsoft.AspNetCore.HttpOverrides;
 | 
				
			||||||
@@ -24,6 +27,7 @@ using Microsoft.Extensions.Options;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
using Newtonsoft.Json;
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using System.Security.Cryptography.X509Certificates;
 | 
				
			||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
using System.Text.Encodings.Web;
 | 
					using System.Text.Encodings.Web;
 | 
				
			||||||
using System.Text.Unicode;
 | 
					using System.Text.Unicode;
 | 
				
			||||||
@@ -273,6 +277,21 @@ public class Startup : AppStartup
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        services.AddSignalR();
 | 
					        services.AddSignalR();
 | 
				
			||||||
        #endregion
 | 
					        #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" />
 | 
						<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.Application\ThingsGateway.Gateway.Application.csproj" />
 | 
				
			||||||
		<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj" />
 | 
							<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj" />
 | 
				
			||||||
		<ProjectReference Include="..\Gateway\ThingsGateway.Management\ThingsGateway.Management.csproj" />
 | 
							<ProjectReference Include="..\Gateway\ThingsGateway.Management\ThingsGateway.Management.csproj" />
 | 
				
			||||||
@@ -19,8 +19,8 @@
 | 
				
			|||||||
	<!--nuget包解压复制文件,上下文动态加载,网关管理和网关冗余-->
 | 
						<!--nuget包解压复制文件,上下文动态加载,网关管理和网关冗余-->
 | 
				
			||||||
	<Import Project="..\ThingsGateway.Server\targets\GatewayOther.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayRelease' " />
 | 
						<Import Project="..\ThingsGateway.Server\targets\GatewayOther.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayRelease' " />
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	<ItemGroup  Condition=" '$(SolutionName)' == 'ThingsGatewayRelease' " >
 | 
						<ItemGroup Condition=" '$(SolutionName)' == 'ThingsGatewayRelease' ">
 | 
				
			||||||
		<PackageReference Include="ThingsGateway.Photino.Blazor" Version="$(Version)"  />
 | 
							<PackageReference Include="ThingsGateway.Photino.Blazor" Version="$(Version)" />
 | 
				
			||||||
	</ItemGroup>
 | 
						</ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -29,15 +29,17 @@
 | 
				
			|||||||
	<!--直接引用-->
 | 
						<!--直接引用-->
 | 
				
			||||||
	<Import Project="..\ThingsGateway.Server\targets\PluginDebug.targets" Condition=" '$(SolutionName)' != 'ThingsGatewayPro' AND '$(Configuration)' == 'Debug' " />
 | 
						<Import Project="..\ThingsGateway.Server\targets\PluginDebug.targets" Condition=" '$(SolutionName)' != 'ThingsGatewayPro' AND '$(Configuration)' == 'Debug' " />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<!--直接引用Pro-->
 | 
				
			||||||
 | 
						<Import Project="..\ThingsGateway.Server\targets\PluginDebug.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayPro' AND '$(Configuration)' == 'Debug'" />
 | 
				
			||||||
 | 
						<Import Project="..\ThingsGateway.Server\targets\PluginContext.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayPro' AND  '$(Configuration)' != 'Debug' " />
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	<!--nuget包解压复制文件,插件域隔离动态加载-->
 | 
						<!--nuget包解压复制文件,插件域隔离动态加载-->
 | 
				
			||||||
	<!--<Import Project="targets\Plugin.targets" />-->
 | 
						<!--<Import Project="targets\Plugin.targets" />-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<!--nuget包解压复制文件,上下文动态加载,Pro插件-->
 | 
						<!--nuget包解压复制文件,上下文动态加载,Pro插件-->
 | 
				
			||||||
	<Import Project="..\ThingsGateway.Server\targets\Pro2.targets" Condition=" '$(SolutionName)' != 'ThingsGatewayPro' OR  '$(Configuration)' != 'Debug'" />
 | 
						<Import Project="..\ThingsGateway.Server\targets\Pro2.targets" Condition=" '$(SolutionName)' != 'ThingsGatewayPro' OR  '$(Configuration)' != 'Debug'" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<!--直接引用Pro-->
 | 
					
 | 
				
			||||||
	<Import Project="..\ThingsGateway.Server\targets\PluginDebug.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayPro' AND '$(Configuration)' == 'Debug'" />
 | 
					 | 
				
			||||||
	<Import Project="..\ThingsGateway.Server\targets\PluginContext.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayPro' AND  '$(Configuration)' != 'Debug' " />
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<Import Project="..\ThingsGateway.Server\targets\ProPluginDebug.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayPro' AND '$(Configuration)' == 'Debug'" />
 | 
						<Import Project="..\ThingsGateway.Server\targets\ProPluginDebug.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayPro' AND '$(Configuration)' == 'Debug'" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -87,6 +89,9 @@
 | 
				
			|||||||
		<Compile Include="..\ThingsGateway.Server\Program\SingleFilePublish.cs" Link="Program\SingleFilePublish.cs" />
 | 
							<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" />
 | 
							<Content Include="..\ThingsGateway.Server\wwwroot\favicon.ico" Link="wwwroot\favicon.ico" />
 | 
				
			||||||
@@ -119,11 +124,23 @@
 | 
				
			|||||||
		</EmbeddedResource>
 | 
							</EmbeddedResource>
 | 
				
			||||||
	</ItemGroup>
 | 
						</ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<ItemGroup>
 | 
				
			||||||
 | 
						  <Content Remove="launchSettings.json" />
 | 
				
			||||||
 | 
						</ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<ItemGroup>
 | 
						<ItemGroup>
 | 
				
			||||||
		<Content Include="favicon.ico">
 | 
							<Content Include="favicon.ico">
 | 
				
			||||||
			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
								<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
				
			||||||
		</Content>
 | 
							</Content>
 | 
				
			||||||
	</ItemGroup>
 | 
						</ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<ItemGroup>
 | 
				
			||||||
 | 
						  <None Include="Properties\launchSettings.json">
 | 
				
			||||||
 | 
						    <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
 | 
				
			||||||
 | 
						    <CopyToPublishDirectory>Never</CopyToPublishDirectory>
 | 
				
			||||||
 | 
						  </None>
 | 
				
			||||||
 | 
						</ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</Project>
 | 
					</Project>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  "Startway": "DOTNET", // 启动方式:DOTNET: (直接启动) ;WindowsService:(windows服务)  PM2, Systemctl等不需要配置。对应文件夹下的命令文件
 | 
					  "Startway": "DOTNET", // 启动方式:DOTNET: (直接启动) ;WindowsService:(windows服务)  PM2, Systemctl等不需要配置。对应文件夹下的命令文件
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  "AppSettings": {
 | 
					  "AppSettings": {
 | 
				
			||||||
    "InjectSpecificationDocument": true, // 生产环境是否开启Swagger
 | 
					    "InjectSpecificationDocument": true, // 是否开启Swagger
 | 
				
			||||||
    "ExternalAssemblies": [ "Plugins" ], // 插件目录
 | 
					    "ExternalAssemblies": [ "Plugins" ], // 插件目录
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // nuget动态加载的程序集
 | 
					    // nuget动态加载的程序集
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  "Startway": "", // 启动方式:DOTNET: (直接启动) ;WindowsService:(windows服务)  PM2, Systemctl等不需要配置。对应文件夹下的命令文件
 | 
					  "Startway": "", // 启动方式:DOTNET: (直接启动) ;WindowsService:(windows服务)  PM2, Systemctl等不需要配置。对应文件夹下的命令文件
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  "AppSettings": {
 | 
					  "AppSettings": {
 | 
				
			||||||
    "InjectSpecificationDocument": true, // 生产环境是否开启Swagger
 | 
					    "InjectSpecificationDocument": false, // 是否开启Swagger
 | 
				
			||||||
    "ExternalAssemblies": [ "Plugins" ], // 插件目录
 | 
					    "ExternalAssemblies": [ "Plugins" ], // 插件目录
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // nuget动态加载的程序集
 | 
					    // nuget动态加载的程序集
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Microsoft.AspNetCore.Authorization;
 | 
				
			||||||
using Microsoft.AspNetCore.Components.Authorization;
 | 
					using Microsoft.AspNetCore.Components.Authorization;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.DataProtection;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
 | 
				
			||||||
using Microsoft.AspNetCore.HttpOverrides;
 | 
					using Microsoft.AspNetCore.HttpOverrides;
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc.Controllers;
 | 
					using Microsoft.AspNetCore.Mvc.Controllers;
 | 
				
			||||||
using Microsoft.AspNetCore.StaticFiles;
 | 
					using Microsoft.AspNetCore.StaticFiles;
 | 
				
			||||||
@@ -18,6 +21,7 @@ using Microsoft.Extensions.Options;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
using Newtonsoft.Json;
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using System.Security.Cryptography.X509Certificates;
 | 
				
			||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
using System.Text.Encodings.Web;
 | 
					using System.Text.Encodings.Web;
 | 
				
			||||||
using System.Text.Unicode;
 | 
					using System.Text.Unicode;
 | 
				
			||||||
@@ -291,6 +295,21 @@ public class Startup : AppStartup
 | 
				
			|||||||
        services.AddAuthorizationCore();
 | 
					        services.AddAuthorizationCore();
 | 
				
			||||||
        services.AddScoped<IAuthorizationHandler, BlazorServerAuthenticationHandler>();
 | 
					        services.AddScoped<IAuthorizationHandler, BlazorServerAuthenticationHandler>();
 | 
				
			||||||
        services.AddScoped<AuthenticationStateProvider, BlazorServerAuthenticationStateProvider>();
 | 
					        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">
 | 
							<Content Include="favicon.ico">
 | 
				
			||||||
			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
								<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
				
			||||||
		</Content>
 | 
							</Content>
 | 
				
			||||||
 | 
							<None Update="ThingsGateway.pfx">
 | 
				
			||||||
 | 
							  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
				
			||||||
 | 
							</None>
 | 
				
			||||||
		<None Update="WindowsService">
 | 
							<None Update="WindowsService">
 | 
				
			||||||
			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
								<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
				
			||||||
		</None>
 | 
							</None>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								src/ThingsGateway.Server/ThingsGateway.pfx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/ThingsGateway.Server/ThingsGateway.pfx
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -16,7 +16,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<ItemGroup>
 | 
						<ItemGroup>
 | 
				
			||||||
	  <PackageReference Include="TouchSocket.Dmtp" Version="3.1.0" />
 | 
						  <PackageReference Include="TouchSocket.Dmtp" Version="3.1.2" />
 | 
				
			||||||
	</ItemGroup>
 | 
						</ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,10 +23,10 @@ namespace ThingsGateway.Upgrade;
 | 
				
			|||||||
[RolePermission]
 | 
					[RolePermission]
 | 
				
			||||||
[LoggingMonitor]
 | 
					[LoggingMonitor]
 | 
				
			||||||
[Authorize(AuthenticationSchemes = "Bearer")]
 | 
					[Authorize(AuthenticationSchemes = "Bearer")]
 | 
				
			||||||
public class AutoUpdateControler : ControllerBase
 | 
					public class AutoUpdateController : ControllerBase
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    private IUpdateZipFileHostedService _updateZipFileService;
 | 
					    private IUpdateZipFileHostedService _updateZipFileService;
 | 
				
			||||||
    public AutoUpdateControler(IUpdateZipFileHostedService updateZipFileService)
 | 
					    public AutoUpdateController(IUpdateZipFileHostedService updateZipFileService)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        _updateZipFileService = updateZipFileService;
 | 
					        _updateZipFileService = updateZipFileService;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Microsoft.AspNetCore.Authorization;
 | 
				
			||||||
using Microsoft.AspNetCore.Components.Authorization;
 | 
					using Microsoft.AspNetCore.Components.Authorization;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.DataProtection;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
 | 
				
			||||||
using Microsoft.AspNetCore.HttpOverrides;
 | 
					using Microsoft.AspNetCore.HttpOverrides;
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc.Controllers;
 | 
					using Microsoft.AspNetCore.Mvc.Controllers;
 | 
				
			||||||
using Microsoft.AspNetCore.StaticFiles;
 | 
					using Microsoft.AspNetCore.StaticFiles;
 | 
				
			||||||
@@ -18,6 +21,7 @@ using Microsoft.Extensions.Options;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
using Newtonsoft.Json;
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using System.Security.Cryptography.X509Certificates;
 | 
				
			||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
using System.Text.Encodings.Web;
 | 
					using System.Text.Encodings.Web;
 | 
				
			||||||
using System.Text.Unicode;
 | 
					using System.Text.Unicode;
 | 
				
			||||||
@@ -297,6 +301,21 @@ public class Startup : AppStartup
 | 
				
			|||||||
        services.AddAuthorizationCore();
 | 
					        services.AddAuthorizationCore();
 | 
				
			||||||
        services.AddScoped<IAuthorizationHandler, BlazorServerAuthenticationHandler>();
 | 
					        services.AddScoped<IAuthorizationHandler, BlazorServerAuthenticationHandler>();
 | 
				
			||||||
        services.AddScoped<AuthenticationStateProvider, BlazorServerAuthenticationStateProvider>();
 | 
					        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
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -76,6 +76,9 @@
 | 
				
			|||||||
		<None Update="pm2-linux.json">
 | 
							<None Update="pm2-linux.json">
 | 
				
			||||||
			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
								<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
				
			||||||
		</None>
 | 
							</None>
 | 
				
			||||||
 | 
							<None Update="ThingsGateway.pfx">
 | 
				
			||||||
 | 
							  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
				
			||||||
 | 
							</None>
 | 
				
			||||||
		<None Update="WindowsServiceDelete.bat">
 | 
							<None Update="WindowsServiceDelete.bat">
 | 
				
			||||||
			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
								<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
				
			||||||
		</None>
 | 
							</None>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								src/Upgrade/ThingsGateway.UpgradeServer/ThingsGateway.pfx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/Upgrade/ThingsGateway.UpgradeServer/ThingsGateway.pfx
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -1,5 +1,5 @@
 | 
				
			|||||||
cd ..
 | 
					cd ..
 | 
				
			||||||
sc create ThingsGateway binPath= %~dp0ThingsGateway.AdminServer.exe start= auto 
 | 
					sc create ThingsGateway binPath= %~dp0ThingsGateway.UpgradeServer.exe start= auto 
 | 
				
			||||||
sc description ThingsGateway "ThingsGateway"
 | 
					sc description ThingsGateway.UpgradeServer "ThingsGateway.UpgradeServer"
 | 
				
			||||||
Net Start ThingsGateway
 | 
					Net Start ThingsGateway.UpgradeServer
 | 
				
			||||||
pause
 | 
					pause
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,3 @@
 | 
				
			|||||||
net stop ThingsGateway
 | 
					net stop ThingsGateway.UpgradeServer
 | 
				
			||||||
sc delete ThingsGateway
 | 
					sc delete ThingsGateway.UpgradeServer
 | 
				
			||||||
pause
 | 
					pause
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,7 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "urls": "http://*:5500",
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  "ConfigurationScanDirectories": [ "Configuration", "" ], // 扫描配置文件json文件夹(自动合并该文件夹里面所有json文件)
 | 
					  "ConfigurationScanDirectories": [ "Configuration", "" ], // 扫描配置文件json文件夹(自动合并该文件夹里面所有json文件)
 | 
				
			||||||
  "IgnoreConfigurationFiles": [ "" ],
 | 
					  "IgnoreConfigurationFiles": [ "" ],
 | 
				
			||||||
  "ExternalAssemblies": [ "" ]
 | 
					  "ExternalAssemblies": [ "" ],
 | 
				
			||||||
 | 
					  "DetailedErrors": true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  "ConfigurationScanDirectories": [ "Configuration", "" ], // 扫描配置文件json文件夹(自动合并该文件夹里面所有json文件)
 | 
					  "ConfigurationScanDirectories": [ "Configuration", "" ], // 扫描配置文件json文件夹(自动合并该文件夹里面所有json文件)
 | 
				
			||||||
  "IgnoreConfigurationFiles": [ "" ],
 | 
					  "IgnoreConfigurationFiles": [ "" ],
 | 
				
			||||||
  "ExternalAssemblies": [ "" ]
 | 
					  "ExternalAssemblies": [ "" ],
 | 
				
			||||||
 | 
					  "DetailedErrors": true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,22 +0,0 @@
 | 
				
			|||||||
{
 | 
					 | 
				
			||||||
  "apps": {
 | 
					 | 
				
			||||||
    "name": "ThingsGateway",
 | 
					 | 
				
			||||||
    "script": "dotnet",
 | 
					 | 
				
			||||||
    "exec_mode": "fork",
 | 
					 | 
				
			||||||
    "error_file": "logs/pm2err.log",
 | 
					 | 
				
			||||||
    "out_file": "logs/pm2out.log",
 | 
					 | 
				
			||||||
    "merge_logs": true,
 | 
					 | 
				
			||||||
    "log_date_format": "YYYY-MM-DD HH:mm:ss",
 | 
					 | 
				
			||||||
    "min_uptime": "60s",
 | 
					 | 
				
			||||||
    "max_restarts": 30,
 | 
					 | 
				
			||||||
    "autorestart": true,
 | 
					 | 
				
			||||||
    "restart_delay": "60",
 | 
					 | 
				
			||||||
    "args": [
 | 
					 | 
				
			||||||
      "ThingsGateway.UpgradeServer.dll",
 | 
					 | 
				
			||||||
      " --urls=http://*:5000"
 | 
					 | 
				
			||||||
    ],
 | 
					 | 
				
			||||||
    "env": {
 | 
					 | 
				
			||||||
      "ASPNETCORE_ENVIRONMENT": "Production"
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "configProperties": {
 | 
				
			||||||
 | 
					    "System.Runtime.EnableWriteXorExecute": false
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
<Project>
 | 
					<Project>
 | 
				
			||||||
  <PropertyGroup>
 | 
					  <PropertyGroup>
 | 
				
			||||||
    <Version>10.5.9</Version>
 | 
					    <Version>10.5.18</Version>
 | 
				
			||||||
  </PropertyGroup>
 | 
					  </PropertyGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user