Compare commits
21 Commits
10.12.7.0
...
10.12.27.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5deef89b96 | ||
|
|
ce5fa0f37d | ||
|
|
694437c7d5 | ||
|
|
0e78cdefe7 | ||
|
|
087dc9aaa3 | ||
|
|
dacf255f1a | ||
|
|
e3960ce115 | ||
|
|
0cf098be85 | ||
|
|
c93c80468c | ||
|
|
a464594885 | ||
|
|
b42f8afa35 | ||
|
|
facf8bd401 | ||
|
|
feeb17eca3 | ||
|
|
b3405cd674 | ||
|
|
c35f9cef93 | ||
|
|
3f382202db | ||
|
|
2a3493cc82 | ||
|
|
aaa459ebe0 | ||
|
|
51a8acbc3e | ||
|
|
dc132a1999 | ||
|
|
0c31cfcbc5 |
43
README.md
@@ -1,4 +1,7 @@
|
||||
# ThingsGateway
|
||||
|
||||
<p align="center">
|
||||
<img src="logo.svg" width = "400" height = "200" alt="The name of the image" align=center />
|
||||
</p>
|
||||
|
||||
[](https://gitee.com/ThingsGateway/ThingsGateway/stargazers)
|
||||
[](https://github.com/ThingsGateway/ThingsGateway)
|
||||
@@ -11,31 +14,31 @@
|
||||
</a>
|
||||
|
||||
## Introduction
|
||||
|
||||
|
||||
A cross-platform, high-performance edge data collection gateway based on net8/10.
|
||||
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
|
||||
|
||||
[Documentation](https://thingsgateway.cn/).
|
||||
|
||||
|
||||
[NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
|
||||
|
||||
|
||||
|
||||
|
||||
## Demo
|
||||
|
||||
|
||||
|
||||
[Demo](https://demo.thingsgateway.cn/)
|
||||
|
||||
|
||||
|
||||
Account: **SuperAdmin**
|
||||
|
||||
|
||||
|
||||
Password: **111111**
|
||||
|
||||
|
||||
|
||||
|
||||
## Docker
|
||||
|
||||
@@ -50,7 +53,7 @@ docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
|
||||
|
||||
### Plugin List
|
||||
|
||||
|
||||
|
||||
|
||||
#### Data Collection Plugins
|
||||
|
||||
@@ -80,28 +83,28 @@ docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
|
||||
| TDengineDB | Time-series database storage |
|
||||
| QuestDB | Time-series database storage |
|
||||
|
||||
|
||||
|
||||
|
||||
## License
|
||||
|
||||
|
||||
|
||||
[License](https://thingsgateway.cn/docs/1)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Sponsorship
|
||||
|
||||
|
||||
|
||||
[Sponsorship Approach](https://thingsgateway.cn/docs/1000)
|
||||
|
||||
|
||||
|
||||
## Community
|
||||
|
||||
|
||||
|
||||
QQ Group: 605534569 [Jump](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569)
|
||||
|
||||
|
||||
|
||||
## Pro Plugins
|
||||
|
||||
|
||||
|
||||
[Plugin List](https://thingsgateway.cn/docs/1001)
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# ThingsGateway
|
||||
|
||||
<p align="center">
|
||||
<img src="logo.svg" width = "400" height = "200" alt="The name of the image" align=center />
|
||||
</p>
|
||||
|
||||
[](https://gitee.com/ThingsGateway/ThingsGateway/stargazers)
|
||||
[](https://github.com/ThingsGateway/ThingsGateway)
|
||||
|
||||
BIN
icon.ico
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 257 KiB |
BIN
icon.png
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 177 KiB |
9
logo.svg
Normal file
|
After Width: | Height: | Size: 236 KiB |
@@ -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
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 257 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 177 KiB |
@@ -0,0 +1,134 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ThingsGateway.Common;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||
public static class EnumerableQueryPageOptionsExtensions
|
||||
{
|
||||
public static IEnumerable<T> GetData<T>(this IEnumerable<T> datas, QueryPageOptions option, out int totalCount, FilterKeyValueAction where = null)
|
||||
{
|
||||
totalCount = 0;
|
||||
if (datas == null)
|
||||
return new List<T>();
|
||||
where ??= option.ToFilter();
|
||||
if (where.HasFilters())
|
||||
{
|
||||
datas = datas.Where(where.GetFilterFunc<T>());//name asc模式
|
||||
}
|
||||
|
||||
if (option.SortList.Count > 0)
|
||||
{
|
||||
datas = datas.Sort(option.SortList);//name asc模式
|
||||
}
|
||||
if (option.AdvancedSortList.Count > 0)
|
||||
{
|
||||
datas = datas.Sort(option.AdvancedSortList);//name asc模式
|
||||
}
|
||||
if (option.SortOrder != SortOrder.Unset && !option.SortName.IsNullOrWhiteSpace())
|
||||
{
|
||||
datas = datas.Sort(option.SortName, option.SortOrder);
|
||||
}
|
||||
|
||||
totalCount = datas.Count();
|
||||
|
||||
if (option.IsPage)
|
||||
{
|
||||
datas = datas.Skip((option.PageIndex - 1) * option.PageItems).Take(option.PageItems);
|
||||
}
|
||||
else if (option.IsVirtualScroll)
|
||||
{
|
||||
datas = datas.Skip((option.StartIndex) * option.PageItems).Take(option.PageItems);
|
||||
}
|
||||
return datas;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static IEnumerable<T> GetQuery<T>(this IEnumerable<T> query, QueryPageOptions option, Func<IEnumerable<T>, IEnumerable<T>>? queryFunc = null, FilterKeyValueAction where = null)
|
||||
{
|
||||
if (queryFunc != null)
|
||||
query = queryFunc(query);
|
||||
where ??= option.ToFilter();
|
||||
|
||||
if (where.HasFilters())
|
||||
{
|
||||
query = query.Where(where.GetFilterFunc<T>());//name asc模式
|
||||
}
|
||||
|
||||
if (option.SortOrder != SortOrder.Unset && !string.IsNullOrEmpty(option.SortName))
|
||||
{
|
||||
var invoker = Utility.GetSortFunc<T>();
|
||||
query = invoker(query, option.SortName, option.SortOrder);
|
||||
}
|
||||
else if (option.SortList.Count > 0)
|
||||
{
|
||||
var invoker = Utility.GetSortListFunc<T>();
|
||||
query = invoker(query, option.SortList);
|
||||
}
|
||||
else if (option.AdvancedSortList.Count > 0)
|
||||
{
|
||||
var invoker = Utility.GetSortListFunc<T>();
|
||||
query = invoker(query, option.AdvancedSortList);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 根据查询条件返回QueryData
|
||||
/// </summary>
|
||||
public static QueryData<T> GetQueryData<T>(this IEnumerable<T> datas, QueryPageOptions option, FilterKeyValueAction where = null)
|
||||
{
|
||||
var ret = new QueryData<T>()
|
||||
{
|
||||
IsSorted = option.SortOrder != SortOrder.Unset,
|
||||
IsFiltered = option.Filters.Count > 0,
|
||||
IsAdvanceSearch = option.AdvanceSearches.Count > 0 || option.CustomerSearches.Count > 0,
|
||||
IsSearch = option.Searches.Count > 0
|
||||
};
|
||||
var items = datas.GetData(option, out var totalCount, where);
|
||||
ret.TotalCount = totalCount;
|
||||
|
||||
if (totalCount > 0)
|
||||
{
|
||||
if (!items.Any() && option.PageIndex != 1)
|
||||
{
|
||||
option.PageIndex = 1;
|
||||
items = datas.GetData(option, out totalCount, where);
|
||||
}
|
||||
}
|
||||
ret.Items = items.ToList();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据查询条件返回QueryData
|
||||
/// </summary>
|
||||
public static QueryData<SelectedItem> GetQueryData<T>(this IEnumerable<T> datas, VirtualizeQueryOption option, Func<IEnumerable<T>, IEnumerable<SelectedItem>> func, FilterKeyValueAction where = null)
|
||||
{
|
||||
var ret = new QueryData<SelectedItem>()
|
||||
{
|
||||
IsSorted = false,
|
||||
IsFiltered = false,
|
||||
IsAdvanceSearch = false,
|
||||
IsSearch = !option.SearchText.IsNullOrWhiteSpace()
|
||||
};
|
||||
|
||||
var items = datas.Skip((option.StartIndex)).Take(option.Count);
|
||||
ret.TotalCount = datas.Count();
|
||||
|
||||
ret.Items = func(items).ToList();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -19,7 +19,7 @@ using System.Reflection;
|
||||
|
||||
using ThingsGateway.Common.Extension;
|
||||
|
||||
namespace ThingsGateway.DB;
|
||||
namespace ThingsGateway.Common;
|
||||
|
||||
/// <summary>
|
||||
/// 导出excel扩展
|
||||
@@ -11,7 +11,7 @@
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace ThingsGateway.DB;
|
||||
namespace ThingsGateway.Common;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.7" />
|
||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
||||
<PackageReference Include="BootstrapBlazor" Version="9.11.4" />
|
||||
<PackageReference Include="BootstrapBlazor" Version="9.12.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
using Yitter.IdGenerator;
|
||||
|
||||
namespace ThingsGateway.DB;
|
||||
namespace ThingsGateway.Common;
|
||||
|
||||
/// <summary>
|
||||
/// 公共功能
|
||||
@@ -16,74 +16,6 @@ namespace ThingsGateway.DB;
|
||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||
public static class QueryPageOptionsExtensions
|
||||
{
|
||||
public static IEnumerable<T> GetData<T>(this IEnumerable<T> datas, QueryPageOptions option, out int totalCount, FilterKeyValueAction where = null)
|
||||
{
|
||||
totalCount = 0;
|
||||
if (datas == null)
|
||||
return new List<T>();
|
||||
where ??= option.ToFilter();
|
||||
if (where.HasFilters())
|
||||
{
|
||||
datas = datas.Where(where.GetFilterFunc<T>());//name asc模式
|
||||
}
|
||||
|
||||
if (option.SortList.Count > 0)
|
||||
{
|
||||
datas = datas.Sort(option.SortList);//name asc模式
|
||||
}
|
||||
if (option.AdvancedSortList.Count > 0)
|
||||
{
|
||||
datas = datas.Sort(option.AdvancedSortList);//name asc模式
|
||||
}
|
||||
if (option.SortOrder != SortOrder.Unset && !option.SortName.IsNullOrWhiteSpace())
|
||||
{
|
||||
datas = datas.Sort(option.SortName, option.SortOrder);
|
||||
}
|
||||
|
||||
totalCount = datas.Count();
|
||||
|
||||
if (option.IsPage)
|
||||
{
|
||||
datas = datas.Skip((option.PageIndex - 1) * option.PageItems).Take(option.PageItems);
|
||||
}
|
||||
else if (option.IsVirtualScroll)
|
||||
{
|
||||
datas = datas.Skip((option.StartIndex) * option.PageItems).Take(option.PageItems);
|
||||
}
|
||||
return datas;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static IEnumerable<T> GetQuery<T>(this IEnumerable<T> query, QueryPageOptions option, Func<IEnumerable<T>, IEnumerable<T>>? queryFunc = null, FilterKeyValueAction where = null)
|
||||
{
|
||||
if (queryFunc != null)
|
||||
query = queryFunc(query);
|
||||
where ??= option.ToFilter();
|
||||
|
||||
if (where.HasFilters())
|
||||
{
|
||||
query = query.Where(where.GetFilterFunc<T>());//name asc模式
|
||||
}
|
||||
|
||||
if (option.SortOrder != SortOrder.Unset && !string.IsNullOrEmpty(option.SortName))
|
||||
{
|
||||
var invoker = Utility.GetSortFunc<T>();
|
||||
query = invoker(query, option.SortName, option.SortOrder);
|
||||
}
|
||||
else if (option.SortList.Count > 0)
|
||||
{
|
||||
var invoker = Utility.GetSortListFunc<T>();
|
||||
query = invoker(query, option.SortList);
|
||||
}
|
||||
else if (option.AdvancedSortList.Count > 0)
|
||||
{
|
||||
var invoker = Utility.GetSortListFunc<T>();
|
||||
query = invoker(query, option.AdvancedSortList);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据查询条件返回sqlsugar ISugarQueryable
|
||||
/// </summary>
|
||||
@@ -111,50 +43,4 @@ public static class QueryPageOptionsExtensions
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据查询条件返回QueryData
|
||||
/// </summary>
|
||||
public static QueryData<T> GetQueryData<T>(this IEnumerable<T> datas, QueryPageOptions option, FilterKeyValueAction where = null)
|
||||
{
|
||||
var ret = new QueryData<T>()
|
||||
{
|
||||
IsSorted = option.SortOrder != SortOrder.Unset,
|
||||
IsFiltered = option.Filters.Count > 0,
|
||||
IsAdvanceSearch = option.AdvanceSearches.Count > 0 || option.CustomerSearches.Count > 0,
|
||||
IsSearch = option.Searches.Count > 0
|
||||
};
|
||||
var items = datas.GetData(option, out var totalCount, where);
|
||||
ret.TotalCount = totalCount;
|
||||
|
||||
if (totalCount > 0)
|
||||
{
|
||||
if (!items.Any() && option.PageIndex != 1)
|
||||
{
|
||||
option.PageIndex = 1;
|
||||
items = datas.GetData(option, out totalCount, where);
|
||||
}
|
||||
}
|
||||
ret.Items = items.ToList();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据查询条件返回QueryData
|
||||
/// </summary>
|
||||
public static QueryData<SelectedItem> GetQueryData<T>(this IEnumerable<T> datas, VirtualizeQueryOption option, Func<IEnumerable<T>, IEnumerable<SelectedItem>> func, FilterKeyValueAction where = null)
|
||||
{
|
||||
var ret = new QueryData<SelectedItem>()
|
||||
{
|
||||
IsSorted = false,
|
||||
IsFiltered = false,
|
||||
IsAdvanceSearch = false,
|
||||
IsSearch = !option.SearchText.IsNullOrWhiteSpace()
|
||||
};
|
||||
|
||||
var items = datas.Skip((option.StartIndex)).Take(option.Count);
|
||||
ret.TotalCount = datas.Count();
|
||||
|
||||
ret.Items = func(items).ToList();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 257 KiB |
|
After Width: | Height: | Size: 177 KiB |
@@ -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;
|
||||
@@ -414,7 +412,7 @@ public class MachineInfo
|
||||
#if NETFRAMEWORK || WINDOWS
|
||||
{
|
||||
var ci = new Microsoft.VisualBasic.Devices.ComputerInfo();
|
||||
Memory = (ulong)(ci.TotalPhysicalMemory / 1024.0);
|
||||
Memory = (ulong)(ci.TotalPhysicalMemory / 1024.0 / 1024.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -634,7 +632,7 @@ public class MachineInfo
|
||||
//if (dic2.TryGetValue("Model Name", out str)) Product = str;
|
||||
if (dic.TryGetValue("Model Identifier", out var str)) Product = str;
|
||||
if (dic.TryGetValue("Processor Name", out str)) Processor = str;
|
||||
if (dic.TryGetValue("Memory", out str)) Memory = (UInt64)str.TrimEnd("GB").Trim().ToLong() * 1024 * 1024;
|
||||
if (dic.TryGetValue("Memory", out str)) Memory = (UInt64)str.TrimEnd("GB").Trim().ToLong() * 1024;
|
||||
if (dic.TryGetValue("Serial Number (system)", out str)) Serial = str;
|
||||
if (dic.TryGetValue("Hardware UUID", out str)) UUID = str;
|
||||
if (dic.TryGetValue("Processor Name", out str)) Processor = str;
|
||||
@@ -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
|
||||
@@ -870,7 +868,7 @@ public class MachineInfo
|
||||
if (dic != null)
|
||||
{
|
||||
if (dic.TryGetValue("MemTotal", out var str) && !str.IsNullOrEmpty())
|
||||
Memory = (UInt64)str.TrimEnd(" kB").ToLong();
|
||||
Memory = (UInt64)(str.TrimEnd(" kB").ToLong() / 1024.0);
|
||||
|
||||
ulong ma = 0;
|
||||
if (dic.TryGetValue("MemAvailable", out str) && !str.IsNullOrEmpty())
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\Foundation.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<IncludeAsyncInterfaces>false</IncludeAsyncInterfaces>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="$(TargetFramework)=='net6.0'">
|
||||
<DefineConstants>$(DefineConstants);PLAT_THREADPOOLWORKITEM;PLAT_MRVTSC</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework)=='net8.0'">
|
||||
<DefineConstants>$(DefineConstants);PLAT_THREADPOOLWORKITEM;PLAT_MRVTSC</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="$(TargetFramework)=='netstandard2.0'">
|
||||
<DefineConstants>$(DefineConstants);PLAT_MRVTSC</DefineConstants>
|
||||
<IncludeAsyncInterfaces>true</IncludeAsyncInterfaces>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="$(TargetFramework)=='net462'">
|
||||
<DefineConstants>$(DefineConstants);PLAT_MRVTSC</DefineConstants>
|
||||
<IncludeAsyncInterfaces>true</IncludeAsyncInterfaces>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="1.1.0" Condition="$(IncludeAsyncInterfaces)=='true'" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,5 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Benchmark, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d136a87a0c11ded39332f7ead1f2ce53256a42a350ee847df1dc520bc781210a5f1fe73b3f91a4001fdcfa35340a212e70443de46e6928510f57a55f4ab9b95257f5067d4de4be8cdad460781866b20a6ca20f66302df61b52e55e16c080cad95aae8b94ffdd54718b9963d0240b0d0d5afe655b05c91771f7dc8a314387d3c3")]
|
||||
|
||||
[assembly: InternalsVisibleTo("PooledAwait.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d136a87a0c11ded39332f7ead1f2ce53256a42a350ee847df1dc520bc781210a5f1fe73b3f91a4001fdcfa35340a212e70443de46e6928510f57a55f4ab9b95257f5067d4de4be8cdad460781866b20a6ca20f66302df61b52e55e16c080cad95aae8b94ffdd54718b9963d0240b0d0d5afe655b05c91771f7dc8a314387d3c3")]
|
||||
@@ -4,7 +4,7 @@
|
||||
<Import Project="..\..\PackNuget.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net47;netstandard2.0;net6.0;net6.0-windows;net8.0;$(OtherTargetFrameworks);net8.0-windows;</TargetFrameworks>
|
||||
<TargetFrameworks>net462;netstandard2.0;net6.0;net6.0-windows;net8.0;$(OtherTargetFrameworks);net8.0-windows;</TargetFrameworks>
|
||||
<AssemblyName>ThingsGateway.NewLife.X</AssemblyName>
|
||||
<RootNamespace>ThingsGateway.NewLife</RootNamespace>
|
||||
<AssemblyTitle>工具核心库</AssemblyTitle>
|
||||
@@ -12,9 +12,6 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<SignAssembly>True</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>newlife.snk</AssemblyOriginatorKeyFile>
|
||||
|
||||
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -24,7 +21,7 @@
|
||||
<None Remove="..\..\..\README.zh-CN.md" Pack="false" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='net47' or '$(TargetFramework)'=='net5.0-windows' or '$(TargetFramework)'=='net6.0-windows' or '$(TargetFramework)'=='net7.0-windows' or '$(TargetFramework)'=='net8.0-windows'">
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='net462' or '$(TargetFramework)'=='net5.0-windows' or '$(TargetFramework)'=='net6.0-windows' or '$(TargetFramework)'=='net7.0-windows' or '$(TargetFramework)'=='net8.0-windows'">
|
||||
<DefineConstants>__WIN__</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -35,11 +32,12 @@
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
|
||||
<PackageReference Include="System.Memory" Version="4.6.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net47'">
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net462'">
|
||||
<PackageReference Include="System.Memory" Version="4.6.3" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.6.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net47'">
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net462'">
|
||||
<Using Include="System.Net.Http" />
|
||||
<Reference Include="Microsoft.VisualBasic" />
|
||||
<Reference Include="System.Management" />
|
||||
@@ -54,10 +52,7 @@
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="$(TargetFramework)=='net6.0'">
|
||||
<DefineConstants>$(DefineConstants);PLAT_THREADPOOLWORKITEM;PLAT_MRVTSC</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework)=='net8.0'">
|
||||
<PropertyGroup Condition="$(TargetFramework)=='net6.0' OR $(TargetFramework)=='net6.0-windows' OR $(TargetFramework)=='net8.0' OR $(TargetFramework)=='net8.0-windows' OR $(TargetFramework)=='net10.0' OR $(TargetFramework)=='net10.0-windows'">
|
||||
<DefineConstants>$(DefineConstants);PLAT_THREADPOOLWORKITEM;PLAT_MRVTSC</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -66,7 +61,7 @@
|
||||
<IncludeAsyncInterfaces>true</IncludeAsyncInterfaces>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="$(TargetFramework)=='net47'">
|
||||
<PropertyGroup Condition="$(TargetFramework)=='net462'">
|
||||
<DefineConstants>$(DefineConstants);PLAT_MRVTSC</DefineConstants>
|
||||
<IncludeAsyncInterfaces>true</IncludeAsyncInterfaces>
|
||||
</PropertyGroup>
|
||||
@@ -88,6 +83,6 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
|
||||
</ItemGroup>-->
|
||||
|
||||
|
||||
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Photino.NET" Version="4.0.16" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 257 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 177 KiB |
@@ -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)))
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SqlSugarCore.Dm" Version="8.8.2" />
|
||||
<PackageReference Include="SqlSugarCore.Kdbndp" Version="9.3.7.905" />
|
||||
<PackageReference Include="SqlSugarCore.Kdbndp" Version="9.3.7.1032" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.20" />
|
||||
<!--<PackageReference Include="Microsoft.Data.Sqlite" Version="$(NET10Version)" />-->
|
||||
<PackageReference Include="MySqlConnector" Version="2.4.0" />
|
||||
|
||||
@@ -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.27</PluginVersion>
|
||||
<ProPluginVersion>10.12.27</ProPluginVersion>
|
||||
<DefaultVersion>10.12.27</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.15</TSVersion>
|
||||
|
||||
|
||||
</PropertyGroup>
|
||||
@@ -22,7 +22,9 @@
|
||||
<OtherTargetFrameworks>net10.0</OtherTargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<PropertyGroup>
|
||||
<RestoreEnablePackagePruning Condition="'$(TargetFramework)' == 'net462' "> false</RestoreEnablePackagePruning>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' != 'net8.0' ">
|
||||
<PluginTargetFramework>net10.0</PluginTargetFramework>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -333,6 +333,10 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
||||
/// <inheritdoc/>
|
||||
private Task SendAsync(ISendMessage sendMessage, IClientChannel channel, CancellationToken token = default)
|
||||
{
|
||||
if(!channel.Online)
|
||||
{
|
||||
throw new InvalidOperationException("Channel is offline");
|
||||
}
|
||||
return SendAsync(this, sendMessage, channel, token);
|
||||
|
||||
static async PooledTask SendAsync(DeviceBase @this, ISendMessage sendMessage, IClientChannel channel, CancellationToken token)
|
||||
@@ -1060,10 +1064,6 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
||||
}
|
||||
|
||||
Channel.Collects.Remove(this);
|
||||
if (Channel is IClientChannel clientChannel)
|
||||
{
|
||||
clientChannel.LogSeted(false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1118,10 +1118,6 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
||||
|
||||
Channel.Collects.Remove(this);
|
||||
|
||||
if (Channel is IClientChannel clientChannel)
|
||||
{
|
||||
clientChannel.LogSeted(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -428,8 +428,9 @@ where TWriter : IByteBlockWriter
|
||||
}
|
||||
|
||||
|
||||
public static int WriteNormalString(this Span<byte> span, string value, Encoding encoding)
|
||||
public static int WriteNormalString(this Span<byte> span, string value, Encoding encoding=null)
|
||||
{
|
||||
encoding ??= Encoding.UTF8;
|
||||
var maxSize = encoding.GetMaxByteCount(value.Length);
|
||||
var chars = value.AsSpan();
|
||||
|
||||
@@ -439,7 +440,7 @@ where TWriter : IByteBlockWriter
|
||||
{
|
||||
fixed (byte* p1 = &span[0])
|
||||
{
|
||||
var len = Encoding.UTF8.GetBytes(p, chars.Length, p1, maxSize);
|
||||
var len = encoding.GetBytes(p, chars.Length, p1, maxSize);
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Admin\ThingsGateway.NewLife.X\ThingsGateway.NewLife.X.csproj" />
|
||||
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,34 +509,16 @@ internal sealed class AlarmTask : IDisposable
|
||||
{
|
||||
scheduledTask.Change(100, 100);
|
||||
}
|
||||
|
||||
ParallelOptions.CancellationToken = cancellation;
|
||||
// 遍历设备变量列表
|
||||
if (!GlobalData.AlarmEnableIdVariables.IsEmpty)
|
||||
{
|
||||
// 使用 Parallel.ForEach 执行指定的操作
|
||||
Parallel.ForEach(GlobalData.AlarmEnableIdVariables, ParallelOptions, (item, state, index) =>
|
||||
{
|
||||
// 如果取消请求已经被触发,则结束任务
|
||||
if (cancellation.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
// 如果该变量的报警功能未启用,则跳过该变量
|
||||
if (!item.Value.AlarmEnable)
|
||||
return;
|
||||
|
||||
// 如果该变量离线,则跳过该变量
|
||||
if (!item.Value.IsOnline)
|
||||
return;
|
||||
|
||||
// 对该变量进行报警分析
|
||||
AlarmAnalysis(item.Value);
|
||||
});
|
||||
Parallel.ForEach(GlobalData.AlarmEnableIdVariables, ParallelOptions, Analysis);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
//if (scheduledTask.Period != 5000)
|
||||
// scheduledTask.Change(0, 5000); // 如果没有启用报警的变量,则设置下次执行时间为5秒后
|
||||
scheduledTask.SetNext(5000); // 如果没有启用报警的变量,则设置下次执行时间为5秒后
|
||||
}
|
||||
|
||||
@@ -552,6 +534,21 @@ internal sealed class AlarmTask : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private static void Analysis(KeyValuePair<long, VariableRuntime> item, ParallelLoopState state, long index)
|
||||
{
|
||||
// 如果取消请求已经被触发,则结束任务
|
||||
if (state.ShouldExitCurrentIteration)
|
||||
return;
|
||||
|
||||
// 如果该变量的报警功能未启用,则跳过该变量
|
||||
if (!item.Value.AlarmEnable)
|
||||
return;
|
||||
|
||||
// 如果该变量离线,则跳过该变量
|
||||
if (!item.Value.IsOnline)
|
||||
return;
|
||||
|
||||
// 对该变量进行报警分析
|
||||
AlarmAnalysis(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);
|
||||
|
||||
@@ -295,6 +295,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
|
||||
catch
|
||||
{
|
||||
}
|
||||
_tcpDmtpService.TryDispose();
|
||||
}
|
||||
if (_tcpDmtpClient != null)
|
||||
{
|
||||
@@ -305,6 +306,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
|
||||
catch
|
||||
{
|
||||
}
|
||||
_tcpDmtpClient.TryDispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,7 +347,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 +378,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 +423,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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 257 KiB |
BIN
src/Plugin/ThingsGateway.Debug.Photino/favicon.png
Normal file
|
After Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 257 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 177 KiB |
@@ -58,7 +58,6 @@ public class ModbusBenchmark : IDisposable
|
||||
thingsgatewaymodbuss.Add(thingsgatewaymodbus);
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < ClientCount; i++)
|
||||
{
|
||||
|
||||
@@ -68,7 +67,6 @@ public class ModbusBenchmark : IDisposable
|
||||
nmodbuss.Add(nmodbus);
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < ClientCount; i++)
|
||||
{
|
||||
var client = new ModbusTcpMaster();
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.15.4" />
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.15.5" />
|
||||
<PackageReference Include="NModbus" Version="3.0.81" />
|
||||
<PackageReference Include="S7netplus" Version="0.20.0" />
|
||||
<!--<PackageReference Include="ThingsGateway.Foundation.Modbus" Version="$(DefaultVersion)" />
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
using ThingsGateway.Foundation.Modbus;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
#pragma warning disable CA1861 // 不要将常量数组作为参数
|
||||
@@ -55,6 +57,7 @@ public class ModbusMasterDemo
|
||||
|
||||
//获取协议对象
|
||||
using var device = GetDevice(channel);
|
||||
await channel.SetupAsync(channel.Config);
|
||||
|
||||
//读取具体类型数据
|
||||
var data = await device.ReadDoubleAsync("400001"); //通过字符串转化地址,读取保持寄存器地址0
|
||||
|
||||
@@ -55,6 +55,7 @@ public class SiemensS7MasterDemo
|
||||
|
||||
//获取协议对象
|
||||
using var device = GetDevice(channel);
|
||||
await channel.SetupAsync(channel.Config);
|
||||
|
||||
//读取具体类型数据
|
||||
var data = await device.ReadDoubleAsync("V1"); //通过字符串转化地址,读取v1
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
using Riok.Mapperly.Abstractions;
|
||||
|
||||
using ThingsGateway.Common;
|
||||
using ThingsGateway.DB;
|
||||
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
|
||||
@@ -171,10 +171,10 @@ public partial class MqttCollect : CollectBase
|
||||
mqttClientSubscribeOptionsBuilder = mqttClientSubscribeOptionsBuilder.WithTopicFilter(
|
||||
f => f.WithTopic(item));
|
||||
}
|
||||
var mqttClientSubscribeOptions = mqttClientSubscribeOptionsBuilder.Build();
|
||||
if (mqttClientSubscribeOptions.TopicFilters.Count > 0)
|
||||
_mqttSubscribeOptions = mqttClientSubscribeOptions;
|
||||
}
|
||||
var mqttClientSubscribeOptions = mqttClientSubscribeOptionsBuilder.Build();
|
||||
if (mqttClientSubscribeOptions.TopicFilters.Count > 0)
|
||||
_mqttSubscribeOptions = mqttClientSubscribeOptions;
|
||||
|
||||
return Task.FromResult(dataResult);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using ThingsGateway.Extension;
|
||||
using ThingsGateway.Foundation.OpcDa;
|
||||
using ThingsGateway.Foundation.OpcDa.Rcw;
|
||||
using ThingsGateway.Common;
|
||||
|
||||
|
||||
#if Plugin
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ using ThingsGateway.Razor;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
using ThingsGateway.Common;
|
||||
|
||||
namespace ThingsGateway.Debug;
|
||||
|
||||
/// <summary>
|
||||
@@ -73,7 +75,7 @@ public partial class OpcUaImportVariable
|
||||
{
|
||||
Items = BuildTreeItemList(await PopulateBranchAsync(ObjectIds.ObjectsFolder), RenderTreeItem).ToList();
|
||||
ShowSkeleton = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
return InvokeAsync(StateHasChanged);
|
||||
});
|
||||
}
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 257 KiB |
BIN
src/ThingsGateway.Photino/favicon.png
Normal file
|
After Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 257 KiB |
BIN
src/ThingsGateway.RemoteWebApp/favicon.png
Normal file
|
After Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 257 KiB |
BIN
src/ThingsGateway.ScriptDebug/favicon.png
Normal file
|
After Width: | Height: | Size: 177 KiB |
@@ -3,6 +3,6 @@
|
||||
"CheckInterval": 1800000, //检查间隔
|
||||
"MaxChannelCount": 50, //最大通道数量
|
||||
"MaxDeviceCount": 50, //最大设备数量
|
||||
"MaxVariableCount": 10000 //最大变量数量
|
||||
"MaxVariableCount": 5000 //最大变量数量
|
||||
}
|
||||
}
|
||||
|
||||
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
@@ -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,36 @@ 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 (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)
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 257 KiB |
BIN
src/ThingsGateway.Server/favicon.png
Normal file
|
After Width: | Height: | Size: 177 KiB |
@@ -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倍
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 257 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 177 KiB |