mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-28 22:23:59 +08:00
Compare commits
12 Commits
10.12.7.0
...
10.12.14.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a464594885 | ||
|
|
b42f8afa35 | ||
|
|
facf8bd401 | ||
|
|
feeb17eca3 | ||
|
|
b3405cd674 | ||
|
|
c35f9cef93 | ||
|
|
3f382202db | ||
|
|
2a3493cc82 | ||
|
|
aaa459ebe0 | ||
|
|
51a8acbc3e | ||
|
|
dc132a1999 | ||
|
|
0c31cfcbc5 |
@@ -11,8 +11,6 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime;
|
||||
|
||||
using ThingsGateway.NewLife;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -22,7 +20,7 @@ public class HardwareInfo
|
||||
/// 当前磁盘信息
|
||||
/// </summary>
|
||||
public DriveInfo DriveInfo { get; set; }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 主机环境
|
||||
@@ -151,6 +149,6 @@ public class HardwareInfo
|
||||
/// 更新时间
|
||||
/// </summary>
|
||||
public DateTime UpdateTime { get; set; }
|
||||
public ulong AppRunTotalMinute { get; set; }
|
||||
public ulong SystemRunTotalMinute { get; set; }
|
||||
public ulong AppRunTotalMinute { get; set; }
|
||||
public ulong SystemRunTotalMinute { get; set; }
|
||||
}
|
||||
|
||||
@@ -74,10 +74,10 @@ public class HardwareJob : IJob, IHardwareJob
|
||||
{
|
||||
try
|
||||
{
|
||||
var machine = MachineInfo.GetCurrent();
|
||||
var machine = MachineInfo.GetCurrent();
|
||||
if (HardwareInfo == null)
|
||||
{
|
||||
HardwareInfo=machine.AdaptHardwareInfo();
|
||||
HardwareInfo = machine.AdaptHardwareInfo();
|
||||
|
||||
string currentPath = Directory.GetCurrentDirectory();
|
||||
DriveInfo drive = new(Path.GetPathRoot(currentPath));
|
||||
@@ -100,8 +100,8 @@ public class HardwareJob : IJob, IHardwareJob
|
||||
var machine = MachineInfo.GetCurrent();
|
||||
machine.Refresh();
|
||||
machine.AdaptHardwareInfo(HardwareInfo);
|
||||
HardwareInfo.AppRunTotalMinute = (ulong)Runtime.AppTickCount64 / 1000 /60;
|
||||
HardwareInfo.SystemRunTotalMinute = (ulong)Runtime.TickCount64 / 1000 /60;
|
||||
HardwareInfo.AppRunTotalMinute = (ulong)Runtime.AppTickCount64 / 1000 / 60;
|
||||
HardwareInfo.SystemRunTotalMinute = (ulong)Runtime.TickCount64 / 1000 / 60;
|
||||
HardwareInfo.UpdateTime = TimerX.Now;
|
||||
error = false;
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using Riok.Mapperly.Abstractions;
|
||||
|
||||
using ThingsGateway.NewLife;
|
||||
@@ -20,7 +18,7 @@ namespace ThingsGateway.Admin.Application;
|
||||
public static partial class AdminMapper
|
||||
{
|
||||
public static partial HardwareInfo AdaptHardwareInfo(this MachineInfo src);
|
||||
public static partial void AdaptHardwareInfo(this MachineInfo src, HardwareInfo dto);
|
||||
public static partial void AdaptHardwareInfo(this MachineInfo src, HardwareInfo dto);
|
||||
|
||||
public static partial LoginInput AdaptLoginInput(this OpenApiLoginInput src);
|
||||
public static partial OpenApiLoginOutput AdaptOpenApiLoginOutput(this LoginOutput src);
|
||||
|
||||
@@ -87,45 +87,45 @@ public class Startup : AppStartup
|
||||
#if NET8_0_OR_GREATER
|
||||
services
|
||||
.AddRazorComponents(options => options.TemporaryRedirectionUrlValidityDuration = TimeSpan.FromMinutes(10))
|
||||
.AddInteractiveServerComponents(options =>
|
||||
{
|
||||
options.RootComponents.MaxJSRootComponents = 500;
|
||||
options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2);
|
||||
options.MaxBufferedUnacknowledgedRenderBatches = 20;
|
||||
options.DisconnectedCircuitMaxRetained = 1;
|
||||
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
|
||||
})
|
||||
.AddHubOptions(options =>
|
||||
{
|
||||
//单个传入集线器消息的最大大小。默认 32 KB
|
||||
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
|
||||
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
||||
options.StreamBufferCapacity = 30;
|
||||
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
|
||||
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
|
||||
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
|
||||
|
||||
});
|
||||
.AddInteractiveServerComponents(options =>
|
||||
{
|
||||
options.RootComponents.MaxJSRootComponents = 500;
|
||||
options.JSInteropDefaultCallTimeout = TimeSpan.FromSeconds(30);
|
||||
options.MaxBufferedUnacknowledgedRenderBatches = 5;
|
||||
options.DisconnectedCircuitMaxRetained = 1;
|
||||
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
|
||||
})
|
||||
.AddHubOptions(options =>
|
||||
{
|
||||
//单个传入集线器消息的最大大小。默认 32 KB
|
||||
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
|
||||
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
||||
options.StreamBufferCapacity = 30;
|
||||
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
|
||||
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
|
||||
options.HandshakeTimeout = TimeSpan.FromSeconds(15);
|
||||
});
|
||||
|
||||
#else
|
||||
|
||||
services.AddServerSideBlazor(options =>
|
||||
{
|
||||
options.RootComponents.MaxJSRootComponents = 500;
|
||||
options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2);
|
||||
options.MaxBufferedUnacknowledgedRenderBatches = 20;
|
||||
options.DisconnectedCircuitMaxRetained = 1;
|
||||
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
|
||||
}).AddHubOptions(options =>
|
||||
{
|
||||
//单个传入集线器消息的最大大小。默认 32 KB
|
||||
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
|
||||
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
||||
options.StreamBufferCapacity = 30;
|
||||
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
|
||||
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
|
||||
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
|
||||
});
|
||||
{
|
||||
options.RootComponents.MaxJSRootComponents = 500;
|
||||
options.JSInteropDefaultCallTimeout = TimeSpan.FromSeconds(30);
|
||||
options.MaxBufferedUnacknowledgedRenderBatches = 5;
|
||||
options.DisconnectedCircuitMaxRetained = 1;
|
||||
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
|
||||
})
|
||||
.AddHubOptions(options =>
|
||||
{
|
||||
//单个传入集线器消息的最大大小。默认 32 KB
|
||||
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
|
||||
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
||||
options.StreamBufferCapacity = 30;
|
||||
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
|
||||
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
|
||||
options.HandshakeTimeout = TimeSpan.FromSeconds(15);
|
||||
});
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ using System.ComponentModel.DataAnnotations;
|
||||
|
||||
using ThingsGateway.Common.Extension;
|
||||
|
||||
|
||||
#if NET8_0_OR_GREATER
|
||||
using System.Collections.Frozen;
|
||||
#endif
|
||||
@@ -255,8 +256,15 @@ internal class CacheManager
|
||||
/// </summary>
|
||||
/// <param name="assembly">Assembly 程序集实例</param>
|
||||
/// <param name="typeName">类型名称</param>
|
||||
public static IEnumerable<LocalizedString>? GetAllStringsByTypeName(Assembly assembly, string typeName)
|
||||
public static FrozenSet<LocalizedString>? GetAllStringsByTypeName(Assembly assembly, string typeName)
|
||||
=> GetJsonStringByTypeName(GetJsonLocalizationOption(), assembly, typeName, CultureInfo.CurrentUICulture.Name);
|
||||
/// <summary>
|
||||
/// 获取指定文化本地化资源集合
|
||||
/// </summary>
|
||||
/// <param name="assembly">Assembly 程序集实例</param>
|
||||
/// <param name="typeName">类型名称</param>
|
||||
public static FrozenDictionary<string, string>? GetAllHasValueStringsByTypeName(Assembly assembly, string typeName)
|
||||
=> GetHasValueJsonStringByTypeName(GetJsonLocalizationOption(), assembly, typeName, CultureInfo.CurrentUICulture.Name);
|
||||
|
||||
/// <summary>
|
||||
/// 通过指定程序集获取所有本地化信息键值集合
|
||||
@@ -267,7 +275,7 @@ internal class CacheManager
|
||||
/// <param name="cultureName">cultureName 未空时使用 CultureInfo.CurrentUICulture.Name</param>
|
||||
/// <param name="forceLoad">默认 false 使用缓存值 设置 true 时内部强制重新加载</param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<LocalizedString>? GetJsonStringByTypeName(JsonLocalizationOptions option, Assembly assembly, string typeName, string? cultureName = null, bool forceLoad = false)
|
||||
public static FrozenSet<LocalizedString>? GetJsonStringByTypeName(JsonLocalizationOptions option, Assembly assembly, string typeName, string? cultureName = null, bool forceLoad = false)
|
||||
{
|
||||
if (assembly.IsDynamic)
|
||||
{
|
||||
@@ -277,13 +285,15 @@ internal class CacheManager
|
||||
cultureName ??= CultureInfo.CurrentUICulture.Name;
|
||||
if (string.IsNullOrEmpty(cultureName))
|
||||
{
|
||||
return [];
|
||||
return null;
|
||||
}
|
||||
|
||||
var key = $"{CacheKeyPrefix}-{nameof(GetJsonStringByTypeName)}-{assembly.GetUniqueName()}-{cultureName}";
|
||||
var typeKey = $"{CacheKeyPrefix}-{nameof(GetJsonStringByTypeName)}-{assembly.GetUniqueName()}-{typeName}-{cultureName}";
|
||||
if (forceLoad)
|
||||
{
|
||||
Instance.Cache.Remove(key);
|
||||
Instance.Cache.Remove(typeKey);
|
||||
}
|
||||
|
||||
var localizedItems = Instance.GetOrCreate(key, entry =>
|
||||
@@ -304,16 +314,77 @@ internal class CacheManager
|
||||
return items.ToHashSet();
|
||||
#endif
|
||||
});
|
||||
return localizedItems.Where(item => item.SearchedLocation!.Equals(typeName, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
var typeLocalizedItems = Instance.GetOrCreate(typeKey, entry =>
|
||||
{
|
||||
return localizedItems.Where(item => item.SearchedLocation!.Equals(typeName, StringComparison.OrdinalIgnoreCase)).ToFrozenSet();
|
||||
});
|
||||
return typeLocalizedItems;
|
||||
}
|
||||
/// <summary>
|
||||
/// 通过 ILocalizationResolve 接口实现类获得本地化键值集合
|
||||
/// 通过指定程序集获取所有本地化信息键值集合
|
||||
/// </summary>
|
||||
/// <param name="typeName"></param>
|
||||
/// <param name="includeParentCultures"></param>
|
||||
/// <param name="option">JsonLocalizationOptions 实例</param>
|
||||
/// <param name="assembly">Assembly 程序集实例</param>
|
||||
/// <param name="typeName">类型名称</param>
|
||||
/// <param name="cultureName">cultureName 未空时使用 CultureInfo.CurrentUICulture.Name</param>
|
||||
/// <param name="forceLoad">默认 false 使用缓存值 设置 true 时内部强制重新加载</param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<LocalizedString> GetTypeStringsFromResolve(string typeName, bool includeParentCultures = true) => Provider.GetRequiredService<ILocalizationResolve>().GetAllStringsByType(typeName, includeParentCultures);
|
||||
public static FrozenDictionary<string, string>? GetHasValueJsonStringByTypeName(JsonLocalizationOptions option, Assembly assembly, string typeName, string? cultureName = null, bool forceLoad = false)
|
||||
{
|
||||
if (assembly.IsDynamic)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
cultureName ??= CultureInfo.CurrentUICulture.Name;
|
||||
if (string.IsNullOrEmpty(cultureName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var key = $"{CacheKeyPrefix}-{nameof(GetJsonStringByTypeName)}-{assembly.GetUniqueName()}-{cultureName}";
|
||||
|
||||
|
||||
var typeKey = $"{CacheKeyPrefix}-{nameof(GetHasValueJsonStringByTypeName)}-{assembly.GetUniqueName()}-{typeName}-{cultureName}";
|
||||
if (forceLoad)
|
||||
{
|
||||
Instance.Cache.Remove(key);
|
||||
Instance.Cache.Remove(typeKey);
|
||||
}
|
||||
|
||||
var localizedItems = Instance.GetOrCreate(key, entry =>
|
||||
{
|
||||
var sections = option.GetJsonStringFromAssembly(assembly, cultureName);
|
||||
var items = sections.SelectMany(section => section.GetChildren().Select(kv =>
|
||||
{
|
||||
var value = kv.Value;
|
||||
if (value == null && option.UseKeyWhenValueIsNull == true)
|
||||
{
|
||||
value = kv.Key;
|
||||
}
|
||||
return new LocalizedString(kv.Key, value ?? "", false, section.Key);
|
||||
}));
|
||||
#if NET8_0_OR_GREATER
|
||||
return items.ToFrozenSet();
|
||||
#else
|
||||
return items.ToHashSet();
|
||||
#endif
|
||||
});
|
||||
|
||||
var typeLocalizedItems = Instance.GetOrCreate(typeKey, entry =>
|
||||
{
|
||||
return localizedItems.Where(item => item.SearchedLocation!.Equals(typeName, StringComparison.OrdinalIgnoreCase) && !item.ResourceNotFound).ToFrozenDictionary(a => a.Name, a => a.Value);
|
||||
});
|
||||
return typeLocalizedItems;
|
||||
}
|
||||
///// <summary>
|
||||
///// 通过 ILocalizationResolve 接口实现类获得本地化键值集合
|
||||
///// </summary>
|
||||
///// <param name="typeName"></param>
|
||||
///// <param name="includeParentCultures"></param>
|
||||
///// <returns></returns>
|
||||
//public static IEnumerable<LocalizedString> GetTypeStringsFromResolve(string typeName, bool includeParentCultures = true) => Provider.GetRequiredService<ILocalizationResolve>().GetAllStringsByType(typeName, includeParentCultures);
|
||||
#endregion
|
||||
|
||||
#region DisplayName
|
||||
|
||||
@@ -81,50 +81,16 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba
|
||||
/// <returns></returns>
|
||||
private string? GetStringSafely(string name) => GetStringFromJson(name);
|
||||
|
||||
private string? GetStringFromService(string name)
|
||||
{
|
||||
// get string from inject service
|
||||
string? ret = null;
|
||||
if (jsonLocalizationOptions.DisableGetLocalizerFromService == false)
|
||||
{
|
||||
var localizer = Utility.GetStringLocalizerFromService(Assembly, typeName);
|
||||
if (localizer != null && localizer is not JsonStringLocalizer)
|
||||
{
|
||||
var l = localizer[name];
|
||||
if (!l.ResourceNotFound)
|
||||
{
|
||||
ret = l.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private string? GetStringFromResourceManager(string name)
|
||||
{
|
||||
string? ret = null;
|
||||
if (jsonLocalizationOptions.DisableGetLocalizerFromResourceManager == false)
|
||||
{
|
||||
ret = GetStringSafely(name, CultureInfo.CurrentUICulture);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private readonly ConcurrentHashSet<string> _missingManifestCache = [];
|
||||
private string? GetStringFromJson(string name)
|
||||
{
|
||||
// get string from json localization file
|
||||
var localizerStrings = MegerResolveLocalizers(CacheManager.GetAllStringsByTypeName(Assembly, typeName));
|
||||
var cacheKey = $"name={name}&culture={CultureInfo.CurrentUICulture.Name}";
|
||||
string? ret = null;
|
||||
if (!_missingManifestCache.Contain(cacheKey))
|
||||
{
|
||||
var l = localizerStrings.Find(i => i.Name == name);
|
||||
if (l is { ResourceNotFound: false })
|
||||
{
|
||||
ret = l.Value;
|
||||
}
|
||||
else
|
||||
var localizerStrings = CacheManager.GetAllHasValueStringsByTypeName(Assembly, typeName);
|
||||
if (localizerStrings?.TryGetValue(name, out ret) != true)
|
||||
{
|
||||
// 如果没有找到资源信息则尝试从父类中查找
|
||||
ret ??= GetStringFromBaseType(name);
|
||||
@@ -150,28 +116,13 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba
|
||||
if (baseType != type)
|
||||
{
|
||||
var baseAssembly = baseType.Assembly;
|
||||
var localizerStrings = MegerResolveLocalizers(CacheManager.GetAllStringsByTypeName(baseAssembly, baseType.FullName!));
|
||||
var l = localizerStrings.Find(i => i.Name == name);
|
||||
if (l is { ResourceNotFound: false })
|
||||
{
|
||||
ret = l.Value;
|
||||
}
|
||||
var localizerStrings = CacheManager.GetAllHasValueStringsByTypeName(baseAssembly, baseType.FullName!);
|
||||
_ = localizerStrings?.TryGetValue(name, out ret);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private List<LocalizedString> MegerResolveLocalizers(IEnumerable<LocalizedString>? localizerStrings)
|
||||
{
|
||||
var localizers = new List<LocalizedString>(CacheManager.GetTypeStringsFromResolve(typeName));
|
||||
|
||||
if (localizerStrings != null)
|
||||
{
|
||||
localizers.AddRange(localizerStrings);
|
||||
}
|
||||
return localizers;
|
||||
}
|
||||
|
||||
private void HandleMissingResourceItem(string name)
|
||||
{
|
||||
localizationMissingItemHandler.HandleMissingItem(name, typeName, CultureInfo.CurrentUICulture.Name);
|
||||
@@ -183,7 +134,7 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba
|
||||
_missingManifestCache.TryAdd($"name={name}&culture={CultureInfo.CurrentUICulture.Name}");
|
||||
}
|
||||
|
||||
private List<LocalizedString>? _allLocalizerdStrings;
|
||||
private LocalizedString[]? _allLocalizerdStrings;
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前语言的所有资源信息
|
||||
@@ -198,7 +149,7 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba
|
||||
?? GetAllStringsFromBase()
|
||||
?? GetAllStringsFromJson();
|
||||
|
||||
_allLocalizerdStrings = MegerResolveLocalizers(items);
|
||||
_allLocalizerdStrings = items.ToArray();
|
||||
}
|
||||
return _allLocalizerdStrings;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace ThingsGateway.NewLife.Collections;
|
||||
/// 文档 https://newlifex.com/core/object_pool
|
||||
/// </remarks>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
|
||||
public class ObjectPoolLock<T> : DisposeBase where T : class
|
||||
{
|
||||
#region 属性
|
||||
/// <summary>名称</summary>
|
||||
@@ -22,11 +22,6 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
|
||||
/// <summary>繁忙个数</summary>
|
||||
public Int32 BusyCount => _BusyCount;
|
||||
|
||||
/// <summary>最大个数。默认0,0表示无上限</summary>
|
||||
public Int32 Max { get; set; } = 0;
|
||||
|
||||
private readonly object _syncRoot = new();
|
||||
|
||||
/// <summary>基础空闲集合。只保存最小个数,最热部分</summary>
|
||||
private readonly Stack<T> _free = new();
|
||||
|
||||
@@ -73,7 +68,7 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
|
||||
if (_inited) return;
|
||||
_inited = true;
|
||||
|
||||
WriteLog($"Init {typeof(T).FullName} Max={Max}");
|
||||
WriteLog($"Init {typeof(T).FullName}");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
@@ -86,7 +81,7 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
|
||||
T? pi = null;
|
||||
do
|
||||
{
|
||||
lock (_syncRoot)
|
||||
lock (lockThis)
|
||||
{
|
||||
if (_free.Count > 0)
|
||||
{
|
||||
@@ -95,13 +90,6 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Max > 0 && BusyCount >= Max)
|
||||
{
|
||||
var msg = $"申请失败,已有 {BusyCount:n0} 达到或超过最大值 {Max:n0}";
|
||||
WriteLog("Acquire Max " + msg);
|
||||
throw new Exception(Name + " " + msg);
|
||||
}
|
||||
|
||||
pi = OnCreate();
|
||||
if (BusyCount == 0) Init();
|
||||
|
||||
@@ -114,7 +102,7 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
|
||||
// 如果拿到的对象不可用,则重新借
|
||||
} while (pi == null || !OnGet(pi));
|
||||
|
||||
lock (_syncRoot)
|
||||
lock (lockThis)
|
||||
{
|
||||
// 加入繁忙集合
|
||||
_busy.Add(pi);
|
||||
@@ -129,16 +117,12 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
|
||||
/// <returns></returns>
|
||||
protected virtual Boolean OnGet(T value) => true;
|
||||
|
||||
/// <summary>申请资源包装项,Dispose时自动归还到池中</summary>
|
||||
/// <returns></returns>
|
||||
public PoolItem<T> GetItem() => new(this, Get());
|
||||
|
||||
/// <summary>归还</summary>
|
||||
/// <param name="value"></param>
|
||||
public virtual Boolean Return(T value)
|
||||
{
|
||||
if (value == null) return false;
|
||||
lock (_syncRoot)
|
||||
lock (lockThis)
|
||||
{
|
||||
// 从繁忙队列找到并移除缓存项
|
||||
if (!_busy.Remove(value))
|
||||
@@ -163,7 +147,7 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
|
||||
{
|
||||
return false;
|
||||
}
|
||||
lock (_syncRoot)
|
||||
lock (lockThis)
|
||||
{
|
||||
_free.Push(value);
|
||||
_FreeCount++;
|
||||
@@ -180,18 +164,9 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
|
||||
/// <summary>清空已有对象</summary>
|
||||
public virtual Int32 Clear()
|
||||
{
|
||||
var count = _FreeCount + _BusyCount;
|
||||
|
||||
//_busy.Clear();
|
||||
//_BusyCount = 0;
|
||||
|
||||
//_free.Clear();
|
||||
//while (_free2.TryDequeue(out var rs)) ;
|
||||
//_FreeCount = 0;
|
||||
|
||||
lock (_syncRoot)
|
||||
lock (lockThis)
|
||||
{
|
||||
count = _FreeCount + _BusyCount;
|
||||
var count = _FreeCount + _BusyCount;
|
||||
|
||||
while (_free.Count > 0)
|
||||
{
|
||||
@@ -207,9 +182,9 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
|
||||
}
|
||||
_busy.Clear();
|
||||
_BusyCount = 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>销毁</summary>
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Reflection;
|
||||
using System.Runtime;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Security;
|
||||
|
||||
using ThingsGateway.NewLife.Collections;
|
||||
using ThingsGateway.NewLife.Data;
|
||||
using ThingsGateway.NewLife.Log;
|
||||
using ThingsGateway.NewLife.Model;
|
||||
using ThingsGateway.NewLife.Reflection;
|
||||
using ThingsGateway.NewLife.Serialization;
|
||||
using ThingsGateway.NewLife.Windows;
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime;
|
||||
|
||||
|
||||
#if NETFRAMEWORK
|
||||
using System.Management;
|
||||
@@ -682,7 +680,7 @@ public class MachineInfo
|
||||
HeapSize = (ulong)(info.HeapSizeBytes / 1024 / 1024);
|
||||
TotalMemory = (ulong)(GC.GetTotalMemory(false) / 1024 / 1024);
|
||||
FragmentedBytes = (ulong)(info.FragmentedBytes / 1024 / 1024);
|
||||
GCAvailableMemory = (ulong)(info.TotalAvailableMemoryBytes - info.MemoryLoadBytes) / 1024 / 1024;
|
||||
GCAvailableMemory = (ulong)Math.Max(0, (info.TotalAvailableMemoryBytes - info.MemoryLoadBytes) / 1024 / 1024);
|
||||
CommittedBytes = (ulong)(info.TotalCommittedBytes / 1024 / 1024);
|
||||
TotalAllocatedBytes = (ulong)(GC.GetTotalAllocatedBytes(false) / 1024 / 1024);
|
||||
#if NET8_0_OR_GREATER
|
||||
|
||||
@@ -184,7 +184,7 @@ public static class Runtime
|
||||
public static Int64 AppStartTick = TickCount64;
|
||||
|
||||
/// <summary>软件启动以来的毫秒数</summary>
|
||||
public static Int64 AppTickCount64 => TickCount64-AppStartTick;
|
||||
public static Int64 AppTickCount64 => TickCount64 - AppStartTick;
|
||||
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
/// <summary>系统启动以来的毫秒数</summary>
|
||||
@@ -256,7 +256,7 @@ public static class Runtime
|
||||
|
||||
return dic;
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region 设置
|
||||
private static Boolean? _createConfigOnMissing;
|
||||
|
||||
@@ -694,8 +694,12 @@ namespace ThingsGateway.SqlSugar
|
||||
var enumerator = table.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
var cur = enumerator.Current;
|
||||
yield return cur.Value.Item2[rowIndex];
|
||||
var kvp = enumerator.Current;
|
||||
var list = kvp.Value.Item2;
|
||||
if (list != null && rowIndex < list.Count)
|
||||
yield return list[rowIndex];
|
||||
else
|
||||
yield return new DataInfos { ColumnName = kvp.Key, Value = DBNull.Value };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -201,6 +201,7 @@ namespace ThingsGateway.SqlSugar
|
||||
{
|
||||
foreach (var column in columns)
|
||||
{
|
||||
|
||||
if (column.IsIgnore)
|
||||
{
|
||||
continue;
|
||||
@@ -210,6 +211,12 @@ namespace ThingsGateway.SqlSugar
|
||||
{
|
||||
name = column.PropertyName;
|
||||
}
|
||||
if (!results.TryGetValue(name, out var tuple) || tuple.Item2 == null)
|
||||
{
|
||||
// 某些列可能不在 DataTable 中(例如数据库多了列)
|
||||
continue;
|
||||
}
|
||||
|
||||
var value = ValueConverter(column, GetValue(item, column));
|
||||
if (column.SqlParameterDbType != null && column.SqlParameterDbType is Type && UtilMethods.HasInterface((Type)column.SqlParameterDbType, typeof(ISugarDataConverter)))
|
||||
{
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<PluginVersion>10.12.7</PluginVersion>
|
||||
<ProPluginVersion>10.12.7</ProPluginVersion>
|
||||
<DefaultVersion>10.12.7</DefaultVersion>
|
||||
<AuthenticationVersion>10.11.6</AuthenticationVersion>
|
||||
<SourceGeneratorVersion>10.11.6</SourceGeneratorVersion>
|
||||
<PluginVersion>10.12.13</PluginVersion>
|
||||
<ProPluginVersion>10.12.13</ProPluginVersion>
|
||||
<DefaultVersion>10.12.13</DefaultVersion>
|
||||
<AuthenticationVersion>10.11.7</AuthenticationVersion>
|
||||
<SourceGeneratorVersion>10.11.7</SourceGeneratorVersion>
|
||||
<NET8Version>8.0.21</NET8Version>
|
||||
<NET10Version>10.0.0-rc.2.25502.107</NET10Version>
|
||||
<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
|
||||
<IsTrimmable>false</IsTrimmable>
|
||||
<ManagementProPluginVersion>10.11.87</ManagementProPluginVersion>
|
||||
<ManagementPluginVersion>10.11.87</ManagementPluginVersion>
|
||||
<TSVersion>4.0.0-beta.140</TSVersion>
|
||||
<TSVersion>4.0.0-rc.5</TSVersion>
|
||||
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -30,7 +30,6 @@ public interface IClientChannel : IChannel, ISender, IClient, IClientSender, IOn
|
||||
WaitHandlePool<MessageBase> WaitHandlePool { get; }
|
||||
|
||||
WaitLock GetLock(string key);
|
||||
void LogSeted(bool logSeted);
|
||||
|
||||
/// <summary>
|
||||
/// 设置数据处理适配器
|
||||
|
||||
@@ -80,13 +80,11 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
||||
|
||||
|
||||
|
||||
private bool logSet;
|
||||
/// <inheritdoc/>
|
||||
public void SetDataHandlingAdapterLogger(ILog log)
|
||||
{
|
||||
if (!logSet && ReadOnlyDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
if (ReadOnlyDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
{
|
||||
logSet = true;
|
||||
handleAdapter.Logger = log;
|
||||
}
|
||||
}
|
||||
@@ -96,12 +94,8 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
||||
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
||||
SetAdapter(singleStreamDataHandlingAdapter);
|
||||
|
||||
logSet = false;
|
||||
}
|
||||
public void LogSeted(bool logSeted)
|
||||
{
|
||||
logSet = logSeted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置数据处理适配器。
|
||||
/// </summary>
|
||||
|
||||
@@ -97,13 +97,15 @@ public static class PluginUtil
|
||||
{
|
||||
action += a =>
|
||||
{
|
||||
a.UseTcpSessionCheckClear()
|
||||
.SetCheckClearType(CheckClearType.All)
|
||||
.SetTick(TimeSpan.FromMilliseconds(channelOptions.CheckClearTime))
|
||||
.SetOnClose((c, t) =>
|
||||
{
|
||||
return c.CloseAsync($"{channelOptions.CheckClearTime}ms Timeout");
|
||||
});
|
||||
a.UseTcpSessionCheckClear(options =>
|
||||
{
|
||||
options.CheckClearType = CheckClearType.All;
|
||||
options.Tick = TimeSpan.FromMilliseconds(channelOptions.CheckClearTime);
|
||||
options.OnClose = (c, t) =>
|
||||
{
|
||||
return c.CloseAsync($"{channelOptions.CheckClearTime}ms Timeout");
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
return action;
|
||||
|
||||
@@ -52,13 +52,11 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
||||
/// <inheritdoc/>
|
||||
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => ProtectedDataHandlingAdapter;
|
||||
|
||||
private bool logSet;
|
||||
/// <inheritdoc/>
|
||||
public void SetDataHandlingAdapterLogger(ILog log)
|
||||
{
|
||||
if (!logSet && ProtectedDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
if (ProtectedDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
{
|
||||
logSet = true;
|
||||
handleAdapter.Logger = log;
|
||||
}
|
||||
}
|
||||
@@ -68,13 +66,9 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
||||
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
||||
SetAdapter(singleStreamDataHandlingAdapter);
|
||||
|
||||
logSet = false;
|
||||
}
|
||||
|
||||
public void LogSeted(bool logSeted)
|
||||
{
|
||||
logSet = logSeted;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -34,12 +34,10 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
||||
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
||||
pool?.CancelAll();
|
||||
}
|
||||
private bool logSet;
|
||||
public void SetDataHandlingAdapterLogger(ILog log)
|
||||
{
|
||||
if (!logSet && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
if (DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
{
|
||||
logSet = true;
|
||||
handleAdapter.Logger = log;
|
||||
}
|
||||
}
|
||||
@@ -49,12 +47,8 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
||||
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
||||
SetAdapter(singleStreamDataHandlingAdapter);
|
||||
|
||||
logSet = false;
|
||||
}
|
||||
public void LogSeted(bool logSeted)
|
||||
{
|
||||
logSet = logSeted;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
|
||||
|
||||
|
||||
@@ -25,13 +25,11 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
|
||||
public TcpSessionClientChannel()
|
||||
{
|
||||
}
|
||||
private bool logSet;
|
||||
/// <inheritdoc/>
|
||||
public void SetDataHandlingAdapterLogger(ILog log)
|
||||
{
|
||||
if (!logSet && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
if (DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
{
|
||||
logSet = true;
|
||||
handleAdapter.Logger = log;
|
||||
}
|
||||
}
|
||||
@@ -41,12 +39,8 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
|
||||
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
||||
SetAdapter(singleStreamDataHandlingAdapter);
|
||||
|
||||
logSet = false;
|
||||
}
|
||||
public void LogSeted(bool logSeted)
|
||||
{
|
||||
logSet = logSeted;
|
||||
}
|
||||
|
||||
public void ResetSign(int minSign = 1, int maxSign = ushort.MaxValue - 1)
|
||||
{
|
||||
var pool = WaitHandlePool;
|
||||
|
||||
@@ -30,27 +30,21 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
||||
ResetSign();
|
||||
}
|
||||
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
||||
private bool logSet;
|
||||
/// <inheritdoc/>
|
||||
public void SetDataHandlingAdapterLogger(ILog log)
|
||||
{
|
||||
if (!logSet && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
if (DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
{
|
||||
logSet = true;
|
||||
handleAdapter.Logger = log;
|
||||
}
|
||||
}
|
||||
public void LogSeted(bool logSeted)
|
||||
{
|
||||
logSet = logSeted;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void SetDataHandlingAdapter(DataHandlingAdapter adapter)
|
||||
{
|
||||
if (adapter is UdpDataHandlingAdapter udpDataHandlingAdapter)
|
||||
SetAdapter(udpDataHandlingAdapter);
|
||||
|
||||
logSet = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1060,10 +1060,6 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
||||
}
|
||||
|
||||
Channel.Collects.Remove(this);
|
||||
if (Channel is IClientChannel clientChannel)
|
||||
{
|
||||
clientChannel.LogSeted(false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1118,10 +1114,6 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
||||
|
||||
Channel.Collects.Remove(this);
|
||||
|
||||
if (Channel is IClientChannel clientChannel)
|
||||
{
|
||||
clientChannel.LogSeted(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -59,14 +59,15 @@ public class CronScheduledTask : DisposeBase, IScheduledTask
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private static volatile int NextId = 0;
|
||||
public void Start()
|
||||
{
|
||||
_timer?.Dispose();
|
||||
if (Check()) return;
|
||||
if (_taskAction != null)
|
||||
_timer = new TimerX(TimerCallback, _state, _interval, nameof(CronScheduledTask)) { Async = true, Reentrant = false };
|
||||
_timer = new TimerX(TimerCallback, _state, _interval, $"{nameof(CronScheduledTask)}{(Interlocked.Increment(ref NextId) / 100)}") { Async = true, Reentrant = false };
|
||||
else if (_taskFunc != null || _valueTaskFunc != null)
|
||||
_timer = new TimerX(TimerCallbackAsync, _state, _interval, nameof(CronScheduledTask)) { Async = true, Reentrant = false };
|
||||
_timer = new TimerX(TimerCallbackAsync, _state, _interval, $"{nameof(CronScheduledTask)}{(Interlocked.Increment(ref NextId) / 100)}") { Async = true, Reentrant = false };
|
||||
}
|
||||
|
||||
private ValueTask TimerCallbackAsync(object? state)
|
||||
|
||||
@@ -46,11 +46,13 @@ public class ScheduledAsyncTask : DisposeBase, IScheduledTask, IScheduledIntInte
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static volatile int NextId = 0;
|
||||
public void Start()
|
||||
{
|
||||
_timer?.Dispose();
|
||||
if (!Check())
|
||||
_timer = new TimerX(DoAsync, _state, IntervalMS, IntervalMS, nameof(ScheduledAsyncTask)) { Async = true, Reentrant = false };
|
||||
_timer = new TimerX(DoAsync, _state, IntervalMS, IntervalMS, $"{nameof(ScheduledAsyncTask)}{(Interlocked.Increment(ref NextId) / 100)}") { Async = true, Reentrant = false };
|
||||
}
|
||||
|
||||
private ValueTask DoAsync(object? state)
|
||||
|
||||
@@ -36,11 +36,12 @@ public class ScheduledSyncTask : DisposeBase, IScheduledTask, IScheduledIntInter
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private static volatile int NextId = 0;
|
||||
public void Start()
|
||||
{
|
||||
_timer?.Dispose();
|
||||
if (!Check())
|
||||
_timer = new TimerX(TimerCallback, _state, IntervalMS, IntervalMS, nameof(ScheduledSyncTask)) { Async = true, Reentrant = false };
|
||||
_timer = new TimerX(TimerCallback, _state, IntervalMS, IntervalMS, $"{nameof(ScheduledSyncTask)}{(Interlocked.Increment(ref NextId) / 100)}") { Async = true, Reentrant = false };
|
||||
}
|
||||
|
||||
private void TimerCallback(object? state)
|
||||
|
||||
@@ -430,7 +430,7 @@ public abstract partial class CollectBase : DriverBase
|
||||
|
||||
readErrorCount++;
|
||||
if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
|
||||
@this.LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] failed - {3}", @this.DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage));
|
||||
@this.LogMessage?.Trace(string.Format("{0} - Failed to collect data [{1} - {2}] - {3}", @this.DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage));
|
||||
|
||||
//if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
|
||||
// LogMessage?.Trace(string.Format("{0} - Collecting [{1} - {2}]", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length));
|
||||
@@ -450,7 +450,7 @@ public abstract partial class CollectBase : DriverBase
|
||||
{
|
||||
// 读取成功时记录日志并增加成功计数器
|
||||
if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
|
||||
@this.LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] data succeeded {3}", @this.DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.Content.Span.ToHexString(' ')));
|
||||
@this.LogMessage?.Trace(string.Format("{0} - Collected [{1} - {2}] data successfully {3}", @this.DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.Content.Span.ToHexString(' ')));
|
||||
@this.CurrentDevice.SetDeviceStatus(TimerX.Now, null);
|
||||
}
|
||||
else
|
||||
@@ -475,7 +475,7 @@ public abstract partial class CollectBase : DriverBase
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
|
||||
@this.LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] data failed - {3}", @this.DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage));
|
||||
@this.LogMessage?.Trace(string.Format("{0} - Failed to collect data [{1} - {2}] - {3}", @this.DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -566,10 +566,24 @@ public abstract partial class CollectBase : DriverBase
|
||||
{
|
||||
foreach (var item in varRead)
|
||||
{
|
||||
if (!item.Value.Equals(writeInfoLists[item].ToObject(item.Value?.GetType())))
|
||||
var cValue = writeInfoLists[item].ToObject(item.RawValue?.GetType());
|
||||
if (!item.RawValue.Equals(cValue))
|
||||
{
|
||||
// 如果写入值与读取值不同,则更新操作结果为失败
|
||||
operResults[item.Name] = new OperResult($"The write value is inconsistent with the read value, Write value: {writeInfoLists[item].ToObject(item.Value?.GetType())}, read value: {item.Value}");
|
||||
if (cValue is IComparable)
|
||||
{
|
||||
operResults[item.Name] = new OperResult($"The write value is inconsistent with the read value, Write value: {writeInfoLists[item].ToObject(item.Value?.GetType())}, read value: {item.Value}");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cValue != null)
|
||||
{
|
||||
if (item.RawValue.ToSystemTextJsonString(false) != cValue.ToSystemTextJsonString(false))
|
||||
operResults[item.Name] = new OperResult($"The write value is inconsistent with the read value, Write value: {writeInfoLists[item].ToObject(item.Value?.GetType())}, read value: {item.Value}");
|
||||
}
|
||||
else
|
||||
operResults[item.Name] = new OperResult($"The write value is inconsistent with the read value, Write value: {writeInfoLists[item].ToObject(item.Value?.GetType())}, read value: {item.Value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +113,28 @@ public partial class ManagementTask : AsyncDisposableObject
|
||||
.ConfigurePlugins(a =>
|
||||
{
|
||||
a.UseTcpSessionCheckClear();
|
||||
|
||||
|
||||
a.UseReconnection<TcpDmtpClient>(options =>
|
||||
{
|
||||
options.TryCount = -1;
|
||||
options.PollingInterval = TimeSpan.FromMilliseconds(_managementOptions.HeartbeatInterval);
|
||||
options.PrintLog = true;
|
||||
options.CheckAction = async (c, count) =>
|
||||
{
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||
if ((await c.PingAsync(cts.Token).ConfigureAwait(false)).IsSuccess)
|
||||
{
|
||||
return ConnectionCheckResult.Alive;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ConnectionCheckResult.Dead;
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
a.UseDmtpRpc(a => a.ConfigureDefaultSerializationSelector(b =>
|
||||
{
|
||||
b.UseSystemTextJson(json =>
|
||||
@@ -131,10 +153,6 @@ public partial class ManagementTask : AsyncDisposableObject
|
||||
a.Add<FilePlugin>();
|
||||
|
||||
|
||||
a.UseDmtpHeartbeat()//使用Dmtp心跳
|
||||
.SetTick(TimeSpan.FromMilliseconds(_managementOptions.HeartbeatInterval))
|
||||
.SetMaxFailCount(3);
|
||||
|
||||
a.AddDmtpCreatedChannelPlugin(async () =>
|
||||
{
|
||||
try
|
||||
@@ -194,9 +212,6 @@ public partial class ManagementTask : AsyncDisposableObject
|
||||
|
||||
a.Add<FilePlugin>();
|
||||
|
||||
a.UseDmtpHeartbeat()//使用Dmtp心跳
|
||||
.SetTick(TimeSpan.FromMilliseconds(_managementOptions.HeartbeatInterval))
|
||||
.SetMaxFailCount(3);
|
||||
});
|
||||
|
||||
await tcpDmtpService.SetupAsync(config).ConfigureAwait(false);
|
||||
|
||||
@@ -345,7 +345,25 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
|
||||
.ConfigurePlugins(a =>
|
||||
{
|
||||
a.UseTcpSessionCheckClear();
|
||||
a.UseReconnection<TcpDmtpClient>(options =>
|
||||
{
|
||||
options.TryCount = -1;
|
||||
options.PollingInterval = TimeSpan.FromMilliseconds(redundancy.HeartbeatInterval);
|
||||
options.PrintLog = true;
|
||||
options.CheckAction = async (c, count) =>
|
||||
{
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||
if ((await c.PingAsync(cts.Token).ConfigureAwait(false)).IsSuccess)
|
||||
{
|
||||
return ConnectionCheckResult.Alive;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ConnectionCheckResult.Dead;
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
a.UseDmtpRpc(a => a.ConfigureDefaultSerializationSelector(b =>
|
||||
{
|
||||
b.UseSystemTextJson(json =>
|
||||
@@ -358,9 +376,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
|
||||
json.Converters.Add(new JArraySystemTextJsonConverter());
|
||||
});
|
||||
}));
|
||||
a.UseDmtpHeartbeat()//使用Dmtp心跳
|
||||
.SetTick(TimeSpan.FromMilliseconds(redundancy.HeartbeatInterval))
|
||||
.SetMaxFailCount(redundancy.MaxErrorCount);
|
||||
|
||||
|
||||
});
|
||||
|
||||
@@ -405,9 +421,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
|
||||
json.Converters.Add(new JArraySystemTextJsonConverter());
|
||||
});
|
||||
}));
|
||||
a.UseDmtpHeartbeat()//使用Dmtp心跳
|
||||
.SetTick(TimeSpan.FromMilliseconds(redundancy.HeartbeatInterval))
|
||||
.SetMaxFailCount(redundancy.MaxErrorCount);
|
||||
|
||||
});
|
||||
|
||||
await tcpDmtpService.SetupAsync(config).ConfigureAwait(false);
|
||||
|
||||
@@ -26,7 +26,7 @@ internal static class RuntimeServiceHelper
|
||||
public static async Task InitAsync(List<ChannelRuntime> newChannelRuntimes, List<DeviceRuntime> newDeviceRuntimes, ILogger logger)
|
||||
{
|
||||
//批量修改之后,需要重新加载通道
|
||||
foreach (var newChannelRuntime in newChannelRuntimes)
|
||||
await newChannelRuntimes.ParallelForEachAsync(async (newChannelRuntime, token) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -44,7 +44,7 @@ internal static class RuntimeServiceHelper
|
||||
{
|
||||
logger.LogWarning(ex, "Init Channel");
|
||||
}
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
GlobalData.ChannelDeviceRuntimeDispatchService.Dispatch(null);
|
||||
GlobalData.VariableRuntimeDispatchService.Dispatch(null);
|
||||
}
|
||||
@@ -71,28 +71,28 @@ internal static class RuntimeServiceHelper
|
||||
|
||||
public static async Task InitAsync(List<DeviceRuntime> newDeviceRuntimes, ILogger logger)
|
||||
{
|
||||
foreach (var newDeviceRuntime in newDeviceRuntimes)
|
||||
{
|
||||
try
|
||||
await newDeviceRuntimes.ParallelForEachAsync(async (newDeviceRuntime, token) =>
|
||||
{
|
||||
if (GlobalData.IdChannels.TryGetValue(newDeviceRuntime.ChannelId, out var newChannelRuntime))
|
||||
try
|
||||
{
|
||||
newDeviceRuntime.Init(newChannelRuntime);
|
||||
if (GlobalData.IdChannels.TryGetValue(newDeviceRuntime.ChannelId, out var newChannelRuntime))
|
||||
{
|
||||
newDeviceRuntime.Init(newChannelRuntime);
|
||||
|
||||
var newVariableRuntimes = (await GlobalData.VariableService.GetAllAsync(newDeviceRuntime.Id).ConfigureAwait(false)).AdaptEnumerableVariableRuntime();
|
||||
var newVariableRuntimes = (await GlobalData.VariableService.GetAllAsync(newDeviceRuntime.Id).ConfigureAwait(false)).AdaptEnumerableVariableRuntime();
|
||||
|
||||
newVariableRuntimes.ParallelForEach(item => item.Init(newDeviceRuntime));
|
||||
newVariableRuntimes.ParallelForEach(item => item.Init(newDeviceRuntime));
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogWarning("Channel not found");
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning("Channel not found");
|
||||
logger.LogWarning(ex, "Init Device");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "Init Device");
|
||||
}
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
GlobalData.ChannelDeviceRuntimeDispatchService.Dispatch(null);
|
||||
GlobalData.VariableRuntimeDispatchService.Dispatch(null);
|
||||
@@ -234,16 +234,16 @@ internal static class RuntimeServiceHelper
|
||||
public static async Task RestartDeviceAsync(List<DeviceRuntime> newDeviceRuntimes)
|
||||
{
|
||||
var groups = GlobalData.GetDeviceThreadManages(newDeviceRuntimes);
|
||||
foreach (var group in groups)
|
||||
await groups.ParallelForEachAsync(async (group, token) =>
|
||||
{
|
||||
if (group.Key != null)
|
||||
await group.Key.RestartDeviceAsync(group.Value, false).ConfigureAwait(false);
|
||||
}
|
||||
foreach (var group in GlobalData.GetAllVariableBusinessDeviceRuntime().Where(a => !newDeviceRuntimes.Contains(a)).Where(a => a.Driver?.DeviceThreadManage != null).GroupBy(a => a.Driver.DeviceThreadManage))
|
||||
}).ConfigureAwait(false);
|
||||
await GlobalData.GetAllVariableBusinessDeviceRuntime().Where(a => !newDeviceRuntimes.Contains(a)).Where(a => a.Driver?.DeviceThreadManage != null).GroupBy(a => a.Driver.DeviceThreadManage).ParallelForEachAsync(async (group, token) =>
|
||||
{
|
||||
if (group.Key != null)
|
||||
await group.Key.RestartDeviceAsync(group.ToArray(), false).ConfigureAwait(false);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
public static async Task RemoveDeviceAsync(HashSet<long> newDeciceIds)
|
||||
{
|
||||
@@ -254,17 +254,17 @@ internal static class RuntimeServiceHelper
|
||||
public static async Task RemoveDeviceAsync(IEnumerable<DeviceRuntime> deviceRuntimes)
|
||||
{
|
||||
var groups = GlobalData.GetDeviceThreadManages(deviceRuntimes);
|
||||
foreach (var group in groups)
|
||||
{
|
||||
if (group.Key != null)
|
||||
await group.Key.RemoveDeviceAsync(group.Value.Select(a => a.Id).ToArray()).ConfigureAwait(false);
|
||||
}
|
||||
await groups.ParallelForEachAsync(async (group, token) =>
|
||||
{
|
||||
if (group.Key != null)
|
||||
await group.Key.RemoveDeviceAsync(group.Value.Select(a => a.Id).ToArray()).ConfigureAwait(false);
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
foreach (var group in GlobalData.GetAllVariableBusinessDeviceRuntime().Where(a => !deviceRuntimes.Contains(a)).Where(a => a.Driver?.DeviceThreadManage != null).GroupBy(a => a.Driver.DeviceThreadManage))
|
||||
await GlobalData.GetAllVariableBusinessDeviceRuntime().Where(a => !deviceRuntimes.Contains(a)).Where(a => a.Driver?.DeviceThreadManage != null).GroupBy(a => a.Driver.DeviceThreadManage).ParallelForEachAsync(async (group, token) =>
|
||||
{
|
||||
if (group.Key != null)
|
||||
await group.Key.RestartDeviceAsync(group.ToArray(), false).ConfigureAwait(false);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,9 @@
|
||||
<Content Include="..\ThingsGateway.Server\Layout\MainLayout.razor" Link="Layout\MainLayout.razor" />
|
||||
<Compile Include="..\ThingsGateway.Server\Layout\MainLayout.razor.cs" Link="Layout\MainLayout.razor.cs" />
|
||||
<Content Include="..\ThingsGateway.Server\Layout\MainLayout.razor.css" Link="Layout\MainLayout.razor.css" />
|
||||
<Content Include="..\ThingsGateway.Server\Layout\Gitee2025opensource.razor" Link="Layout\Gitee2025opensource.razor" />
|
||||
<Compile Include="..\ThingsGateway.Server\Layout\Gitee2025opensource.razor.cs" Link="Layout\Gitee2025opensource.razor.cs" />
|
||||
<Content Include="..\ThingsGateway.Server\Layout\Gitee2025opensource.razor.css" Link="Layout\Gitee2025opensource.razor.css" />
|
||||
<Content Include="..\ThingsGateway.Server\Layout\AccessDenied.razor" Link="Layout\AccessDenied.razor" />
|
||||
<Compile Include="..\ThingsGateway.Server\Layout\AccessDenied.razor.cs" Link="Layout\AccessDenied.razor.cs" />
|
||||
<Content Include="..\ThingsGateway.Server\Layout\Login.razor" Link="Layout\Login.razor" />
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
"CheckInterval": 1800000, //检查间隔
|
||||
"MaxChannelCount": 50, //最大通道数量
|
||||
"MaxDeviceCount": 50, //最大设备数量
|
||||
"MaxVariableCount": 10000 //最大变量数量
|
||||
"MaxVariableCount": 5000 //最大变量数量
|
||||
}
|
||||
}
|
||||
|
||||
18
src/ThingsGateway.Server/Layout/Gitee2025opensource.razor
Normal file
18
src/ThingsGateway.Server/Layout/Gitee2025opensource.razor
Normal file
@@ -0,0 +1,18 @@
|
||||
@inherits ComponentBase
|
||||
@namespace ThingsGateway.Server
|
||||
|
||||
<div class="popup-overlay">
|
||||
<div class="popup-window text-align: center; ">
|
||||
<p>
|
||||
🎉 <strong>ThingsGateway</strong> 正在参加
|
||||
<strong> Gitee 2025 最受欢迎的开源软件评选活动 </strong>,
|
||||
需要你的支持!
|
||||
</p>
|
||||
<a href="https://gitee.com/activity/2025opensource?ident=I4XWR9"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="popup-link" onclick="@OnClick">
|
||||
👉 前往投票支持
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
28
src/ThingsGateway.Server/Layout/Gitee2025opensource.razor.cs
Normal file
28
src/ThingsGateway.Server/Layout/Gitee2025opensource.razor.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#pragma warning disable CA2007 // 考虑对等待的任务调用 ConfigureAwait
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace ThingsGateway.Server;
|
||||
|
||||
public partial class Gitee2025opensource
|
||||
{
|
||||
[CascadingParameter]
|
||||
private Func<Task>? OnCloseAsync { get; set; }
|
||||
|
||||
private async Task OnClick()
|
||||
{
|
||||
if (OnCloseAsync != null)
|
||||
{
|
||||
await OnCloseAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/* 弹窗遮罩 */
|
||||
.popup-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(10, 10, 10, 0.6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
/* 弹窗主体 */
|
||||
.popup-window {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 30px 40px;
|
||||
max-width: 420px;
|
||||
box-shadow: 0 0 30px rgba(0, 0, 0, 0.3);
|
||||
text-align: center;
|
||||
animation: popup-in 0.3s ease-out;
|
||||
}
|
||||
/* 按钮与链接 */
|
||||
.popup-link {
|
||||
display: inline-block;
|
||||
margin-top: 10px;
|
||||
margin-right: 20px; /* ✅ 按钮之间留空隙 */
|
||||
margin-bottom: 20px; /* ✅ 按钮之间留空隙 */
|
||||
background-color: #e4405f;
|
||||
color: white;
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
|
||||
.popup-link:hover {
|
||||
background-color: #c8324f;
|
||||
}
|
||||
@@ -112,3 +112,4 @@
|
||||
</CascadingValue>
|
||||
</CascadingValue>
|
||||
|
||||
|
||||
|
||||
@@ -239,4 +239,33 @@ public partial class MainLayout : IDisposable
|
||||
};
|
||||
await DialogService.Show(op);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 显示投票弹窗
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task ShowGitee()
|
||||
{
|
||||
|
||||
await DialogService.Show(new DialogOption()
|
||||
{
|
||||
IsScrolling = false,
|
||||
ShowFooter = false,
|
||||
Title = "Gitee 评选活动",
|
||||
BodyTemplate = BootstrapDynamicComponent.CreateComponent<Gitee2025opensource>().Render(),
|
||||
ShowCloseButton = false,
|
||||
ShowHeaderCloseButton = false,
|
||||
Size = Size.Small,
|
||||
});
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (WebsiteOption.Value.Demo)
|
||||
{
|
||||
await ShowGitee();
|
||||
}
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public class Program
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -109,8 +109,8 @@ public class Startup : AppStartup
|
||||
.AddInteractiveServerComponents(options =>
|
||||
{
|
||||
options.RootComponents.MaxJSRootComponents = 500;
|
||||
options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2);
|
||||
options.MaxBufferedUnacknowledgedRenderBatches = 20;
|
||||
options.JSInteropDefaultCallTimeout = TimeSpan.FromSeconds(30);
|
||||
options.MaxBufferedUnacknowledgedRenderBatches = 5;
|
||||
options.DisconnectedCircuitMaxRetained = 1;
|
||||
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
|
||||
})
|
||||
@@ -120,30 +120,31 @@ public class Startup : AppStartup
|
||||
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
|
||||
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
||||
options.StreamBufferCapacity = 30;
|
||||
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
|
||||
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
|
||||
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
|
||||
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
|
||||
options.HandshakeTimeout = TimeSpan.FromSeconds(15);
|
||||
});
|
||||
|
||||
#else
|
||||
|
||||
services.AddServerSideBlazor(options =>
|
||||
{
|
||||
options.RootComponents.MaxJSRootComponents = 500;
|
||||
options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2);
|
||||
options.MaxBufferedUnacknowledgedRenderBatches = 20;
|
||||
options.DisconnectedCircuitMaxRetained = 1;
|
||||
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
|
||||
}).AddHubOptions(options =>
|
||||
{
|
||||
//单个传入集线器消息的最大大小。默认 32 KB
|
||||
options.MaximumReceiveMessageSize =32 * 1024 * 1024;
|
||||
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
||||
options.StreamBufferCapacity = 30;
|
||||
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
|
||||
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
|
||||
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
|
||||
});
|
||||
{
|
||||
options.RootComponents.MaxJSRootComponents = 500;
|
||||
options.JSInteropDefaultCallTimeout = TimeSpan.FromSeconds(30);
|
||||
options.MaxBufferedUnacknowledgedRenderBatches = 5;
|
||||
options.DisconnectedCircuitMaxRetained = 1;
|
||||
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
|
||||
})
|
||||
.AddHubOptions(options =>
|
||||
{
|
||||
//单个传入集线器消息的最大大小。默认 32 KB
|
||||
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
|
||||
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
||||
options.StreamBufferCapacity = 30;
|
||||
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
|
||||
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
|
||||
options.HandshakeTimeout = TimeSpan.FromSeconds(15);
|
||||
});
|
||||
|
||||
#endif
|
||||
|
||||
@@ -207,10 +208,7 @@ public class Startup : AppStartup
|
||||
|
||||
var websiteOptions = App.GetConfig<WebsiteOptions>("Website");
|
||||
|
||||
if (websiteOptions.BlazorConnectionLimitEnable)
|
||||
{
|
||||
services.AddSingleton<CircuitHandler, ConnectionLimiterCircuitHandler>();
|
||||
}
|
||||
services.AddSingleton<CircuitHandler, ConnectionLimiterCircuitHandler>();
|
||||
if (websiteOptions.Demo)
|
||||
{
|
||||
authenticationBuilder.AddOAuth<GiteeOAuthOptions, AdminOAuthHandler<GiteeOAuthOptions>>("Gitee", "Gitee", options =>
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
using Microsoft.AspNetCore.Components.Server.Circuits;
|
||||
|
||||
using System.Runtime;
|
||||
|
||||
using ThingsGateway.Common;
|
||||
|
||||
namespace ThingsGateway.Server;
|
||||
@@ -22,6 +24,12 @@ public class ConnectionLimiterCircuitHandler : CircuitHandler
|
||||
|
||||
public override Task OnCircuitOpenedAsync(Circuit circuit, CancellationToken cancellationToken)
|
||||
{
|
||||
//主动触发垃圾回收,释放上个链路资源
|
||||
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
|
||||
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect(1, GCCollectionMode.Optimized, blocking: false, compacting: false);
|
||||
|
||||
WebsiteOptions ??= App.GetOptions<WebsiteOptions>();
|
||||
|
||||
if (!WebsiteOptions.BlazorConnectionLimitEnable)
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
"System.Runtime.EnableWriteXorExecute": false,
|
||||
"System.GC.HeapHardLimitPercent": 95, //堆限制百分比
|
||||
"System.GC.HighMemoryPercent": 90, //高内存百分比
|
||||
"System.GC.DynamicAdaptationMode": 1 //动态适应模式
|
||||
"System.GC.DynamicAdaptationMode": 1, //动态适应模式
|
||||
"System.GC.ConserveMemory": 5 //节省内存模式,0-9
|
||||
//"System.GC.RegionRange": 549755813888 //8GB, 区域范围,保留的虚拟内存,如DOCKER内出现OOM,可以调大,一般是进程内存限制的2倍
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user