Compare commits
	
		
			67 Commits
		
	
	
		
			10.11.85.0
			...
			v10
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | ce5fa0f37d | ||
|   | 694437c7d5 | ||
|   | 0e78cdefe7 | ||
|   | 087dc9aaa3 | ||
|   | dacf255f1a | ||
|   | e3960ce115 | ||
|   | 0cf098be85 | ||
|   | c93c80468c | ||
|   | a464594885 | ||
|   | b42f8afa35 | ||
|   | facf8bd401 | ||
|   | feeb17eca3 | ||
|   | b3405cd674 | ||
|   | c35f9cef93 | ||
|   | 3f382202db | ||
|   | 2a3493cc82 | ||
|   | aaa459ebe0 | ||
|   | 51a8acbc3e | ||
|   | dc132a1999 | ||
|   | 0c31cfcbc5 | ||
|   | 0e44bc67cd | ||
|   | 12dfbba42c | ||
|   | 96b4287f3a | ||
|   | 7d406de29f | ||
|   | 81f0ef466a | ||
|   | 3f2d6b133c | ||
|   | e776dc67eb | ||
| ![devin-ai-integration[bot]](/assets/img/avatar_default.png)  | bc5827d140 | ||
|   | 21838bf4af | ||
|   | 6090108597 | ||
|   | b47b9e6f43 | ||
|   | 18d1cffb2d | ||
|   | 516fd7f235 | ||
|   | 2ee16c3533 | ||
|   | 7d22f5c78e | ||
|   | 3e604ee2fd | ||
|   | 47e442874c | ||
|   | 2a8c0cbab1 | ||
|   | c26898b49d | ||
|   | 00c24d06a3 | ||
|   | 3461f34240 | ||
|   | aa1ce08c02 | ||
|   | 9c230c2da9 | ||
|   | 21215d0379 | ||
|   | 7448183791 | ||
|   | 35edd7dc43 | ||
|   | bd178831e3 | ||
|   | fe9ec6ad10 | ||
|   | 6f9ec2e24b | ||
|   | c0337e2b19 | ||
|   | 8a95f48f5a | ||
|   | 14f3c31265 | ||
|   | 1bad65378f | ||
|   | db3affc67e | ||
|   | 5ee8b50a92 | ||
|   | 301beda2a2 | ||
|   | 628b51a353 | ||
|   | f03445bc83 | ||
|   | 55a2ff5487 | ||
|   | 0fef7dcf3b | ||
|   | 19d9702606 | ||
|   | a8a9774932 | ||
|   | aad0f0e8c3 | ||
|   | e74eae50a7 | ||
|   | 3b16d7019f | ||
|   | 3e038028c2 | ||
|   | b1d8041f7e | 
							
								
								
									
										55
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @@ -1,35 +1,44 @@ | |||||||
| # 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) | ||||||
|  | [](https://deepwiki.com/ThingsGateway/ThingsGateway) | ||||||
|  | [](https://www.nuget.org/packages/ThingsGateway.Foundation/) | ||||||
|  | [](https://www.nuget.org/packages/ThingsGateway.Foundation/) | ||||||
|  | [](https://thingsgateway.cn/docs/1) | ||||||
|  | <a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569"> | ||||||
|  | <img src="https://img.shields.io/badge/QQ群-605534569-red" alt="QQ"> | ||||||
|  | </a> | ||||||
|  |  | ||||||
| ## Introduction | ## Introduction | ||||||
|  |  | ||||||
|  | A cross-platform, high-performance edge data collection gateway based on net8/10. | ||||||
| A cross-platform, high-performance edge data collection gateway based on net9. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ## Documentation | ## Documentation | ||||||
|  |  | ||||||
|  |  | ||||||
| [Documentation](https://thingsgateway.cn/). | [Documentation](https://thingsgateway.cn/). | ||||||
|  |  | ||||||
| [NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22) | [NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ## Demo | ## Demo | ||||||
|  |  | ||||||
|  |  | ||||||
| [Demo](https://demo.thingsgateway.cn/) | [Demo](https://demo.thingsgateway.cn/) | ||||||
|  |  | ||||||
|  |  | ||||||
| Account: **SuperAdmin** | Account: **SuperAdmin** | ||||||
|  |  | ||||||
|  |  | ||||||
| Password: **111111** | Password: **111111** | ||||||
|  |  | ||||||
|  |  | ||||||
| **In the upper-right corner, switch to the IoT Gateway module in the personal popup box** |  | ||||||
|  |  | ||||||
| ## Docker | ## Docker | ||||||
|  |  | ||||||
| @@ -44,7 +53,7 @@ docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64 | |||||||
|  |  | ||||||
| ### Plugin List | ### Plugin List | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #### Data Collection Plugins | #### Data Collection Plugins | ||||||
|  |  | ||||||
| @@ -74,28 +83,28 @@ docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64 | |||||||
| | TDengineDB       | Time-series database storage                                                                      | | | TDengineDB       | Time-series database storage                                                                      | | ||||||
| | QuestDB          | Time-series database storage                                                                      | | | QuestDB          | Time-series database storage                                                                      | | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ## License | ## License | ||||||
|  |  | ||||||
|  |  | ||||||
| [License](https://thingsgateway.cn/docs/1) | [License](https://thingsgateway.cn/docs/1) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ## Sponsorship | ## Sponsorship | ||||||
|  |  | ||||||
|  |  | ||||||
| [Sponsorship Approach](https://thingsgateway.cn/docs/1000) | [Sponsorship Approach](https://thingsgateway.cn/docs/1000) | ||||||
|  |  | ||||||
|  |  | ||||||
| ## Community | ## 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) | 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 | ## Pro Plugins | ||||||
|  |  | ||||||
|  |  | ||||||
| [Plugin List](https://thingsgateway.cn/docs/1001) | [Plugin List](https://thingsgateway.cn/docs/1001) | ||||||
|   | |||||||
| @@ -1,8 +1,21 @@ | |||||||
| # 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) | ||||||
|  | [](https://deepwiki.com/ThingsGateway/ThingsGateway) | ||||||
|  | [](https://www.nuget.org/packages/ThingsGateway.Foundation/) | ||||||
|  | [](https://www.nuget.org/packages/ThingsGateway.Foundation/) | ||||||
|  | [](https://thingsgateway.cn/docs/1) | ||||||
|  | <a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569"> | ||||||
|  | <img src="https://img.shields.io/badge/QQ群-605534569-red" alt="QQ"> | ||||||
|  | </a> | ||||||
|  |  | ||||||
| ## 介绍 | ## 介绍 | ||||||
|  |  | ||||||
| 基于net9的跨平台高性能边缘采集网关 | 基于net8/10的跨平台高性能边缘采集网关 | ||||||
|  |  | ||||||
| ## 文档 | ## 文档 | ||||||
|  |  | ||||||
| @@ -19,7 +32,6 @@ | |||||||
|  |  | ||||||
| 密码 : **111111** | 密码 : **111111** | ||||||
|  |  | ||||||
| **右上角个人弹出框中,切换到物联网关模块** |  | ||||||
|  |  | ||||||
| ## Docker | ## Docker | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										
											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 | 
| @@ -251,11 +251,13 @@ public class RequestAuditFilter : IAsyncActionFilter, IOrderedFilter | |||||||
|  |  | ||||||
|         if (exception == null) |         if (exception == null) | ||||||
|         { |         { | ||||||
|             logger.Log(LogLevel.Information, $"{logData.Method}:{logData.Path}-{logData.Operation}"); |             if (logger.IsEnabled(LogLevel.Information)) | ||||||
|  |                 logger.Log(LogLevel.Information, $"{logData.Method}:{logData.Path}-{logData.Operation}"); | ||||||
|         } |         } | ||||||
|         else |         else | ||||||
|         { |         { | ||||||
|             logger.Log(LogLevel.Warning, $"{logData.Method}:{logData.Path}-{logData.Operation}{Environment.NewLine}{logData.Exception?.ToSystemTextJsonString()}{Environment.NewLine}{logData.Validation?.ToSystemTextJsonString()}"); |             if (logger.IsEnabled(LogLevel.Warning)) | ||||||
|  |                 logger.Log(LogLevel.Warning, $"{logData.Method}:{logData.Path}-{logData.Operation}{Environment.NewLine}{logData.Exception?.ToSystemTextJsonString()}{Environment.NewLine}{logData.Validation?.ToSystemTextJsonString()}"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,7 +8,8 @@ | |||||||
| //  QQ群:605534569 | //  QQ群:605534569 | ||||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||||
|  |  | ||||||
| using ThingsGateway.NewLife; | using System.ComponentModel; | ||||||
|  | using System.Runtime; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Admin.Application; | namespace ThingsGateway.Admin.Application; | ||||||
|  |  | ||||||
| @@ -20,10 +21,6 @@ public class HardwareInfo | |||||||
|     /// </summary> |     /// </summary> | ||||||
|     public DriveInfo DriveInfo { get; set; } |     public DriveInfo DriveInfo { get; set; } | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// 硬件信息获取 |  | ||||||
|     /// </summary> |  | ||||||
|     public MachineInfo? MachineInfo { get; set; } |  | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 主机环境 |     /// 主机环境 | ||||||
| @@ -40,19 +37,118 @@ public class HardwareInfo | |||||||
|     /// </summary> |     /// </summary> | ||||||
|     public string OsArchitecture { get; set; } |     public string OsArchitecture { get; set; } | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// 唯一编码 |  | ||||||
|     /// </summary> |  | ||||||
|     public string UUID { get; set; } |  | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary>系统名称</summary> | ||||||
|     /// 进程占用内存 |     public String OSName { get; set; } | ||||||
|     /// </summary> |  | ||||||
|     [AutoGenerateColumn(Ignore = true)] |     /// <summary>系统版本</summary> | ||||||
|     public int WorkingSet { get; set; } |     public String OSVersion { get; set; } | ||||||
|  |     public String UUID { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>内存总量。单位MB</summary> | ||||||
|  |     public UInt64 Memory { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>可用内存。单位MB</summary> | ||||||
|  |     public UInt64 AvailableMemory { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>CPU占用率</summary> | ||||||
|  |     public Double CpuRate { get; set; } | ||||||
|  |     public Double Battery { get; set; } | ||||||
|  |     public Double Temperature { get; set; } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <summary>处理器型号</summary> | ||||||
|  |     public String? Processor { get; set; } | ||||||
|  |     #region GC与进程内存信息 | ||||||
|  |  | ||||||
|  |     /// <summary>GC 认为“内存吃紧”的阈值。单位:MB</summary> | ||||||
|  |     [DisplayName("GC高内存阈值")] | ||||||
|  |     public UInt64 HighMemoryLoadThreshold { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>GC 可用内存上限。单位:MB</summary> | ||||||
|  |     [DisplayName("GC可用内存上限")] | ||||||
|  |     public UInt64 TotalAvailableMemory { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>当前托管堆容量。单位:MB</summary> | ||||||
|  |     [DisplayName("托管堆容量")] | ||||||
|  |     public UInt64 HeapSize { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>托管堆已用内存。单位:MB</summary> | ||||||
|  |     [DisplayName("托管堆已用")] | ||||||
|  |     public UInt64 TotalMemory { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>托管堆碎片大小。单位:MB</summary> | ||||||
|  |     [DisplayName("托管堆碎片")] | ||||||
|  |     public UInt64 FragmentedBytes { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>GC识别可用内存。单位:MB</summary> | ||||||
|  |     [DisplayName("GC识别可用内存")] | ||||||
|  |     public UInt64 GCAvailableMemory { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>GC 已提交的内存。单位:MB</summary> | ||||||
|  |     [DisplayName("GC已提交内存")] | ||||||
|  |     public UInt64 CommittedBytes { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>GC 累计分配的托管内存。单位:MB</summary> | ||||||
|  |     [DisplayName("GC累计分配")] | ||||||
|  |     public UInt64 TotalAllocatedBytes { get; set; } | ||||||
|  |     /// <summary>GC 暂停累计时间。单位:毫秒</summary> | ||||||
|  |     [DisplayName("GC累计暂停时间")] | ||||||
|  |     public UInt64 TotalPauseDurationMs { get; set; } | ||||||
|  |     /// <summary>GC 代0收集次数</summary> | ||||||
|  |     [DisplayName("GC Gen0 次数")] | ||||||
|  |     public Int32 GcGen0Count { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>GC 代1收集次数</summary> | ||||||
|  |     [DisplayName("GC Gen1 次数")] | ||||||
|  |     public Int32 GcGen1Count { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>GC 代2收集次数</summary> | ||||||
|  |     [DisplayName("GC Gen2 次数")] | ||||||
|  |     public Int32 GcGen2Count { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>Server GC 是否启用</summary> | ||||||
|  |     [DisplayName("是否使用Server GC")] | ||||||
|  |     public Boolean IsServerGC { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>GC 延迟模式</summary> | ||||||
|  |     [DisplayName("GC延迟模式")] | ||||||
|  |     public GCLatencyMode? GCLatencyMode { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>GC 固定对象数</summary> | ||||||
|  |     [DisplayName("固定对象数")] | ||||||
|  |     public Int64 PinnedObjectsCount { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>终结队列挂起对象数</summary> | ||||||
|  |     [DisplayName("终结挂起数")] | ||||||
|  |     public Int64 FinalizationPendingCount { get; set; } | ||||||
|  |  | ||||||
|  |     #endregion | ||||||
|  |  | ||||||
|  |     #region 进程内存信息 | ||||||
|  |  | ||||||
|  |     /// <summary>进程虚拟内存使用量。单位:MB</summary> | ||||||
|  |     [DisplayName("虚拟内存")] | ||||||
|  |     public UInt64 VirtualMemory { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>进程私有内存使用量。单位:MB</summary> | ||||||
|  |     [DisplayName("私有内存")] | ||||||
|  |     public UInt64 PrivateMemory { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>进程峰值工作集。单位:MB</summary> | ||||||
|  |     [DisplayName("峰值工作集")] | ||||||
|  |     public UInt64 PeakWorkingSet { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary>进程当前工作集。单位:MB</summary> | ||||||
|  |     [DisplayName("当前工作集")] | ||||||
|  |     public UInt64 WorkingSet { get; set; } | ||||||
|  |  | ||||||
|  |     #endregion | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 更新时间 |     /// 更新时间 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     public string UpdateTime { get; set; } |     public DateTime UpdateTime { get; set; } | ||||||
|  |     public ulong AppRunTotalMinute { get; set; } | ||||||
|  |     public ulong SystemRunTotalMinute { get; set; } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,12 +8,10 @@ | |||||||
| // QQ群:605534569 | // QQ群:605534569 | ||||||
| // ------------------------------------------------------------------------------ | // ------------------------------------------------------------------------------ | ||||||
|  |  | ||||||
| using Microsoft.Extensions.Hosting; |  | ||||||
| using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||||
|  |  | ||||||
| using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||||||
|  |  | ||||||
| using ThingsGateway.Extension; |  | ||||||
| using ThingsGateway.NewLife; | using ThingsGateway.NewLife; | ||||||
| using ThingsGateway.NewLife.Caching; | using ThingsGateway.NewLife.Caching; | ||||||
| using ThingsGateway.NewLife.Threading; | using ThingsGateway.NewLife.Threading; | ||||||
| @@ -43,7 +41,7 @@ public class HardwareJob : IJob, IHardwareJob | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 运行信息获取 |     /// 运行信息获取 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     public HardwareInfo HardwareInfo { get; } = new(); |     public HardwareInfo HardwareInfo { get; private set; } | ||||||
|  |  | ||||||
|     /// <inheritdoc/> |     /// <inheritdoc/> | ||||||
|     public HardwareInfoOptions HardwareInfoOptions { get; private set; } |     public HardwareInfoOptions HardwareInfoOptions { get; private set; } | ||||||
| @@ -76,9 +74,10 @@ public class HardwareJob : IJob, IHardwareJob | |||||||
|         { |         { | ||||||
|             try |             try | ||||||
|             { |             { | ||||||
|                 if (HardwareInfo.MachineInfo == null) |                 var machine = MachineInfo.GetCurrent(); | ||||||
|  |                 if (HardwareInfo == null) | ||||||
|                 { |                 { | ||||||
|                     HardwareInfo.MachineInfo = MachineInfo.GetCurrent(); |                     HardwareInfo = machine.AdaptHardwareInfo(); | ||||||
|  |  | ||||||
|                     string currentPath = Directory.GetCurrentDirectory(); |                     string currentPath = Directory.GetCurrentDirectory(); | ||||||
|                     DriveInfo drive = new(Path.GetPathRoot(currentPath)); |                     DriveInfo drive = new(Path.GetPathRoot(currentPath)); | ||||||
| @@ -88,10 +87,9 @@ public class HardwareJob : IJob, IHardwareJob | |||||||
|                     HardwareInfo.DriveInfo = drive; |                     HardwareInfo.DriveInfo = drive; | ||||||
|                     HardwareInfo.OsArchitecture = Environment.OSVersion.Platform.ToString() + " " + RuntimeInformation.OSArchitecture.ToString(); // 系统架构 |                     HardwareInfo.OsArchitecture = Environment.OSVersion.Platform.ToString() + " " + RuntimeInformation.OSArchitecture.ToString(); // 系统架构 | ||||||
|                     HardwareInfo.FrameworkDescription = RuntimeInformation.FrameworkDescription; // NET框架 |                     HardwareInfo.FrameworkDescription = RuntimeInformation.FrameworkDescription; // NET框架 | ||||||
|                     HardwareInfo.Environment = App.HostEnvironment.IsDevelopment() ? "Development" : "Production"; |                     HardwareInfo.Environment = App.HostEnvironment.EnvironmentName; | ||||||
|                     HardwareInfo.UUID = HardwareInfo.MachineInfo.UUID; |  | ||||||
|  |  | ||||||
|                     HardwareInfo.UpdateTime = TimerX.Now.ToDefaultDateTimeFormat(); |                     HardwareInfo.UpdateTime = TimerX.Now; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             catch |             catch | ||||||
| @@ -99,9 +97,12 @@ public class HardwareJob : IJob, IHardwareJob | |||||||
|             } |             } | ||||||
|             try |             try | ||||||
|             { |             { | ||||||
|                 HardwareInfo.MachineInfo.Refresh(); |                 var machine = MachineInfo.GetCurrent(); | ||||||
|                 HardwareInfo.UpdateTime = TimerX.Now.ToDefaultDateTimeFormat(); |                 machine.Refresh(); | ||||||
|                 HardwareInfo.WorkingSet = (Environment.WorkingSet / 1024.0 / 1024.0).ToInt(); |                 machine.AdaptHardwareInfo(HardwareInfo); | ||||||
|  |                 HardwareInfo.AppRunTotalMinute = (ulong)Runtime.AppTickCount64 / 1000 / 60; | ||||||
|  |                 HardwareInfo.SystemRunTotalMinute = (ulong)Runtime.TickCount64 / 1000 / 60; | ||||||
|  |                 HardwareInfo.UpdateTime = TimerX.Now; | ||||||
|                 error = false; |                 error = false; | ||||||
|             } |             } | ||||||
|             catch (Exception ex) |             catch (Exception ex) | ||||||
| @@ -123,10 +124,10 @@ public class HardwareJob : IJob, IHardwareJob | |||||||
|                             { |                             { | ||||||
|                                 Date = TimerX.Now, |                                 Date = TimerX.Now, | ||||||
|                                 DriveUsage = (100 - (HardwareInfo.DriveInfo.TotalFreeSpace * 100.00 / HardwareInfo.DriveInfo.TotalSize)).ToInt(), |                                 DriveUsage = (100 - (HardwareInfo.DriveInfo.TotalFreeSpace * 100.00 / HardwareInfo.DriveInfo.TotalSize)).ToInt(), | ||||||
|                                 Battery = (HardwareInfo.MachineInfo.Battery * 100).ToInt(), |                                 Battery = (HardwareInfo.Battery * 100).ToInt(), | ||||||
|                                 MemoryUsage = (HardwareInfo.WorkingSet), |                                 MemoryUsage = (HardwareInfo.WorkingSet), | ||||||
|                                 CpuUsage = (HardwareInfo.MachineInfo.CpuRate * 100).ToInt(), |                                 CpuUsage = (HardwareInfo.CpuRate * 100).ToInt(), | ||||||
|                                 Temperature = (HardwareInfo.MachineInfo.Temperature).ToInt(), |                                 Temperature = (HardwareInfo.Temperature).ToInt(), | ||||||
|                             }; |                             }; | ||||||
|                             await _db.InsertableT(his).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false); |                             await _db.InsertableT(his).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false); | ||||||
|                             MemoryCache.Remove(CacheKey); |                             MemoryCache.Remove(CacheKey); | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ public class HistoryHardwareInfo | |||||||
|  |  | ||||||
|     /// <inheritdoc/> |     /// <inheritdoc/> | ||||||
|     [SugarColumn(ColumnDescription = "内存")] |     [SugarColumn(ColumnDescription = "内存")] | ||||||
|     public int MemoryUsage { get; set; } |     public UInt64 MemoryUsage { get; set; } | ||||||
|  |  | ||||||
|     /// <inheritdoc/> |     /// <inheritdoc/> | ||||||
|     [SugarColumn(ColumnDescription = "CPU使用率")] |     [SugarColumn(ColumnDescription = "CPU使用率")] | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ | |||||||
|     "UserNoModule": "This account has not been assigned a module. Please contact the administrator", |     "UserNoModule": "This account has not been assigned a module. Please contact the administrator", | ||||||
|     "UserNull": "User {0} does not exist" |     "UserNull": "User {0} does not exist" | ||||||
|   }, |   }, | ||||||
|    |  | ||||||
|   "ThingsGateway.Admin.Application.BlazorAuthenticationHandler": { |   "ThingsGateway.Admin.Application.BlazorAuthenticationHandler": { | ||||||
|     "UserExpire": "User expired, please login again" |     "UserExpire": "User expired, please login again" | ||||||
|   }, |   }, | ||||||
| @@ -46,12 +46,44 @@ | |||||||
|     "FileTypeError": "Not supported format {0}" |     "FileTypeError": "Not supported format {0}" | ||||||
|   }, |   }, | ||||||
|   "ThingsGateway.Admin.Application.HardwareInfo": { |   "ThingsGateway.Admin.Application.HardwareInfo": { | ||||||
|     "Environment": "HostEnvironment", |     "DriveInfo": "Current Disk Info", | ||||||
|     "FrameworkDescription": ".NETFramework", |     "AppRunTotalMinute": "AppRunTotalMinute(min)", | ||||||
|     "OsArchitecture": "System Architecture", |     "SystemRunTotalMinute": "SystemRunTotalMinute(min)", | ||||||
|     "UpdateTime": "UpdateTime", |     "Environment": "Host Environment", | ||||||
|     "UUID": "UUID" |     "FrameworkDescription": ".NET Framework", | ||||||
|  |     "OsArchitecture": "OS Architecture", | ||||||
|  |     "OSName": "OS Name", | ||||||
|  |     "OSVersion": "OS Version", | ||||||
|  |     "UUID": "UUID", | ||||||
|  |     "Memory": "Total Memory", | ||||||
|  |     "AvailableMemory": "Available Memory", | ||||||
|  |     "CpuRate": "CPU Usage", | ||||||
|  |     "Battery": "Battery Level", | ||||||
|  |     "Temperature": "Temperature", | ||||||
|  |     "Processor": "Processor Model", | ||||||
|  |     "HighMemoryLoadThreshold": "GC High Memory Threshold", | ||||||
|  |     "TotalAvailableMemory": "GC Total Available Memory", | ||||||
|  |     "HeapSize": "Managed Heap Size", | ||||||
|  |     "TotalMemory": "Managed Heap Used", | ||||||
|  |     "FragmentedBytes": "Managed Heap Fragmented", | ||||||
|  |     "GCAvailableMemory": "GC Available Memory", | ||||||
|  |     "CommittedBytes": "GC Committed Bytes", | ||||||
|  |     "TotalAllocatedBytes": "GC Total Allocated (MB)", | ||||||
|  |     "TotalPauseDurationMs": "GC Total Pause Duration (ms)", | ||||||
|  |     "GcGen0Count": "GC Gen0 Count", | ||||||
|  |     "GcGen1Count": "GC Gen1 Count", | ||||||
|  |     "GcGen2Count": "GC Gen2 Count", | ||||||
|  |     "IsServerGC": "Server GC", | ||||||
|  |     "GCLatencyMode": "GC Latency Mode", | ||||||
|  |     "PinnedObjectsCount": "Pinned Objects Count", | ||||||
|  |     "FinalizationPendingCount": "Finalization Pending Count", | ||||||
|  |     "VirtualMemory": "Virtual Memory", | ||||||
|  |     "PrivateMemory": "Private Memory", | ||||||
|  |     "PeakWorkingSet": "Peak Working Set", | ||||||
|  |     "WorkingSet": "Current Working Set", | ||||||
|  |     "UpdateTime": "Update Time" | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   "ThingsGateway.Admin.Application.HardwareJob": { |   "ThingsGateway.Admin.Application.HardwareJob": { | ||||||
|     "GetHardwareInfoFail": "Get Hardwareinfo Fail" |     "GetHardwareInfoFail": "Get Hardwareinfo Fail" | ||||||
|   }, |   }, | ||||||
|   | |||||||
| @@ -46,11 +46,42 @@ | |||||||
|     "FileTypeError": "不支持 {0} 格式" |     "FileTypeError": "不支持 {0} 格式" | ||||||
|   }, |   }, | ||||||
|   "ThingsGateway.Admin.Application.HardwareInfo": { |   "ThingsGateway.Admin.Application.HardwareInfo": { | ||||||
|  |     "DriveInfo": "当前磁盘信息", | ||||||
|  |     "AppRunTotalMinute": "软件运行时长(min)", | ||||||
|  |     "SystemRunTotalMinute": "系统运行时长(min)", | ||||||
|     "Environment": "主机环境", |     "Environment": "主机环境", | ||||||
|     "FrameworkDescription": "NET框架", |     "FrameworkDescription": ".NET 框架", | ||||||
|     "OsArchitecture": "系统架构", |     "OsArchitecture": "系统架构", | ||||||
|     "UpdateTime": "更新时间", |     "OSName": "系统名称", | ||||||
|     "UUID": "唯一编码" |     "OSVersion": "系统版本", | ||||||
|  |     "UUID": "UUID", | ||||||
|  |     "Memory": "系统总内存", | ||||||
|  |     "AvailableMemory": "系统可用内存", | ||||||
|  |     "CpuRate": "CPU 占用率", | ||||||
|  |     "Battery": "电池电量", | ||||||
|  |     "Temperature": "温度", | ||||||
|  |     "Processor": "处理器型号", | ||||||
|  |     "HighMemoryLoadThreshold": "GC 高内存阈值", | ||||||
|  |     "TotalAvailableMemory": "GC 总内存阈值", | ||||||
|  |     "HeapSize": "托管堆容量", | ||||||
|  |     "TotalMemory": "托管对象占用", | ||||||
|  |     "FragmentedBytes": "托管堆碎片", | ||||||
|  |     "GCAvailableMemory": "GC 可用内存", | ||||||
|  |     "CommittedBytes": "GC 提交内存总量", | ||||||
|  |     "TotalAllocatedBytes": "GC 累计分配(MB)", | ||||||
|  |     "TotalPauseDurationMs": "GC 累计暂停时间(ms)", | ||||||
|  |     "GcGen0Count": "GC Gen0 次数", | ||||||
|  |     "GcGen1Count": "GC Gen1 次数", | ||||||
|  |     "GcGen2Count": "GC Gen2 次数", | ||||||
|  |     "IsServerGC": "Server GC", | ||||||
|  |     "GCLatencyMode": "GC 延迟模式", | ||||||
|  |     "PinnedObjectsCount": "固定对象数", | ||||||
|  |     "FinalizationPendingCount": "终结挂起数", | ||||||
|  |     "VirtualMemory": "虚拟内存", | ||||||
|  |     "PrivateMemory": "私有内存", | ||||||
|  |     "PeakWorkingSet": "峰值工作集", | ||||||
|  |     "WorkingSet": "当前工作集", | ||||||
|  |     "UpdateTime": "更新时间" | ||||||
|   }, |   }, | ||||||
|   "ThingsGateway.Admin.Application.HardwareJob": { |   "ThingsGateway.Admin.Application.HardwareJob": { | ||||||
|     "GetHardwareInfoFail": "获取硬件信息出错" |     "GetHardwareInfoFail": "获取硬件信息出错" | ||||||
|   | |||||||
| @@ -10,11 +10,16 @@ | |||||||
|  |  | ||||||
| using Riok.Mapperly.Abstractions; | using Riok.Mapperly.Abstractions; | ||||||
|  |  | ||||||
|  | using ThingsGateway.NewLife; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Admin.Application; | namespace ThingsGateway.Admin.Application; | ||||||
|  |  | ||||||
| [Mapper(UseDeepCloning = true, EnumMappingStrategy = EnumMappingStrategy.ByName, RequiredMappingStrategy = RequiredMappingStrategy.None)] | [Mapper(UseDeepCloning = true, EnumMappingStrategy = EnumMappingStrategy.ByName, RequiredMappingStrategy = RequiredMappingStrategy.None)] | ||||||
| public static partial class AdminMapper | 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 LoginInput AdaptLoginInput(this OpenApiLoginInput src); |     public static partial LoginInput AdaptLoginInput(this OpenApiLoginInput src); | ||||||
|     public static partial OpenApiLoginOutput AdaptOpenApiLoginOutput(this LoginOutput src); |     public static partial OpenApiLoginOutput AdaptOpenApiLoginOutput(this LoginOutput src); | ||||||
|     public static partial SessionOutput AdaptSessionOutput(this SysUser src); |     public static partial SessionOutput AdaptSessionOutput(this SysUser src); | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ namespace ThingsGateway.Admin.Application; | |||||||
| /// <typeparam name="TEntry"></typeparam> | /// <typeparam name="TEntry"></typeparam> | ||||||
| public class EventService<TEntry> : IEventService<TEntry>, IDisposable | public class EventService<TEntry> : IEventService<TEntry>, IDisposable | ||||||
| { | { | ||||||
|     private ConcurrentDictionary<string, Func<TEntry, Task>> Cache = new(); |     private NonBlockingDictionary<string, Func<TEntry, Task>> Cache = new(); | ||||||
|  |  | ||||||
|     public void Dispose() |     public void Dispose() | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -19,12 +19,14 @@ | |||||||
| 	</ItemGroup> | 	</ItemGroup> | ||||||
| 	 | 	 | ||||||
| 	<ItemGroup> | 	<ItemGroup> | ||||||
| 		<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" /> | 		<PackageReference Include="Riok.Mapperly" Version="4.3.0" ExcludeAssets="runtime" PrivateAssets="all"> | ||||||
| 		<PackageReference Include="Rougamo.Fody" Version="5.0.1" /> | 		  <IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||||
|  | 		</PackageReference> | ||||||
|  | 		<PackageReference Include="Rougamo.Fody" Version="5.0.2" /> | ||||||
| 	</ItemGroup> | 	</ItemGroup> | ||||||
| 	<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' "> | 	<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' "> | ||||||
| 		<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" /> | 		<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" /> | ||||||
| 		<PackageReference Include="System.Formats.Asn1" Version="8.0.2" /> | 		<PackageReference Include="System.Formats.Asn1" Version="9.0.10" /> | ||||||
| 		<PackageReference Include="System.Threading.RateLimiting" Version="8.0.0" /> | 		<PackageReference Include="System.Threading.RateLimiting" Version="8.0.0" /> | ||||||
|  |  | ||||||
| 	</ItemGroup> | 	</ItemGroup> | ||||||
|   | |||||||
| @@ -4,8 +4,8 @@ | |||||||
|  |  | ||||||
| <div class="tg-table h-100"> | <div class="tg-table h-100"> | ||||||
|  |  | ||||||
|     <Table TItem="TItem" IsBordered="true" IsStriped="true" TableSize="TableSize.Compact" SelectedRows=SelectedRows SelectedRowsChanged=privateSelectedRowsChanged IsMultipleSelect="IsMultipleSelect" @ref="Instance" SearchTemplate="SearchTemplate" |     <Table Id=@Id TItem="TItem" IsBordered="true" IsStriped="true" TableSize="TableSize.Compact" SelectedRows=SelectedRows SelectedRowsChanged=privateSelectedRowsChanged IsMultipleSelect="IsMultipleSelect" @ref="Instance" SearchTemplate="SearchTemplate" | ||||||
|            DataService="DataService" CreateItemCallback="CreateItemCallback!" |            DataService="DataService" CreateItemCallback="CreateItemCallback!" RenderMode=RenderMode OnColumnCreating=OnColumnCreating | ||||||
|            IsPagination="IsPagination" PageItemsSource="PageItemsSource" IsFixedHeader="IsFixedHeader" IndentSize=24 RowHeight=RowHeight ShowSearchText="ShowSearchText" ShowSearchButton="ShowSearchButton" DisableEditButtonCallback="DisableEditButtonCallback" DisableDeleteButtonCallback="DisableDeleteButtonCallback" BeforeShowEditDialogCallback=" BeforeShowEditDialogCallback!" |            IsPagination="IsPagination" PageItemsSource="PageItemsSource" IsFixedHeader="IsFixedHeader" IndentSize=24 RowHeight=RowHeight ShowSearchText="ShowSearchText" ShowSearchButton="ShowSearchButton" DisableEditButtonCallback="DisableEditButtonCallback" DisableDeleteButtonCallback="DisableDeleteButtonCallback" BeforeShowEditDialogCallback=" BeforeShowEditDialogCallback!" | ||||||
|            IsTree="IsTree" OnTreeExpand="OnTreeExpand!" TreeNodeConverter="TreeNodeConverter!" TreeIcon="fa-solid fa-circle-chevron-right" TreeExpandIcon="fa-solid fa-circle-chevron-right fa-rotate-90" IsAutoQueryFirstRender=IsAutoQueryFirstRender |            IsTree="IsTree" OnTreeExpand="OnTreeExpand!" TreeNodeConverter="TreeNodeConverter!" TreeIcon="fa-solid fa-circle-chevron-right" TreeExpandIcon="fa-solid fa-circle-chevron-right fa-rotate-90" IsAutoQueryFirstRender=IsAutoQueryFirstRender | ||||||
|            ShowDefaultButtons="ShowDefaultButtons" ShowAdvancedSearch="ShowAdvancedSearch" ShowResetButton=ShowResetButton |            ShowDefaultButtons="ShowDefaultButtons" ShowAdvancedSearch="ShowAdvancedSearch" ShowResetButton=ShowResetButton | ||||||
| @@ -14,7 +14,7 @@ | |||||||
|            ShowSkeleton="true" ShowLoading="ShowLoading" ShowSearch="ShowSearch" SearchModel=@SearchModel ShowLineNo |            ShowSkeleton="true" ShowLoading="ShowLoading" ShowSearch="ShowSearch" SearchModel=@SearchModel ShowLineNo | ||||||
|            SearchMode=SearchMode ShowExportPdfButton=ShowExportPdfButton ExportButtonText=@ExportButtonText |            SearchMode=SearchMode ShowExportPdfButton=ShowExportPdfButton ExportButtonText=@ExportButtonText | ||||||
|            ShowExportButton=@ShowExportButton Items=Items ClickToSelect=ClickToSelect ScrollMode=ScrollMode |            ShowExportButton=@ShowExportButton Items=Items ClickToSelect=ClickToSelect ScrollMode=ScrollMode | ||||||
|            ShowExportCsvButton=@ShowExportCsvButton ShowCardView=ShowCardView |            ShowExportCsvButton=@ShowExportCsvButton ShowCardView=ShowCardView OnColumnVisibleChanged=OnColumnVisibleChanged | ||||||
|            FixedExtendButtonsColumn=FixedExtendButtonsColumn FixedMultipleColumn=FixedMultipleColumn FixedDetailRowHeaderColumn=FixedDetailRowHeaderColumn FixedLineNoColumn=FixedLineNoColumn |            FixedExtendButtonsColumn=FixedExtendButtonsColumn FixedMultipleColumn=FixedMultipleColumn FixedDetailRowHeaderColumn=FixedDetailRowHeaderColumn FixedLineNoColumn=FixedLineNoColumn | ||||||
|            IsAutoRefresh=IsAutoRefresh AutoRefreshInterval=AutoRefreshInterval |            IsAutoRefresh=IsAutoRefresh AutoRefreshInterval=AutoRefreshInterval | ||||||
|            AllowDragColumn=@AllowDragColumn Height=@Height ShowRefresh=ShowRefresh |            AllowDragColumn=@AllowDragColumn Height=@Height ShowRefresh=ShowRefresh | ||||||
| @@ -41,6 +41,7 @@ | |||||||
|            DoubleClickToEdit="DoubleClickToEdit" |            DoubleClickToEdit="DoubleClickToEdit" | ||||||
|            OnDoubleClickCellCallback="OnDoubleClickCellCallback" |            OnDoubleClickCellCallback="OnDoubleClickCellCallback" | ||||||
|            OnDoubleClickRowCallback="OnDoubleClickRowCallback" |            OnDoubleClickRowCallback="OnDoubleClickRowCallback" | ||||||
|  |            RowContentTemplate="RowContentTemplate" | ||||||
|            OnClickRowCallback="OnClickRowCallback"> |            OnClickRowCallback="OnClickRowCallback"> | ||||||
|     </Table> |     </Table> | ||||||
| </div> | </div> | ||||||
|   | |||||||
| @@ -13,6 +13,23 @@ namespace ThingsGateway.Admin.Razor; | |||||||
| [CascadingTypeParameter(nameof(TItem))] | [CascadingTypeParameter(nameof(TItem))] | ||||||
| public partial class AdminTable<TItem> where TItem : class, new() | public partial class AdminTable<TItem> where TItem : class, new() | ||||||
| { | { | ||||||
|  |     /// <inheritdoc cref="Table{TItem}.OnColumnVisibleChanged"/> | ||||||
|  |     [Parameter] | ||||||
|  |     public Func<string, bool, Task> OnColumnVisibleChanged { get; set; } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc cref="Table{TItem}.OnColumnCreating"/> | ||||||
|  |     [Parameter] | ||||||
|  |     public Func<List<ITableColumn>, Task> OnColumnCreating { get; set; } | ||||||
|  |     /// <inheritdoc cref="Table{TItem}.RenderMode"/> | ||||||
|  |     [Parameter] | ||||||
|  |     public TableRenderMode RenderMode { get; set; } | ||||||
|  |  | ||||||
|  |     public List<ITableColumn> Columns => Instance?.Columns; | ||||||
|  |  | ||||||
|  |     public IEnumerable<ITableColumn> GetVisibleColumns => Instance?.GetVisibleColumns(); | ||||||
|  |     public List<TItem> Rows => Instance?.Rows; | ||||||
|  |  | ||||||
|  |  | ||||||
|     /// <inheritdoc cref="Table{TItem}.SelectedRowsChanged"/> |     /// <inheritdoc cref="Table{TItem}.SelectedRowsChanged"/> | ||||||
|     [Parameter] |     [Parameter] | ||||||
|     public EventCallback<List<TItem>> SelectedRowsChanged { get; set; } |     public EventCallback<List<TItem>> SelectedRowsChanged { get; set; } | ||||||
| @@ -40,6 +57,10 @@ public partial class AdminTable<TItem> where TItem : class, new() | |||||||
|     /// <inheritdoc cref="Table{TItem}.OnDoubleClickRowCallback"/> |     /// <inheritdoc cref="Table{TItem}.OnDoubleClickRowCallback"/> | ||||||
|     [Parameter] |     [Parameter] | ||||||
|     public Func<TItem, Task>? OnDoubleClickRowCallback { get; set; } |     public Func<TItem, Task>? OnDoubleClickRowCallback { get; set; } | ||||||
|  |     /// <inheritdoc cref="Table{TItem}.RowContentTemplate"/> | ||||||
|  |     [Parameter] | ||||||
|  |     public RenderFragment<TableRowContext<TItem>>? RowContentTemplate { get; set; } | ||||||
|  |  | ||||||
|     /// <inheritdoc cref="Table{TItem}.OnClickRowCallback"/> |     /// <inheritdoc cref="Table{TItem}.OnClickRowCallback"/> | ||||||
|     [Parameter] |     [Parameter] | ||||||
|     public Func<TItem, Task>? OnClickRowCallback { get; set; } |     public Func<TItem, Task>? OnClickRowCallback { get; set; } | ||||||
| @@ -146,6 +167,9 @@ public partial class AdminTable<TItem> where TItem : class, new() | |||||||
|     [Parameter] |     [Parameter] | ||||||
|     public IDataService<TItem> DataService { get; set; } |     public IDataService<TItem> DataService { get; set; } | ||||||
|  |  | ||||||
|  |     [Parameter] | ||||||
|  |     public string? Id { get; set; } | ||||||
|  |  | ||||||
|     /// <inheritdoc cref="Table{TItem}.CreateItemCallback"/> |     /// <inheritdoc cref="Table{TItem}.CreateItemCallback"/> | ||||||
|     [Parameter] |     [Parameter] | ||||||
|     public Func<TItem> CreateItemCallback { get; set; } |     public Func<TItem> CreateItemCallback { get; set; } | ||||||
|   | |||||||
| @@ -22,6 +22,9 @@ | |||||||
|     "SetDefaultModule": "Set as default module" |     "SetDefaultModule": "Set as default module" | ||||||
|   }, |   }, | ||||||
|   "ThingsGateway.Admin.Razor.HardwareInfoPage": { |   "ThingsGateway.Admin.Razor.HardwareInfoPage": { | ||||||
|  |     "GCMemoryInfoConfig": "GCMemoryInfoConfig(MB)", | ||||||
|  |     "GCMemoryInfo": "GCMemoryInfo(MB)", | ||||||
|  |     "GCAnalyzeInfo": "GCAnalyzeInfo", | ||||||
|     "AvailableDisk": "Available Disk", |     "AvailableDisk": "Available Disk", | ||||||
|     "AvailableMemory": "Available Memory", |     "AvailableMemory": "Available Memory", | ||||||
|     "CpuUsage": "CPU Usage", |     "CpuUsage": "CPU Usage", | ||||||
|   | |||||||
| @@ -22,6 +22,10 @@ | |||||||
|     "SetDefaultModule": "设置为默认模块" |     "SetDefaultModule": "设置为默认模块" | ||||||
|   }, |   }, | ||||||
|   "ThingsGateway.Admin.Razor.HardwareInfoPage": { |   "ThingsGateway.Admin.Razor.HardwareInfoPage": { | ||||||
|  |     "GCMemoryInfoConfig": "GC配置信息(MB)", | ||||||
|  |     "GCMemoryInfo": "GC内存信息(MB)", | ||||||
|  |     "GCAnalyzeInfo": "GC统计", | ||||||
|  |  | ||||||
|     "AvailableDisk": "可用磁盘", |     "AvailableDisk": "可用磁盘", | ||||||
|     "AvailableMemory": "可用内存", |     "AvailableMemory": "可用内存", | ||||||
|     "CpuUsage": "CPU使用率", |     "CpuUsage": "CPU使用率", | ||||||
|   | |||||||
| @@ -5,131 +5,189 @@ | |||||||
| @using ThingsGateway.Admin.Application | @using ThingsGateway.Admin.Application | ||||||
| @namespace ThingsGateway.Admin.Razor | @namespace ThingsGateway.Admin.Razor | ||||||
| <div class="row g-2 mx-1 form-inline"> | <div class="row g-2 mx-1 form-inline"> | ||||||
|  |     <div class="col-12 col-md-12"> | ||||||
|     <div class="col-12 col-md-4"> |  | ||||||
|  |  | ||||||
|         <Card IsShadow=true class="m-2 flex-fill" Color="Color.Primary"> |         <Card IsShadow=true class="m-2 flex-fill" Color="Color.Primary"> | ||||||
|             <HeaderTemplate> |             <HeaderTemplate> | ||||||
|                 @Localizer["SystemInfo"] |                 <div class="d-flex justify-content-between align-items-center w-100"> | ||||||
|             </HeaderTemplate> |                     <span>@Localizer["SystemInfo"]</span> | ||||||
|  |                     <small class="text-muted"> | ||||||
|             <BodyTemplate> |                         @HardwareJob.HardwareInfo.UpdateTime.ToString("yyyy-MM-dd HH:mm:ss") | ||||||
|                 <EditorFormObject IsDisplay Model="HardwareJob.HardwareInfo" ItemsPerRow="1" RowType=RowType.Inline LabelWidth="160"> |                     </small> | ||||||
|                     <FieldItems> |  | ||||||
|                         <EditorItem @bind-Field="@context.MachineInfo"> |  | ||||||
|                             <EditTemplate Context="value"> |  | ||||||
|                                 <div class="col-12  col-md-12"> |  | ||||||
|                                     <Display @bind-Value="@value.MachineInfo.OSName" DisplayText="@Localizer[nameof(value.MachineInfo.OSName)]" ShowTooltip ShowLabel=true> |  | ||||||
|                                     </Display> |  | ||||||
|                                 </div> |  | ||||||
|                                 <div class="col-12  col-md-12"> |  | ||||||
|                                     <Display @bind-Value="@value.MachineInfo.OSVersion" DisplayText="@Localizer[nameof(value.MachineInfo.OSVersion)]" ShowTooltip ShowLabel=true> |  | ||||||
|                                     </Display> |  | ||||||
|                                 </div> |  | ||||||
|                             </EditTemplate> |  | ||||||
|                         </EditorItem> |  | ||||||
|  |  | ||||||
|                         <EditorItem @bind-Field="@context.UUID"> |  | ||||||
|                             <EditTemplate Context="value"> |  | ||||||
|                                 <div class="col-12  col-md-12"> |  | ||||||
|                                     <Display @bind-Value="@value.UUID" ShowTooltip ShowLabel=true> |  | ||||||
|                                     </Display> |  | ||||||
|                                 </div> |  | ||||||
|  |  | ||||||
|                             </EditTemplate> |  | ||||||
|                         </EditorItem> |  | ||||||
|                         <EditorItem @bind-Field="@context.DriveInfo" Ignore=true> |  | ||||||
|                         </EditorItem> |  | ||||||
|                     </FieldItems> |  | ||||||
|                 </EditorFormObject> |  | ||||||
|  |  | ||||||
|             </BodyTemplate> |  | ||||||
|         </Card> |  | ||||||
|     </div> |  | ||||||
|     <div class="col-12 col-md-8"> |  | ||||||
|         <Card IsShadow=true class="m-2 flex-fill" Color="Color.Primary"> |  | ||||||
|             <HeaderTemplate> |  | ||||||
|                 @Localizer["HardwareInfo"] |  | ||||||
|             </HeaderTemplate> |  | ||||||
|  |  | ||||||
|             <BodyTemplate> |  | ||||||
|                 <div class="d-flex align-items-center justify-content-center w-100 h-100" > |  | ||||||
|  |  | ||||||
|                     <div class="row g-2 mx-1 form-inline d-flex justify-content-center w-100"> |  | ||||||
|                         <div class="col-12 col-md-4  justify-content-center h-100" > |  | ||||||
|                             @{ |  | ||||||
|                                 var item = HardwareJob.HardwareInfo.MachineInfo.CpuRate; |  | ||||||
|                             } |  | ||||||
|                             <div class="d-flex flex-column align-items-center"> |  | ||||||
|                                 <Circle Width="200" class="m-3" |  | ||||||
|                                         Value=@((int)(item*100<=100?item*100:100)) |  | ||||||
|                                         Color=@((item*100>70?Color.Warning:Color.Success)) |  | ||||||
|                                         StrokeWidth="4" ShowProgress=false> |  | ||||||
|                                     <div class="circle-hardware"> |  | ||||||
|                                         <span> |  | ||||||
|                                             @Localizer["CpuUsage"] <i> @((item * 100).ToString("F0") + " %")</i> |  | ||||||
|                                         </span> |  | ||||||
|                                     </div> |  | ||||||
|                                 </Circle> |  | ||||||
|                                 <div class="mt-1"> |  | ||||||
|                                     <span>@(HardwareJob.HardwareInfo.MachineInfo.Processor)</span> |  | ||||||
|                                 </div> |  | ||||||
|                             </div> |  | ||||||
|                         </div> |  | ||||||
|                         <div class="col-12 col-md-4 justify-content-center h-100"> |  | ||||||
|                             @{ |  | ||||||
|                                 var availableMemory = HardwareJob.HardwareInfo.MachineInfo.AvailableMemory; |  | ||||||
|                                 var memory = HardwareJob.HardwareInfo.MachineInfo.Memory; |  | ||||||
|                             } |  | ||||||
|                             <div class="d-flex flex-column align-items-center "> |  | ||||||
|                                 <Circle Width="200" class="m-3" |  | ||||||
|                                         Value=@((int)(memory>availableMemory?100 - (availableMemory * 100.00 / memory):100)) |  | ||||||
|                                         Color=@((availableMemory * 100.00 / memory<20?Color.Warning:Color.Success)) |  | ||||||
|                                         StrokeWidth="4"> |  | ||||||
|                                     <div class="circle-hardware"> |  | ||||||
|                                         <h6>   @((100 - (availableMemory * 100.00 / memory)).ToString("F2") + " %")  </h6> |  | ||||||
|  |  | ||||||
|                                         <span>   @Localizer["WorkingSet"] <i> @(HardwareJob.HardwareInfo.WorkingSet + " MB")</i></span> |  | ||||||
|                                         <span>   @Localizer["AvailableMemory"] <i> @((availableMemory / 1024.00 / 1024).ToString("F2") + " GB")</i></span> |  | ||||||
|                                         <span>   @Localizer["TotalMemory"] <i> @((memory / 1024.00 / 1024).ToString("F2") + " GB")</i></span> |  | ||||||
|  |  | ||||||
|                                     </div> |  | ||||||
|                                 </Circle> |  | ||||||
|                                 <div class="mt-1"> |  | ||||||
|                                     <span>  @Localizer["MemoryUsage"] </span> |  | ||||||
|                                 </div> |  | ||||||
|                             </div> |  | ||||||
|                         </div> |  | ||||||
|                         <div class="col-12 col-md-4 justify-content-center h-100"> |  | ||||||
|                             @{ |  | ||||||
|                                 var totalFreeSpace = HardwareJob.HardwareInfo.DriveInfo.TotalFreeSpace; |  | ||||||
|                                 var totalSize = HardwareJob.HardwareInfo.DriveInfo.TotalSize; |  | ||||||
|                             } |  | ||||||
|                             <div class="d-flex flex-column align-items-center "> |  | ||||||
|                                 <Circle Width="200" class="m-3" |  | ||||||
|                                         Value=@((int)(totalSize>totalFreeSpace?100 - (totalFreeSpace * 100.00 / totalSize):100)) |  | ||||||
|                                         Color=@((totalFreeSpace * 100.00 / totalSize<20?Color.Warning:Color.Success)) |  | ||||||
|                                         StrokeWidth="4"> |  | ||||||
|                                     <div class="circle-hardware"> |  | ||||||
|                                         <h6>   @((100 - (totalFreeSpace * 100.00 / totalSize)).ToString("F2") + " %")</h6> |  | ||||||
|                                         <span>   @(HardwareJob.HardwareInfo.DriveInfo.VolumeLabel) </span> |  | ||||||
|                                         <span>   @Localizer["AvailableDisk"] <i> @((totalFreeSpace / 1024.00 / 1024 / 1024).ToString("F2") + " GB")</i></span> |  | ||||||
|                                         <span>   @Localizer["TotalDisk"] <i> @((totalSize / 1024.00 / 1024 / 1024).ToString("F2") + " GB")</i></span> |  | ||||||
|                                     </div> |  | ||||||
|                                 </Circle> |  | ||||||
|                                 <div class="mt-1"> |  | ||||||
|                                     <span>  @Localizer["DiskUsage"] </span> |  | ||||||
|                                 </div> |  | ||||||
|                             </div> |  | ||||||
|                         </div> |  | ||||||
|                     </div> |  | ||||||
|                 </div> |                 </div> | ||||||
|  |             </HeaderTemplate> | ||||||
|  |  | ||||||
|  |             <BodyTemplate> | ||||||
|  |                 <EditorForm class="mt-3" AutoGenerateAllItem="false" IsDisplay Model="HardwareJob.HardwareInfo" ItemsPerRow=3 RowType=RowType.Inline LabelWidth=300> | ||||||
|  |                     <FieldItems> | ||||||
|  |  | ||||||
|  |                         <EditorItem @bind-Field="@context.OSName" GroupName="@Localizer["SystemInfo"]" /> | ||||||
|  |                         <EditorItem @bind-Field="@context.OsArchitecture" GroupName="@Localizer["SystemInfo"]" /> | ||||||
|  |                         <EditorItem @bind-Field="@context.OSVersion" GroupName="@Localizer["SystemInfo"]" /> | ||||||
|  |                         <EditorItem @bind-Field="@context.Environment" GroupName="@Localizer["SystemInfo"]" /> | ||||||
|  |                         <EditorItem @bind-Field="@context.FrameworkDescription" GroupName="@Localizer["SystemInfo"]" /> | ||||||
|  |                         <EditorItem @bind-Field="@context.UUID" GroupName="@Localizer["SystemInfo"]" /> | ||||||
|  |                         <EditorItem @bind-Field="@context.SystemRunTotalMinute" GroupName="@Localizer["SystemInfo"]" /> | ||||||
|  |                         <EditorItem @bind-Field="@context.AppRunTotalMinute" GroupName="@Localizer["SystemInfo"]" /> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |                         <EditorItem @bind-Field="@context.Memory" GroupName="@Localizer["GCMemoryInfoConfig"]" GroupOrder=2 /> | ||||||
|  |                         <EditorItem @bind-Field="@context.TotalAvailableMemory" GroupName="@Localizer["GCMemoryInfoConfig"]" GroupOrder=2 /> | ||||||
|  |                         <EditorItem @bind-Field="@context.HighMemoryLoadThreshold" GroupName="@Localizer["GCMemoryConfig"]" GroupOrder=2 /> | ||||||
|  |  | ||||||
|  |                         <EditorItem TValue="bool" TModel="HardwareInfo" @bind-Field="@context.IsServerGC" GroupName="@Localizer["GCMemoryConfig"]" GroupOrder=2> | ||||||
|  |                             <EditTemplate Context="value"> | ||||||
|  |                                 <div class="col-12"> | ||||||
|  |                                     <h6></h6> | ||||||
|  |                                 </div> | ||||||
|  |                             </EditTemplate> | ||||||
|  |                         </EditorItem> | ||||||
|  |  | ||||||
|  |                         <EditorItem @bind-Field="@context.IsServerGC" GroupName="@Localizer["GCMemoryInfoConfig"]" GroupOrder=2 /> | ||||||
|  |                         <EditorItem @bind-Field="@context.GCLatencyMode" GroupName="@Localizer["GCMemoryInfoConfig"]" GroupOrder=2 /> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |                         <EditorItem @bind-Field="@context.WorkingSet" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 /> | ||||||
|  |                         <EditorItem @bind-Field="@context.PrivateMemory" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 /> | ||||||
|  |                         <EditorItem @bind-Field="@context.PeakWorkingSet" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 /> | ||||||
|  |                         <EditorItem TValue="ulong" TModel="HardwareInfo" @bind-Field="@context.HeapSize" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3> | ||||||
|  |                             <EditTemplate Context="value"> | ||||||
|  |                                 <div class="col-12"> | ||||||
|  |                                     <h6></h6> | ||||||
|  |                                 </div> | ||||||
|  |                             </EditTemplate> | ||||||
|  |                         </EditorItem> | ||||||
|  |                         <EditorItem @bind-Field="@context.HeapSize" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 /> | ||||||
|  |                         <EditorItem @bind-Field="@context.TotalMemory" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 /> | ||||||
|  |                         <EditorItem @bind-Field="@context.FragmentedBytes" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 /> | ||||||
|  |                         <EditorItem @bind-Field="@context.CommittedBytes" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 /> | ||||||
|  |  | ||||||
|  |                         <EditorItem TValue="ulong" TModel="HardwareInfo" @bind-Field="@context.AvailableMemory" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3> | ||||||
|  |                             <EditTemplate Context="value"> | ||||||
|  |                                 <div class="col-12"> | ||||||
|  |                                     <h6></h6> | ||||||
|  |                                 </div> | ||||||
|  |                             </EditTemplate> | ||||||
|  |                         </EditorItem> | ||||||
|  |  | ||||||
|  |                         <EditorItem @bind-Field="@context.AvailableMemory" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 /> | ||||||
|  |                         <EditorItem @bind-Field="@context.GCAvailableMemory" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 /> | ||||||
|  |                         <EditorItem @bind-Field="@context.VirtualMemory" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 /> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |                         <EditorItem @bind-Field="@context.GcGen0Count" GroupName="@Localizer["GCAnalyzeInfo"]" GroupOrder=4 /> | ||||||
|  |                         <EditorItem @bind-Field="@context.GcGen1Count" GroupName="@Localizer["GCAnalyzeInfo"]" GroupOrder=4 /> | ||||||
|  |                         <EditorItem @bind-Field="@context.GcGen2Count" GroupName="@Localizer["GCAnalyzeInfo"]" GroupOrder=4 /> | ||||||
|  |                         <EditorItem TValue="ulong" TModel="HardwareInfo" @bind-Field="@context.TotalAllocatedBytes" GroupName="@Localizer["GCAnalyzeInfo"]" GroupOrder=4> | ||||||
|  |                             <EditTemplate Context="value"> | ||||||
|  |                                 <div class="col-12"> | ||||||
|  |                                     <h6></h6> | ||||||
|  |                                 </div> | ||||||
|  |                             </EditTemplate> | ||||||
|  |                         </EditorItem> | ||||||
|  |  | ||||||
|  |                         <EditorItem @bind-Field="@context.TotalAllocatedBytes" GroupName="@Localizer["GCAnalyzeInfo"]" GroupOrder=4 /> | ||||||
|  |                         <EditorItem @bind-Field="@context.TotalPauseDurationMs" GroupName="@Localizer["GCAnalyzeInfo"]" GroupOrder=4 /> | ||||||
|  |  | ||||||
|  |                     </FieldItems> | ||||||
|  |                 </EditorForm> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|             </BodyTemplate> |             </BodyTemplate> | ||||||
|         </Card> |         </Card> | ||||||
|  |  | ||||||
|     </div> |     </div> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|  | <div class="row g-2 mx-1 form-inline mt-2"> | ||||||
|  |  | ||||||
|  |     <div class="col-12 col-md-12"> | ||||||
|  |         <Card IsShadow=true class="m-2 flex-fill" Color="Color.Primary"> | ||||||
|  |             <HeaderTemplate> | ||||||
|  |                 @Localizer["HardwareInfo"] | ||||||
|  |             </HeaderTemplate> | ||||||
|  |  | ||||||
|  |             <BodyTemplate> | ||||||
|  |                 <div class="d-flex align-items-center justify-content-center w-100 h-100"> | ||||||
|  |                     <div class="row g-2 mx-1 form-inline d-flex justify-content-center w-100"> | ||||||
|  |                         <!-- ✅ CPU 使用率 --> | ||||||
|  |                         <div class="col-12 col-md-4 justify-content-center h-100"> | ||||||
|  |                             @{ | ||||||
|  |                                 var cpuUsage = HardwareJob.HardwareInfo.CpuRate; | ||||||
|  |                             } | ||||||
|  |                             <div class="d-flex flex-column align-items-center"> | ||||||
|  |                                 <Circle Width="200" class="m-3" | ||||||
|  |                                         Value=@((int)(cpuUsage*100<=100?cpuUsage*100:100)) | ||||||
|  |                                         Color=@((cpuUsage*100>70?Color.Warning:Color.Success)) | ||||||
|  |                                         StrokeWidth="4" ShowProgress=false> | ||||||
|  |                                     <div class="circle-hardware"> | ||||||
|  |                                         <span> | ||||||
|  |                                             @Localizer["CpuUsage"] <i>@((cpuUsage * 100).ToString("F0") + " %")</i> | ||||||
|  |                                         </span> | ||||||
|  |                                     </div> | ||||||
|  |                                 </Circle> | ||||||
|  |                                 <div class="mt-1"> | ||||||
|  |                                     <span>@(HardwareJob.HardwareInfo.Processor)</span> | ||||||
|  |                                 </div> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |  | ||||||
|  |                         <!-- ✅ 内存使用率 --> | ||||||
|  |                         <div class="col-12 col-md-4 justify-content-center h-100"> | ||||||
|  |                             @{ | ||||||
|  |                                 var availableMemory = HardwareJob.HardwareInfo.AvailableMemory; | ||||||
|  |                                 var memory = HardwareJob.HardwareInfo.Memory; | ||||||
|  |                             } | ||||||
|  |                             <div class="d-flex flex-column align-items-center"> | ||||||
|  |                                 <Circle Width="200" class="m-3" | ||||||
|  |                                         Value=@((int)(memory>availableMemory?100 - (availableMemory * 100.00 / memory):100)) | ||||||
|  |                                         Color=@((availableMemory * 100.00 / memory<20?Color.Warning:Color.Success)) | ||||||
|  |                                         StrokeWidth="4"> | ||||||
|  |                                     <div class="circle-hardware"> | ||||||
|  |                                         <h6>@((100 - (availableMemory * 100.00 / memory)).ToString("F2") + " %")</h6> | ||||||
|  |                                         <span>@Localizer["WorkingSet"] <i>@(HardwareJob.HardwareInfo.WorkingSet + " MB")</i></span> | ||||||
|  |                                         <span>@Localizer["AvailableMemory"] <i>@((availableMemory / 1024.00).ToString("F2") + " GB")</i></span> | ||||||
|  |                                         <span>@Localizer["TotalMemory"] <i>@((memory / 1024.00).ToString("F2") + " GB")</i></span> | ||||||
|  |                                     </div> | ||||||
|  |                                 </Circle> | ||||||
|  |                                 <div class="mt-1"> | ||||||
|  |                                     <span>@Localizer["MemoryUsage"]</span> | ||||||
|  |                                 </div> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |  | ||||||
|  |                         <!-- ✅ 磁盘使用率 --> | ||||||
|  |                         <div class="col-12 col-md-4 justify-content-center h-100"> | ||||||
|  |                             @{ | ||||||
|  |                                 var totalFreeSpace = HardwareJob.HardwareInfo.DriveInfo.TotalFreeSpace; | ||||||
|  |                                 var totalSize = HardwareJob.HardwareInfo.DriveInfo.TotalSize; | ||||||
|  |                             } | ||||||
|  |                             <div class="d-flex flex-column align-items-center"> | ||||||
|  |                                 <Circle Width="200" class="m-3" | ||||||
|  |                                         Value=@((int)(totalSize>totalFreeSpace?100 - (totalFreeSpace * 100.00 / totalSize):100)) | ||||||
|  |                                         Color=@((totalFreeSpace * 100.00 / totalSize<20?Color.Warning:Color.Success)) | ||||||
|  |                                         StrokeWidth="4"> | ||||||
|  |                                     <div class="circle-hardware"> | ||||||
|  |                                         <h6>@((100 - (totalFreeSpace * 100.00 / totalSize)).ToString("F2") + " %")</h6> | ||||||
|  |                                         <span>@(HardwareJob.HardwareInfo.DriveInfo.VolumeLabel)</span> | ||||||
|  |                                         <span>@Localizer["AvailableDisk"] <i>@((totalFreeSpace / 1024.00 / 1024 / 1024).ToString("F2") + " GB")</i></span> | ||||||
|  |                                         <span>@Localizer["TotalDisk"] <i>@((totalSize / 1024.00 / 1024 / 1024).ToString("F2") + " GB")</i></span> | ||||||
|  |                                     </div> | ||||||
|  |                                 </Circle> | ||||||
|  |                                 <div class="mt-1"> | ||||||
|  |                                     <span>@Localizer["DiskUsage"]</span> | ||||||
|  |                                 </div> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </BodyTemplate> | ||||||
|  |         </Card> | ||||||
|  |     </div> | ||||||
|  | </div> | ||||||
|  |  | ||||||
|  |  | ||||||
| <div class="row g-2 mx-1 form-inline"> | <div class="row g-2 mx-1 form-inline"> | ||||||
|  |  | ||||||
|     <div class="col-12 col-md-12"> |     <div class="col-12 col-md-12"> | ||||||
| @@ -140,7 +198,7 @@ | |||||||
|             </HeaderTemplate> |             </HeaderTemplate> | ||||||
|  |  | ||||||
|             <BodyTemplate> |             <BodyTemplate> | ||||||
|                 <Chart @ref=LineChart OnInitAsync="OnInit" Height="var(--line-chart-height)" Width="100%" OnAfterInitAsync="()=>{chartInit=true;return Task.CompletedTask;}" /> |                 <Chart @ref=LineChart OnInitAsync="OnInit" Height="var(--line-chart-height)" Width="100%" OnAfterInitAsync="() => { chartInit = true; return Task.CompletedTask; }" /> | ||||||
|             </BodyTemplate> |             </BodyTemplate> | ||||||
|         </Card> |         </Card> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
| @@ -72,7 +72,7 @@ public partial class HardwareInfoPage : IDisposable | |||||||
|             ChartDataSource.Options.Title = Localizer[nameof(HistoryHardwareInfo)]; |             ChartDataSource.Options.Title = Localizer[nameof(HistoryHardwareInfo)]; | ||||||
|             ChartDataSource.Options.X.Title = Localizer["DateTime"]; |             ChartDataSource.Options.X.Title = Localizer["DateTime"]; | ||||||
|             ChartDataSource.Options.Y.Title = Localizer["Data"]; |             ChartDataSource.Options.Y.Title = Localizer["Data"]; | ||||||
|             ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd HH:mm zz")); |             ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd-HH:mm")); | ||||||
|             ChartDataSource.Data.Add(new ChartDataset() |             ChartDataSource.Data.Add(new ChartDataset() | ||||||
|             { |             { | ||||||
|                 Tension = 0.4f, |                 Tension = 0.4f, | ||||||
| @@ -116,7 +116,7 @@ public partial class HardwareInfoPage : IDisposable | |||||||
|         else |         else | ||||||
|         { |         { | ||||||
|             var hisHardwareInfos = await HardwareJob.GetHistoryHardwareInfos(); |             var hisHardwareInfos = await HardwareJob.GetHistoryHardwareInfos(); | ||||||
|             ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd HH:mm zz")); |             ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd-HH:mm")); | ||||||
|             ChartDataSource.Data[0].Data = hisHardwareInfos.Select(a => (object)a.CpuUsage); |             ChartDataSource.Data[0].Data = hisHardwareInfos.Select(a => (object)a.CpuUsage); | ||||||
|             ChartDataSource.Data[1].Data = hisHardwareInfos.Select(a => (object)a.MemoryUsage); |             ChartDataSource.Data[1].Data = hisHardwareInfos.Select(a => (object)a.MemoryUsage); | ||||||
|             ChartDataSource.Data[2].Data = hisHardwareInfos.Select(a => (object)a.DriveUsage); |             ChartDataSource.Data[2].Data = hisHardwareInfos.Select(a => (object)a.DriveUsage); | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
|  |  | ||||||
| 	<ItemGroup> | 	<ItemGroup> | ||||||
| 		<ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" /> | 		<ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" /> | ||||||
| 		<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.1" /> | 		<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.4" /> | ||||||
| 		<PackageReference Include="BootstrapBlazor.UniverSheet" Version="9.0.5" /> | 		<PackageReference Include="BootstrapBlazor.UniverSheet" Version="9.0.5" /> | ||||||
| 		<PackageReference Include="BootstrapBlazor.WinBox" Version="9.0.7" /> | 		<PackageReference Include="BootstrapBlazor.WinBox" Version="9.0.7" /> | ||||||
| 		<PackageReference Include="BootstrapBlazor.CodeEditor" Version="9.0.3" /> | 		<PackageReference Include="BootstrapBlazor.CodeEditor" Version="9.0.3" /> | ||||||
|   | |||||||
| @@ -87,42 +87,45 @@ public class Startup : AppStartup | |||||||
| #if NET8_0_OR_GREATER | #if NET8_0_OR_GREATER | ||||||
|         services |         services | ||||||
|          .AddRazorComponents(options => options.TemporaryRedirectionUrlValidityDuration = TimeSpan.FromMinutes(10)) |          .AddRazorComponents(options => options.TemporaryRedirectionUrlValidityDuration = TimeSpan.FromMinutes(10)) | ||||||
|          .AddInteractiveServerComponents(options => |             .AddInteractiveServerComponents(options => | ||||||
|          { |             { | ||||||
|              options.RootComponents.MaxJSRootComponents = 500; |                 options.RootComponents.MaxJSRootComponents = 500; | ||||||
|              options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2); |                 options.JSInteropDefaultCallTimeout = TimeSpan.FromSeconds(30); | ||||||
|              options.MaxBufferedUnacknowledgedRenderBatches = 20; |                 options.MaxBufferedUnacknowledgedRenderBatches = 5; | ||||||
|              options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(10); |                 options.DisconnectedCircuitMaxRetained = 1; | ||||||
|          }) |                 options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10); | ||||||
|          .AddHubOptions(options => |             }) | ||||||
|          { |             .AddHubOptions(options => | ||||||
|              //单个传入集线器消息的最大大小。默认 32 KB |             { | ||||||
|              options.MaximumReceiveMessageSize = 32 * 1024 * 1024; |                 //单个传入集线器消息的最大大小。默认 32 KB | ||||||
|              //可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。 |                 options.MaximumReceiveMessageSize = 32 * 1024 * 1024; | ||||||
|              options.StreamBufferCapacity = 30; |                 //可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。 | ||||||
|              options.ClientTimeoutInterval = TimeSpan.FromMinutes(2); |                 options.StreamBufferCapacity = 30; | ||||||
|              options.KeepAliveInterval = TimeSpan.FromSeconds(15); |                 options.ClientTimeoutInterval = TimeSpan.FromSeconds(30); | ||||||
|              options.HandshakeTimeout = TimeSpan.FromSeconds(30); |                 options.KeepAliveInterval = TimeSpan.FromSeconds(15); | ||||||
|          }); |                 options.HandshakeTimeout = TimeSpan.FromSeconds(15); | ||||||
|  |             }); | ||||||
|  |  | ||||||
| #else | #else | ||||||
|  |  | ||||||
|         services.AddServerSideBlazor(options => |         services.AddServerSideBlazor(options => | ||||||
|         { |              { | ||||||
|             options.RootComponents.MaxJSRootComponents = 500; |                  options.RootComponents.MaxJSRootComponents = 500; | ||||||
|             options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2); |                  options.JSInteropDefaultCallTimeout = TimeSpan.FromSeconds(30); | ||||||
|             options.MaxBufferedUnacknowledgedRenderBatches = 20; |                  options.MaxBufferedUnacknowledgedRenderBatches = 5; | ||||||
|             options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(10); |                  options.DisconnectedCircuitMaxRetained = 1; | ||||||
|         }).AddHubOptions(options => |                  options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10); | ||||||
|         { |              }) | ||||||
|             //单个传入集线器消息的最大大小。默认 32 KB |              .AddHubOptions(options => | ||||||
|             options.MaximumReceiveMessageSize = 32 * 1024 * 1024; |              { | ||||||
|             //可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。 |                  //单个传入集线器消息的最大大小。默认 32 KB | ||||||
|             options.StreamBufferCapacity = 30; |                  options.MaximumReceiveMessageSize = 32 * 1024 * 1024; | ||||||
|             options.ClientTimeoutInterval = TimeSpan.FromMinutes(2); |                  //可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。 | ||||||
|             options.KeepAliveInterval = TimeSpan.FromSeconds(15); |                  options.StreamBufferCapacity = 30; | ||||||
|             options.HandshakeTimeout = TimeSpan.FromSeconds(30); |                  options.ClientTimeoutInterval = TimeSpan.FromSeconds(30); | ||||||
|         }); |                  options.KeepAliveInterval = TimeSpan.FromSeconds(15); | ||||||
|  |                  options.HandshakeTimeout = TimeSpan.FromSeconds(15); | ||||||
|  |              }); | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   | |||||||
| @@ -15,15 +15,11 @@ | |||||||
| 		<PublishReadyToRunComposite>true</PublishReadyToRunComposite> | 		<PublishReadyToRunComposite>true</PublishReadyToRunComposite> | ||||||
| 		<ApplicationIcon>wwwroot\favicon.ico</ApplicationIcon> | 		<ApplicationIcon>wwwroot\favicon.ico</ApplicationIcon> | ||||||
|  |  | ||||||
|  | 		<CETCompat>false</CETCompat> | ||||||
|  | 		<ServerGarbageCollection>true</ServerGarbageCollection> | ||||||
| 		<!--动态适用GC--> | 		<!--动态适用GC--> | ||||||
| 		<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode> | 		<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode> | ||||||
| 		<CETCompat>false</CETCompat> | 		 | ||||||
| 		<!--使用自托管线程池--> |  | ||||||
| 		<!--<UseWindowsThreadPool>false</UseWindowsThreadPool> --> |  | ||||||
|  |  | ||||||
| 		<!--使用工作站GC--> |  | ||||||
| 		<!--<ServerGarbageCollection>true</ServerGarbageCollection>--> |  | ||||||
|  |  | ||||||
| 		<!--<PlatformTarget>x86</PlatformTarget>--> | 		<!--<PlatformTarget>x86</PlatformTarget>--> | ||||||
| 	</PropertyGroup> | 	</PropertyGroup> | ||||||
|  |  | ||||||
|   | |||||||
| 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 | 
| @@ -1,4 +1,4 @@ | |||||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
| @@ -477,7 +477,7 @@ public class ConcurrentList<T> : IList<T>, IReadOnlyList<T> | |||||||
|     { |     { | ||||||
|         lock (((ICollection)m_list).SyncRoot) |         lock (((ICollection)m_list).SyncRoot) | ||||||
|         { |         { | ||||||
|             return m_list.IndexOf(item); |             return m_list.LastIndexOf(item); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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; | using ThingsGateway.Common.Extension; | ||||||
| 
 | 
 | ||||||
| namespace ThingsGateway.DB; | namespace ThingsGateway.Common; | ||||||
| 
 | 
 | ||||||
| /// <summary> | /// <summary> | ||||||
| /// 导出excel扩展 | /// 导出excel扩展 | ||||||
| @@ -11,7 +11,7 @@ | |||||||
| using Microsoft.AspNetCore.Components.Forms; | using Microsoft.AspNetCore.Components.Forms; | ||||||
| using Microsoft.AspNetCore.Http; | using Microsoft.AspNetCore.Http; | ||||||
| 
 | 
 | ||||||
| namespace ThingsGateway.DB; | namespace ThingsGateway.Common; | ||||||
| 
 | 
 | ||||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||||
| @@ -16,6 +16,8 @@ using System.Runtime.CompilerServices; | |||||||
| using System.Text.Json; | using System.Text.Json; | ||||||
| using System.Text.RegularExpressions; | using System.Text.RegularExpressions; | ||||||
|  |  | ||||||
|  | using ThingsGateway.Extension; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Common.Extension; | namespace ThingsGateway.Common.Extension; | ||||||
| /// <summary> | /// <summary> | ||||||
| /// 对象拓展类 | /// 对象拓展类 | ||||||
| @@ -48,113 +50,7 @@ public static class ObjectExtensions | |||||||
|         bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type); |         bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// 将 DateTimeOffset 转换成本地 DateTime |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="dateTime"></param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     public static DateTime ConvertToDateTime(this DateTimeOffset dateTime) |  | ||||||
|     { |  | ||||||
|         if (dateTime.Offset.Equals(TimeSpan.Zero)) |  | ||||||
|             return dateTime.UtcDateTime; |  | ||||||
|         if (dateTime.Offset.Equals(TimeZoneInfo.Local.GetUtcOffset(dateTime.DateTime))) |  | ||||||
|             return dateTime.ToLocalTime().DateTime; |  | ||||||
|         else |  | ||||||
|             return dateTime.DateTime; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// 将 DateTimeOffset? 转换成本地 DateTime? |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="dateTime"></param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     public static DateTime? ConvertToDateTime(this DateTimeOffset? dateTime) |  | ||||||
|     { |  | ||||||
|         return dateTime.HasValue ? dateTime.Value.ConvertToDateTime() : null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// 将 DateTime 转换成 DateTimeOffset |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="dateTime"></param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     public static DateTimeOffset ConvertToDateTimeOffset(this DateTime dateTime) |  | ||||||
|     { |  | ||||||
|         return DateTime.SpecifyKind(dateTime, DateTimeKind.Local); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// 将 DateTime? 转换成 DateTimeOffset? |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="dateTime"></param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     public static DateTimeOffset? ConvertToDateTimeOffset(this DateTime? dateTime) |  | ||||||
|     { |  | ||||||
|         return dateTime.HasValue ? dateTime.Value.ConvertToDateTimeOffset() : null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// 将流保存到本地磁盘 |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="stream"></param> |  | ||||||
|     /// <param name="path"></param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     public static void CopyToSave(this Stream stream, string path) |  | ||||||
|     { |  | ||||||
|         // 空检查 |  | ||||||
|         if (string.IsNullOrWhiteSpace(path)) throw new ArgumentNullException(nameof(path)); |  | ||||||
|  |  | ||||||
|         using var fileStream = File.Create(path); |  | ||||||
|         stream.CopyTo(fileStream); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// 将字节数组保存到本地磁盘 |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="bytes"></param> |  | ||||||
|     /// <param name="path"></param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     public static void CopyToSave(this byte[] bytes, string path) |  | ||||||
|     { |  | ||||||
|         using var stream = new MemoryStream(bytes); |  | ||||||
|         stream.CopyToSave(path); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// 将流保存到本地磁盘 |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="stream"></param> |  | ||||||
|     /// <param name="path">需包含文件名完整路径</param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     public static async Task CopyToSaveAsync(this Stream stream, string path) |  | ||||||
|     { |  | ||||||
|         // 空检查 |  | ||||||
|         if (string.IsNullOrWhiteSpace(path)) |  | ||||||
|         { |  | ||||||
|             throw new ArgumentNullException(nameof(path)); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // 文件名判断 |  | ||||||
|         if (string.IsNullOrWhiteSpace(Path.GetFileName(path))) |  | ||||||
|         { |  | ||||||
|             throw new ArgumentException("The parameter of <path> parameter must include the complete file name."); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         using var fileStream = File.Create(path); |  | ||||||
|         await stream.CopyToAsync(fileStream).ConfigureAwait(false); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// 将字节数组保存到本地磁盘 |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="bytes"></param> |  | ||||||
|     /// <param name="path"></param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     public static async Task CopyToSaveAsync(this byte[] bytes, string path) |  | ||||||
|     { |  | ||||||
|         using var stream = new MemoryStream(bytes); |  | ||||||
|         await stream.CopyToSaveAsync(path).ConfigureAwait(false); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 合并两个字典 |     /// 合并两个字典 | ||||||
| @@ -186,7 +82,7 @@ public static class ObjectExtensions | |||||||
|     /// <typeparam name="T"></typeparam> |     /// <typeparam name="T"></typeparam> | ||||||
|     /// <param name="dic">字典</param> |     /// <param name="dic">字典</param> | ||||||
|     /// <param name="newDic">新字典</param> |     /// <param name="newDic">新字典</param> | ||||||
|     internal static void AddOrUpdate<T>(this ConcurrentDictionary<string, T> dic, Dictionary<string, T> newDic) |     internal static void AddOrUpdate<T>(this NonBlockingDictionary<string, T> dic, Dictionary<string, T> newDic) | ||||||
|     { |     { | ||||||
|         foreach (var (key, value) in newDic) |         foreach (var (key, value) in newDic) | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ using System.ComponentModel.DataAnnotations; | |||||||
|  |  | ||||||
| using ThingsGateway.Common.Extension; | using ThingsGateway.Common.Extension; | ||||||
|  |  | ||||||
|  |  | ||||||
| #if NET8_0_OR_GREATER | #if NET8_0_OR_GREATER | ||||||
| using System.Collections.Frozen; | using System.Collections.Frozen; | ||||||
| #endif | #endif | ||||||
| @@ -27,7 +28,7 @@ internal class CacheManager | |||||||
| { | { | ||||||
|     private IMemoryCache Cache { get; set; } |     private IMemoryCache Cache { get; set; } | ||||||
|  |  | ||||||
|     private IServiceProvider Provider { get; set; } |     private static IServiceProvider Provider => App.RootServices; | ||||||
|  |  | ||||||
|     [NotNull] |     [NotNull] | ||||||
|     private static CacheManager? Instance { get; set; } |     private static CacheManager? Instance { get; set; } | ||||||
| @@ -40,8 +41,7 @@ internal class CacheManager | |||||||
|     static CacheManager() |     static CacheManager() | ||||||
|     { |     { | ||||||
|         Instance = new(); |         Instance = new(); | ||||||
|         Instance.Provider = App.RootServices; |         Instance.Cache = Provider.GetRequiredService<IMemoryCache>(); | ||||||
|         Instance.Cache = Instance.Provider.GetRequiredService<IMemoryCache>(); |  | ||||||
|         Options = App.RootServices.GetRequiredService<IOptions<BootstrapBlazorOptions>>().Value; |         Options = App.RootServices.GetRequiredService<IOptions<BootstrapBlazorOptions>>().Value; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -105,37 +105,6 @@ internal class CacheManager | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// 设置 App 开始时间 |  | ||||||
|     /// </summary> |  | ||||||
|     public void SetStartTime() => SetStartTime(DateTimeOffset.Now); |  | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// 设置 App 开始时间 |  | ||||||
|     /// </summary> |  | ||||||
|     private void SetStartTime(DateTimeOffset startDateTimeOffset) |  | ||||||
|     { |  | ||||||
|         GetOrCreate("BootstrapBlazor_StartTime", entry => |  | ||||||
|         { |  | ||||||
|             entry.Priority = CacheItemPriority.NeverRemove; |  | ||||||
|             return startDateTimeOffset; |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// 获取 App 开始时间 |  | ||||||
|     /// </summary> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     public DateTimeOffset GetStartTime() |  | ||||||
|     { |  | ||||||
|         var ret = DateTimeOffset.MinValue; |  | ||||||
|         if (Cache.TryGetValue("BootstrapBlazor_StartTime", out var v) && v is DateTimeOffset d) |  | ||||||
|         { |  | ||||||
|             ret = d; |  | ||||||
|         } |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 获得 缓存数量 |     /// 获得 缓存数量 | ||||||
|     /// </summary> |     /// </summary> | ||||||
| @@ -236,7 +205,7 @@ internal class CacheManager | |||||||
|     /// <returns></returns> |     /// <returns></returns> | ||||||
|     public static IStringLocalizer? CreateLocalizerByType(Type resourceSource) => resourceSource.Assembly.IsDynamic |     public static IStringLocalizer? CreateLocalizerByType(Type resourceSource) => resourceSource.Assembly.IsDynamic | ||||||
|         ? null |         ? null | ||||||
|         : Instance.Provider.GetRequiredService<IStringLocalizerFactory>().Create(resourceSource); |         : Provider.GetRequiredService<IStringLocalizerFactory>().Create(resourceSource); | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 获得 <see cref="JsonLocalizationOptions"/> 值 |     /// 获得 <see cref="JsonLocalizationOptions"/> 值 | ||||||
| @@ -244,7 +213,7 @@ internal class CacheManager | |||||||
|     /// <returns></returns> |     /// <returns></returns> | ||||||
|     private static JsonLocalizationOptions GetJsonLocalizationOption() |     private static JsonLocalizationOptions GetJsonLocalizationOption() | ||||||
|     { |     { | ||||||
|         var localizationOptions = Instance.Provider.GetRequiredService<IOptions<JsonLocalizationOptions>>(); |         var localizationOptions = Provider.GetRequiredService<IOptions<JsonLocalizationOptions>>(); | ||||||
|         return localizationOptions.Value; |         return localizationOptions.Value; | ||||||
|     } |     } | ||||||
|     /// <summary> |     /// <summary> | ||||||
| @@ -253,7 +222,7 @@ internal class CacheManager | |||||||
|     /// <returns></returns> |     /// <returns></returns> | ||||||
|     private static BootstrapBlazorOptions GetBootstrapBlazorOption() |     private static BootstrapBlazorOptions GetBootstrapBlazorOption() | ||||||
|     { |     { | ||||||
|         var localizationOptions = Instance.Provider.GetRequiredService<IOptions<BootstrapBlazorOptions>>(); |         var localizationOptions = Provider.GetRequiredService<IOptions<BootstrapBlazorOptions>>(); | ||||||
|         return localizationOptions.Value; |         return localizationOptions.Value; | ||||||
|     } |     } | ||||||
|     /// <summary> |     /// <summary> | ||||||
| @@ -269,7 +238,7 @@ internal class CacheManager | |||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
|         IStringLocalizer? ret = null; |         IStringLocalizer? ret = null; | ||||||
|         var factories = Instance.Provider.GetServices<IStringLocalizerFactory>(); |         var factories = Provider.GetServices<IStringLocalizerFactory>(); | ||||||
|         var factory = factories.LastOrDefault(a => a is not JsonStringLocalizerFactory); |         var factory = factories.LastOrDefault(a => a is not JsonStringLocalizerFactory); | ||||||
|         if (factory != null) |         if (factory != null) | ||||||
|         { |         { | ||||||
| @@ -287,8 +256,15 @@ internal class CacheManager | |||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="assembly">Assembly 程序集实例</param> |     /// <param name="assembly">Assembly 程序集实例</param> | ||||||
|     /// <param name="typeName">类型名称</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); |         => 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> |     /// <summary> | ||||||
|     /// 通过指定程序集获取所有本地化信息键值集合 |     /// 通过指定程序集获取所有本地化信息键值集合 | ||||||
| @@ -299,7 +275,7 @@ internal class CacheManager | |||||||
|     /// <param name="cultureName">cultureName 未空时使用 CultureInfo.CurrentUICulture.Name</param> |     /// <param name="cultureName">cultureName 未空时使用 CultureInfo.CurrentUICulture.Name</param> | ||||||
|     /// <param name="forceLoad">默认 false 使用缓存值 设置 true 时内部强制重新加载</param> |     /// <param name="forceLoad">默认 false 使用缓存值 设置 true 时内部强制重新加载</param> | ||||||
|     /// <returns></returns> |     /// <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) |         if (assembly.IsDynamic) | ||||||
|         { |         { | ||||||
| @@ -309,13 +285,15 @@ internal class CacheManager | |||||||
|         cultureName ??= CultureInfo.CurrentUICulture.Name; |         cultureName ??= CultureInfo.CurrentUICulture.Name; | ||||||
|         if (string.IsNullOrEmpty(cultureName)) |         if (string.IsNullOrEmpty(cultureName)) | ||||||
|         { |         { | ||||||
|             return []; |             return null; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         var key = $"{CacheKeyPrefix}-{nameof(GetJsonStringByTypeName)}-{assembly.GetUniqueName()}-{cultureName}"; |         var key = $"{CacheKeyPrefix}-{nameof(GetJsonStringByTypeName)}-{assembly.GetUniqueName()}-{cultureName}"; | ||||||
|  |         var typeKey = $"{CacheKeyPrefix}-{nameof(GetJsonStringByTypeName)}-{assembly.GetUniqueName()}-{typeName}-{cultureName}"; | ||||||
|         if (forceLoad) |         if (forceLoad) | ||||||
|         { |         { | ||||||
|             Instance.Cache.Remove(key); |             Instance.Cache.Remove(key); | ||||||
|  |             Instance.Cache.Remove(typeKey); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         var localizedItems = Instance.GetOrCreate(key, entry => |         var localizedItems = Instance.GetOrCreate(key, entry => | ||||||
| @@ -336,16 +314,77 @@ internal class CacheManager | |||||||
|             return items.ToHashSet(); |             return items.ToHashSet(); | ||||||
| #endif | #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> |     /// <summary> | ||||||
|     /// 通过 ILocalizationResolve 接口实现类获得本地化键值集合 |     /// 通过指定程序集获取所有本地化信息键值集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="typeName"></param> |     /// <param name="option">JsonLocalizationOptions 实例</param> | ||||||
|     /// <param name="includeParentCultures"></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> |     /// <returns></returns> | ||||||
|     public static IEnumerable<LocalizedString> GetTypeStringsFromResolve(string typeName, bool includeParentCultures = true) => Instance.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 |     #endregion | ||||||
|  |  | ||||||
|     #region DisplayName |     #region DisplayName | ||||||
|   | |||||||
| @@ -66,7 +66,8 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba | |||||||
|                 } |                 } | ||||||
|                 catch (Exception ex) |                 catch (Exception ex) | ||||||
|                 { |                 { | ||||||
|                     Logger.LogError(ex, "{JsonStringLocalizerName} searched for '{Name}' in '{typeName}' with culture '{CultureName}' throw exception.", nameof(JsonStringLocalizer), name, typeName, CultureInfo.CurrentUICulture.Name); |                     if (Logger?.IsEnabled(LogLevel.Error) == true) | ||||||
|  |                         Logger.LogError(ex, "{JsonStringLocalizerName} searched for '{Name}' in '{typeName}' with culture '{CultureName}' throw exception.", nameof(JsonStringLocalizer), name, typeName, CultureInfo.CurrentUICulture.Name); | ||||||
|                 } |                 } | ||||||
|                 return ret; |                 return ret; | ||||||
|             } |             } | ||||||
| @@ -80,50 +81,16 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba | |||||||
|     /// <returns></returns> |     /// <returns></returns> | ||||||
|     private string? GetStringSafely(string name) => GetStringFromJson(name); |     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 readonly ConcurrentHashSet<string> _missingManifestCache = []; | ||||||
|     private string? GetStringFromJson(string name) |     private string? GetStringFromJson(string name) | ||||||
|     { |     { | ||||||
|         // get string from json localization file |         // get string from json localization file | ||||||
|         var localizerStrings = MegerResolveLocalizers(CacheManager.GetAllStringsByTypeName(Assembly, typeName)); |  | ||||||
|         var cacheKey = $"name={name}&culture={CultureInfo.CurrentUICulture.Name}"; |         var cacheKey = $"name={name}&culture={CultureInfo.CurrentUICulture.Name}"; | ||||||
|         string? ret = null; |         string? ret = null; | ||||||
|         if (!_missingManifestCache.Contain(cacheKey)) |         if (!_missingManifestCache.Contain(cacheKey)) | ||||||
|         { |         { | ||||||
|             var l = localizerStrings.Find(i => i.Name == name); |             var localizerStrings = CacheManager.GetAllHasValueStringsByTypeName(Assembly, typeName); | ||||||
|             if (l is { ResourceNotFound: false }) |             if (localizerStrings?.TryGetValue(name, out ret) != true) | ||||||
|             { |  | ||||||
|                 ret = l.Value; |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |             { | ||||||
|                 // 如果没有找到资源信息则尝试从父类中查找 |                 // 如果没有找到资源信息则尝试从父类中查找 | ||||||
|                 ret ??= GetStringFromBaseType(name); |                 ret ??= GetStringFromBaseType(name); | ||||||
| @@ -149,39 +116,25 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba | |||||||
|             if (baseType != type) |             if (baseType != type) | ||||||
|             { |             { | ||||||
|                 var baseAssembly = baseType.Assembly; |                 var baseAssembly = baseType.Assembly; | ||||||
|                 var localizerStrings = MegerResolveLocalizers(CacheManager.GetAllStringsByTypeName(baseAssembly, baseType.FullName!)); |                 var localizerStrings = CacheManager.GetAllHasValueStringsByTypeName(baseAssembly, baseType.FullName!); | ||||||
|                 var l = localizerStrings.Find(i => i.Name == name); |                 _ = localizerStrings?.TryGetValue(name, out ret); | ||||||
|                 if (l is { ResourceNotFound: false }) |  | ||||||
|                 { |  | ||||||
|                     ret = l.Value; |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         return 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) |     private void HandleMissingResourceItem(string name) | ||||||
|     { |     { | ||||||
|         localizationMissingItemHandler.HandleMissingItem(name, typeName, CultureInfo.CurrentUICulture.Name); |         localizationMissingItemHandler.HandleMissingItem(name, typeName, CultureInfo.CurrentUICulture.Name); | ||||||
|         if (jsonLocalizationOptions.IgnoreLocalizerMissing == false) |         if (jsonLocalizationOptions.IgnoreLocalizerMissing == false) | ||||||
|         { |         { | ||||||
|             Logger.LogInformation("{JsonStringLocalizerName} searched for '{Name}' in '{TypeName}' with culture '{CultureName}' not found.", nameof(JsonStringLocalizer), name, typeName, CultureInfo.CurrentUICulture.Name); |             if (Logger?.IsEnabled(LogLevel.Information) == true) | ||||||
|  |                 Logger.LogInformation("{JsonStringLocalizerName} searched for '{Name}' in '{TypeName}' with culture '{CultureName}' not found.", nameof(JsonStringLocalizer), name, typeName, CultureInfo.CurrentUICulture.Name); | ||||||
|         } |         } | ||||||
|         _missingManifestCache.TryAdd($"name={name}&culture={CultureInfo.CurrentUICulture.Name}"); |         _missingManifestCache.TryAdd($"name={name}&culture={CultureInfo.CurrentUICulture.Name}"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private List<LocalizedString>? _allLocalizerdStrings; |     private LocalizedString[]? _allLocalizerdStrings; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 获取当前语言的所有资源信息 |     /// 获取当前语言的所有资源信息 | ||||||
| @@ -196,7 +149,7 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba | |||||||
|                 ?? GetAllStringsFromBase() |                 ?? GetAllStringsFromBase() | ||||||
|                 ?? GetAllStringsFromJson(); |                 ?? GetAllStringsFromJson(); | ||||||
|  |  | ||||||
|             _allLocalizerdStrings = MegerResolveLocalizers(items); |             _allLocalizerdStrings = items.ToArray(); | ||||||
|         } |         } | ||||||
|         return _allLocalizerdStrings; |         return _allLocalizerdStrings; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ | |||||||
| 	<ItemGroup> | 	<ItemGroup> | ||||||
| 		<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.7" /> | 		<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.7" /> | ||||||
| 		<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" /> | 		<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" /> | ||||||
| 		<PackageReference Include="BootstrapBlazor" Version="9.11.0" /> | 		<PackageReference Include="BootstrapBlazor" Version="9.12.0" /> | ||||||
| 	</ItemGroup> | 	</ItemGroup> | ||||||
|  |  | ||||||
| 	<ItemGroup> | 	<ItemGroup> | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ | |||||||
| 
 | 
 | ||||||
| using Yitter.IdGenerator; | using Yitter.IdGenerator; | ||||||
| 
 | 
 | ||||||
| namespace ThingsGateway.DB; | namespace ThingsGateway.Common; | ||||||
| 
 | 
 | ||||||
| /// <summary> | /// <summary> | ||||||
| /// 公共功能 | /// 公共功能 | ||||||
| @@ -16,74 +16,6 @@ namespace ThingsGateway.DB; | |||||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||||
| public static class QueryPageOptionsExtensions | 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> |     /// <summary> | ||||||
|     /// 根据查询条件返回sqlsugar ISugarQueryable |     /// 根据查询条件返回sqlsugar ISugarQueryable | ||||||
|     /// </summary> |     /// </summary> | ||||||
| @@ -111,50 +43,4 @@ public static class QueryPageOptionsExtensions | |||||||
|         return query; |         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; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ | |||||||
| using Microsoft.AspNetCore.Hosting; | using Microsoft.AspNetCore.Hosting; | ||||||
|  |  | ||||||
| using ThingsGateway; | using ThingsGateway; | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
| using ThingsGateway.Reflection; | using ThingsGateway.Reflection; | ||||||
|  |  | ||||||
| namespace Microsoft.Extensions.Hosting; | namespace Microsoft.Extensions.Hosting; | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ using System.Text.RegularExpressions; | |||||||
|  |  | ||||||
| using ThingsGateway.NewLife; | using ThingsGateway.NewLife; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| /// 对象拓展类 | /// 对象拓展类 | ||||||
| @@ -28,70 +28,10 @@ namespace ThingsGateway.Extensions; | |||||||
| [SuppressSniffer] | [SuppressSniffer] | ||||||
| public static class ObjectExtensions | public static class ObjectExtensions | ||||||
| { | { | ||||||
|     /// <summary> |  | ||||||
|     /// 将 DateTimeOffset 转换成本地 DateTime |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="dateTime"></param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     public static DateTime ConvertToDateTime(this DateTimeOffset dateTime) |  | ||||||
|     { |  | ||||||
|         if (dateTime.Offset.Equals(TimeSpan.Zero)) |  | ||||||
|             return dateTime.UtcDateTime; |  | ||||||
|         if (dateTime.Offset.Equals(TimeZoneInfo.Local.GetUtcOffset(dateTime.DateTime))) |  | ||||||
|             return dateTime.ToLocalTime().DateTime; |  | ||||||
|         else |  | ||||||
|             return dateTime.DateTime; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// 将 DateTimeOffset? 转换成本地 DateTime? |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="dateTime"></param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     public static DateTime? ConvertToDateTime(this DateTimeOffset? dateTime) |  | ||||||
|     { |  | ||||||
|         return dateTime.HasValue ? dateTime.Value.ConvertToDateTime() : null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// 将 DateTime 转换成 DateTimeOffset |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="dateTime"></param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     public static DateTimeOffset ConvertToDateTimeOffset(this DateTime dateTime) |  | ||||||
|     { |  | ||||||
|         return DateTime.SpecifyKind(dateTime, DateTimeKind.Local); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// 将 DateTime? 转换成 DateTimeOffset? |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="dateTime"></param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     public static DateTimeOffset? ConvertToDateTimeOffset(this DateTime? dateTime) |  | ||||||
|     { |  | ||||||
|         return dateTime.HasValue ? dateTime.Value.ConvertToDateTimeOffset() : null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// 将时间戳转换为 DateTime |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="timestamp"></param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     internal static DateTime ConvertToDateTime(this long timestamp) |  | ||||||
|     { |  | ||||||
|         var timeStampDateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); |  | ||||||
|         var digitCount = (int)Math.Floor(Math.Log10(timestamp) + 1); |  | ||||||
|  |  | ||||||
|         if (digitCount != 13 && digitCount != 10) |  | ||||||
|         { |  | ||||||
|             throw new ArgumentException("Data is not a valid timestamp format."); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return (digitCount == 13 |  | ||||||
|             ? timeStampDateTime.AddMilliseconds(timestamp)  // 13 位时间戳 |  | ||||||
|             : timeStampDateTime.AddSeconds(timestamp)).ToLocalTime();   // 10 位时间戳 |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 将 IFormFile 转换成 byte[] |     /// 将 IFormFile 转换成 byte[] | ||||||
| @@ -265,7 +205,7 @@ public static class ObjectExtensions | |||||||
|     /// <typeparam name="T"></typeparam> |     /// <typeparam name="T"></typeparam> | ||||||
|     /// <param name="dic">字典</param> |     /// <param name="dic">字典</param> | ||||||
|     /// <param name="newDic">新字典</param> |     /// <param name="newDic">新字典</param> | ||||||
|     internal static void AddOrUpdate<T>(this ConcurrentDictionary<string, T> dic, Dictionary<string, T> newDic) |     internal static void AddOrUpdate<T>(this NonBlockingDictionary<string, T> dic, Dictionary<string, T> newDic) | ||||||
|     { |     { | ||||||
|         foreach (var (key, value) in newDic) |         foreach (var (key, value) in newDic) | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -94,7 +94,7 @@ public static class AspNetCoreBuilderServiceCollectionExtensions | |||||||
|     /// <param name="mvcBuilder"></param> |     /// <param name="mvcBuilder"></param> | ||||||
|     /// <param name="configure"></param> |     /// <param name="configure"></param> | ||||||
|     /// <returns></returns> |     /// <returns></returns> | ||||||
|     public static IMvcBuilder AddFromConvertBinding(this IMvcBuilder mvcBuilder, Action<ConcurrentDictionary<Type, Type>> configure = default) |     public static IMvcBuilder AddFromConvertBinding(this IMvcBuilder mvcBuilder, Action<NonBlockingDictionary<Type, Type>> configure = default) | ||||||
|     { |     { | ||||||
|         mvcBuilder.Services.AddFromConvertBinding(configure); |         mvcBuilder.Services.AddFromConvertBinding(configure); | ||||||
|  |  | ||||||
| @@ -107,13 +107,13 @@ public static class AspNetCoreBuilderServiceCollectionExtensions | |||||||
|     /// <param name="services"></param> |     /// <param name="services"></param> | ||||||
|     /// <param name="configure"></param> |     /// <param name="configure"></param> | ||||||
|     /// <returns></returns> |     /// <returns></returns> | ||||||
|     public static IServiceCollection AddFromConvertBinding(this IServiceCollection services, Action<ConcurrentDictionary<Type, Type>> configure = default) |     public static IServiceCollection AddFromConvertBinding(this IServiceCollection services, Action<NonBlockingDictionary<Type, Type>> configure = default) | ||||||
|     { |     { | ||||||
|         // 非 Web 环境跳过注册 |         // 非 Web 环境跳过注册 | ||||||
|         if (App.WebHostEnvironment == default) return services; |         if (App.WebHostEnvironment == default) return services; | ||||||
|  |  | ||||||
|         // 定义模型绑定转换器集合 |         // 定义模型绑定转换器集合 | ||||||
|         var modelBinderConverts = new ConcurrentDictionary<Type, Type>(); |         var modelBinderConverts = new NonBlockingDictionary<Type, Type>(); | ||||||
|         modelBinderConverts.TryAdd(typeof(DateTime), typeof(DateTimeModelConvertBinder)); |         modelBinderConverts.TryAdd(typeof(DateTime), typeof(DateTimeModelConvertBinder)); | ||||||
|         modelBinderConverts.TryAdd(typeof(DateTimeOffset), typeof(DateTimeOffsetModelConvertBinder)); |         modelBinderConverts.TryAdd(typeof(DateTimeOffset), typeof(DateTimeOffsetModelConvertBinder)); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|  |  | ||||||
| using Microsoft.AspNetCore.Mvc.ModelBinding; | using Microsoft.AspNetCore.Mvc.ModelBinding; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
|  |  | ||||||
| namespace ThingsGateway.AspNetCore; | namespace ThingsGateway.AspNetCore; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -27,13 +27,13 @@ public class FromConvertBinder : IModelBinder | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 定义模型绑定转换器集合 |     /// 定义模型绑定转换器集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private readonly ConcurrentDictionary<Type, Type> _modelBinderConverts; |     private readonly NonBlockingDictionary<Type, Type> _modelBinderConverts; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 构造函数 |     /// 构造函数 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="modelBinderConverts">定义模型绑定转换器集合</param> |     /// <param name="modelBinderConverts">定义模型绑定转换器集合</param> | ||||||
|     public FromConvertBinder(ConcurrentDictionary<Type, Type> modelBinderConverts) |     public FromConvertBinder(NonBlockingDictionary<Type, Type> modelBinderConverts) | ||||||
|     { |     { | ||||||
|         _modelBinderConverts = modelBinderConverts; |         _modelBinderConverts = modelBinderConverts; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -28,13 +28,13 @@ public class FromConvertBinderProvider : IModelBinderProvider | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 定义模型绑定转换器集合 |     /// 定义模型绑定转换器集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private readonly ConcurrentDictionary<Type, Type> _modelBinderConverts; |     private readonly NonBlockingDictionary<Type, Type> _modelBinderConverts; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 构造函数 |     /// 构造函数 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="modelBinderConverts">定义模型绑定转换器集合</param> |     /// <param name="modelBinderConverts">定义模型绑定转换器集合</param> | ||||||
|     public FromConvertBinderProvider(ConcurrentDictionary<Type, Type> modelBinderConverts) |     public FromConvertBinderProvider(NonBlockingDictionary<Type, Type> modelBinderConverts) | ||||||
|     { |     { | ||||||
|         _modelBinderConverts = modelBinderConverts; |         _modelBinderConverts = modelBinderConverts; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|  |  | ||||||
| using Microsoft.AspNetCore.Mvc.ModelBinding; | using Microsoft.AspNetCore.Mvc.ModelBinding; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
|  |  | ||||||
| namespace ThingsGateway.AspNetCore; | namespace ThingsGateway.AspNetCore; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ | |||||||
| using Microsoft.AspNetCore.Mvc.ModelBinding; | using Microsoft.AspNetCore.Mvc.ModelBinding; | ||||||
| using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; | using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
|  |  | ||||||
| namespace ThingsGateway.AspNetCore; | namespace ThingsGateway.AspNetCore; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ using System.Reflection; | |||||||
|  |  | ||||||
| using ThingsGateway; | using ThingsGateway; | ||||||
| using ThingsGateway.ConfigurableOptions; | using ThingsGateway.ConfigurableOptions; | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
|  |  | ||||||
| namespace Microsoft.Extensions.DependencyInjection; | namespace Microsoft.Extensions.DependencyInjection; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ using System.ComponentModel.DataAnnotations; | |||||||
| using System.Reflection; | using System.Reflection; | ||||||
| using System.Text.RegularExpressions; | using System.Text.RegularExpressions; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
| using ThingsGateway.Templates.Extensions; | using ThingsGateway.Templates.Extensions; | ||||||
|  |  | ||||||
| namespace ThingsGateway.DataValidation; | namespace ThingsGateway.DataValidation; | ||||||
| @@ -40,7 +40,7 @@ public static class DataValidator | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 验证类型正则表达式 |     /// 验证类型正则表达式 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private static readonly ConcurrentDictionary<string, ValidationItemMetadataAttribute> ValidationItemMetadatas; |     private static readonly NonBlockingDictionary<string, ValidationItemMetadataAttribute> ValidationItemMetadatas; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 构造函数 |     /// 构造函数 | ||||||
| @@ -57,7 +57,7 @@ public static class DataValidator | |||||||
|         ValidationItemMetadatas = GetValidationValidationItemMetadatas(); |         ValidationItemMetadatas = GetValidationValidationItemMetadatas(); | ||||||
|  |  | ||||||
|         // 缓存所有正则表达式 |         // 缓存所有正则表达式 | ||||||
|         GetValidationTypeValidationItemMetadataCached = new ConcurrentDictionary<object, (string, ValidationItemMetadataAttribute)>(); |         GetValidationTypeValidationItemMetadataCached = new NonBlockingDictionary<object, (string, ValidationItemMetadataAttribute)>(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
| @@ -203,7 +203,7 @@ public static class DataValidator | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 获取验证类型验证Item集合 |     /// 获取验证类型验证Item集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private static readonly ConcurrentDictionary<object, (string, ValidationItemMetadataAttribute)> GetValidationTypeValidationItemMetadataCached; |     private static readonly NonBlockingDictionary<object, (string, ValidationItemMetadataAttribute)> GetValidationTypeValidationItemMetadataCached; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 获取验证类型正则表达式(需要缓存) |     /// 获取验证类型正则表达式(需要缓存) | ||||||
| @@ -267,9 +267,9 @@ public static class DataValidator | |||||||
|     /// 获取验证类型所有有效的正则表达式 |     /// 获取验证类型所有有效的正则表达式 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <returns></returns> |     /// <returns></returns> | ||||||
|     private static ConcurrentDictionary<string, ValidationItemMetadataAttribute> GetValidationValidationItemMetadatas() |     private static NonBlockingDictionary<string, ValidationItemMetadataAttribute> GetValidationValidationItemMetadatas() | ||||||
|     { |     { | ||||||
|         var vaidationItems = new ConcurrentDictionary<string, ValidationItemMetadataAttribute>(); |         var vaidationItems = new NonBlockingDictionary<string, ValidationItemMetadataAttribute>(); | ||||||
|  |  | ||||||
|         // 查找所有 [ValidationMessageType] 类型中的 [ValidationMessage] 消息定义 |         // 查找所有 [ValidationMessageType] 类型中的 [ValidationMessage] 消息定义 | ||||||
|         var customErrorMessages = ValidationMessageTypes.SelectMany(u => u.GetFields() |         var customErrorMessages = ValidationMessageTypes.SelectMany(u => u.GetFields() | ||||||
|   | |||||||
| @@ -353,7 +353,7 @@ public static class DependencyInjectionServiceCollectionExtensions | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 类型名称集合 |     /// 类型名称集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private static readonly ConcurrentDictionary<string, Type> TypeNamedCollection; |     private static readonly NonBlockingDictionary<string, Type> TypeNamedCollection; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 创建代理方法 |     /// 创建代理方法 | ||||||
| @@ -374,7 +374,7 @@ public static class DependencyInjectionServiceCollectionExtensions | |||||||
|         GlobalServiceProxyType = App.EffectiveTypes |         GlobalServiceProxyType = App.EffectiveTypes | ||||||
|             .FirstOrDefault(u => typeof(AspectDispatchProxy).IsAssignableFrom(u) && typeof(IGlobalDispatchProxy).IsAssignableFrom(u) && u.IsClass && !u.IsInterface && !u.IsAbstract); |             .FirstOrDefault(u => typeof(AspectDispatchProxy).IsAssignableFrom(u) && typeof(IGlobalDispatchProxy).IsAssignableFrom(u) && u.IsClass && !u.IsInterface && !u.IsAbstract); | ||||||
|  |  | ||||||
|         TypeNamedCollection = new ConcurrentDictionary<string, Type>(); |         TypeNamedCollection = new NonBlockingDictionary<string, Type>(); | ||||||
|         DispatchCreateMethod = typeof(AspectDispatchProxy).GetMethod(nameof(AspectDispatchProxy.Create)); |         DispatchCreateMethod = typeof(AspectDispatchProxy).GetMethod(nameof(AspectDispatchProxy.Create)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ using System.Collections.Concurrent; | |||||||
| using System.Reflection; | using System.Reflection; | ||||||
| using System.Text.RegularExpressions; | using System.Text.RegularExpressions; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
| using ThingsGateway.UnifyResult; | using ThingsGateway.UnifyResult; | ||||||
|  |  | ||||||
| namespace ThingsGateway.DynamicApiController; | namespace ThingsGateway.DynamicApiController; | ||||||
|   | |||||||
| @@ -28,21 +28,21 @@ internal static class Penetrates | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 请求动词映射字典 |     /// 请求动词映射字典 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     internal static ConcurrentDictionary<string, string> VerbToHttpMethods { get; private set; } |     internal static NonBlockingDictionary<string, string> VerbToHttpMethods { get; private set; } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 控制器排序集合 |     /// 控制器排序集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     internal static ConcurrentDictionary<string, (string, int, Type)> ControllerOrderCollection { get; set; } |     internal static NonBlockingDictionary<string, (string, int, Type)> ControllerOrderCollection { get; set; } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 构造函数 |     /// 构造函数 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     static Penetrates() |     static Penetrates() | ||||||
|     { |     { | ||||||
|         ControllerOrderCollection = new ConcurrentDictionary<string, (string, int, Type)>(); |         ControllerOrderCollection = new NonBlockingDictionary<string, (string, int, Type)>(); | ||||||
|  |  | ||||||
|         VerbToHttpMethods = new ConcurrentDictionary<string, string> |         VerbToHttpMethods = new NonBlockingDictionary<string, string> | ||||||
|         { |         { | ||||||
|             ["post"] = "POST", |             ["post"] = "POST", | ||||||
|             ["add"] = "POST", |             ["add"] = "POST", | ||||||
| @@ -67,13 +67,13 @@ internal static class Penetrates | |||||||
|             ["patch"] = "PATCH" |             ["patch"] = "PATCH" | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         //IsApiControllerCached = new ConcurrentDictionary<Type, bool>(); |         //IsApiControllerCached = new NonBlockingDictionary<Type, bool>(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ///// <summary> |     ///// <summary> | ||||||
|     ///// <see cref="IsApiController(Type)"/> 缓存集合 |     ///// <see cref="IsApiController(Type)"/> 缓存集合 | ||||||
|     ///// </summary> |     ///// </summary> | ||||||
|     //private static readonly ConcurrentDictionary<Type, bool> IsApiControllerCached; |     //private static readonly NonBlockingDictionary<Type, bool> IsApiControllerCached; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 是否是Api控制器 |     /// 是否是Api控制器 | ||||||
|   | |||||||
| @@ -61,7 +61,7 @@ internal sealed class EventBusHostedService : BackgroundService | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 事件处理程序集合 |     /// 事件处理程序集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private readonly ConcurrentDictionary<EventHandlerWrapper, EventHandlerWrapper> _eventHandlers = new(); |     private readonly NonBlockingDictionary<EventHandlerWrapper, EventHandlerWrapper> _eventHandlers = new(); | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 构造函数 |     /// 构造函数 | ||||||
| @@ -295,7 +295,8 @@ internal sealed class EventBusHostedService : BackgroundService | |||||||
|                         , retryAction: (total, times) => |                         , retryAction: (total, times) => | ||||||
|                         { |                         { | ||||||
|                             // 输出重试日志 |                             // 输出重试日志 | ||||||
|                             _logger.LogWarning("Retrying {times}/{total} times for {EventId}", times, total, eventSource.EventId); |                             if (_logger?.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Warning) == true) | ||||||
|  |                                 _logger.LogWarning("Retrying {times}/{total} times for {EventId}", times, total, eventSource.EventId); | ||||||
|                         }).ConfigureAwait(false); |                         }).ConfigureAwait(false); | ||||||
|                     } |                     } | ||||||
|                     else |                     else | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ using System.ComponentModel.DataAnnotations; | |||||||
| using System.Diagnostics; | using System.Diagnostics; | ||||||
| using System.Reflection; | using System.Reflection; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
| using ThingsGateway.Templates.Extensions; | using ThingsGateway.Templates.Extensions; | ||||||
|  |  | ||||||
| namespace ThingsGateway.FriendlyException; | namespace ThingsGateway.FriendlyException; | ||||||
| @@ -31,7 +31,7 @@ public static class Oops | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 方法错误异常特性 |     /// 方法错误异常特性 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private static readonly ConcurrentDictionary<MethodBase, MethodIfException> _errorMethods; |     private static readonly NonBlockingDictionary<MethodBase, MethodIfException> _errorMethods; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 错误代码类型 |     /// 错误代码类型 | ||||||
| @@ -41,7 +41,7 @@ public static class Oops | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 错误消息字典 |     /// 错误消息字典 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private static readonly ConcurrentDictionary<string, string> _errorCodeMessages; |     private static readonly NonBlockingDictionary<string, string> _errorCodeMessages; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 友好异常设置 |     /// 友好异常设置 | ||||||
| @@ -53,7 +53,7 @@ public static class Oops | |||||||
|     /// </summary> |     /// </summary> | ||||||
|     static Oops() |     static Oops() | ||||||
|     { |     { | ||||||
|         _errorMethods = new ConcurrentDictionary<MethodBase, MethodIfException>(); |         _errorMethods = new NonBlockingDictionary<MethodBase, MethodIfException>(); | ||||||
|         _friendlyExceptionSettings = App.GetConfig<FriendlyExceptionSettingsOptions>("FriendlyExceptionSettings", true); |         _friendlyExceptionSettings = App.GetConfig<FriendlyExceptionSettingsOptions>("FriendlyExceptionSettings", true); | ||||||
|         _errorCodeTypes = GetErrorCodeTypes(); |         _errorCodeTypes = GetErrorCodeTypes(); | ||||||
|         _errorCodeMessages = GetErrorCodeMessages(); |         _errorCodeMessages = GetErrorCodeMessages(); | ||||||
| @@ -258,9 +258,9 @@ public static class Oops | |||||||
|     /// 获取所有错误消息 |     /// 获取所有错误消息 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <returns></returns> |     /// <returns></returns> | ||||||
|     private static ConcurrentDictionary<string, string> GetErrorCodeMessages() |     private static NonBlockingDictionary<string, string> GetErrorCodeMessages() | ||||||
|     { |     { | ||||||
|         var defaultErrorCodeMessages = new ConcurrentDictionary<string, string>(); |         var defaultErrorCodeMessages = new NonBlockingDictionary<string, string>(); | ||||||
|  |  | ||||||
|         // 查找所有 [ErrorCodeType] 类型中的 [ErrorCodeMetadata] 元数据定义 |         // 查找所有 [ErrorCodeType] 类型中的 [ErrorCodeMetadata] 元数据定义 | ||||||
|         var errorCodeMessages = _errorCodeTypes.SelectMany(u => u.GetFields().Where(u => u.IsDefined(typeof(ErrorCodeItemMetadataAttribute)))) |         var errorCodeMessages = _errorCodeTypes.SelectMany(u => u.GetFields().Where(u => u.IsDefined(typeof(ErrorCodeItemMetadataAttribute)))) | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ using Microsoft.AspNetCore.SignalR; | |||||||
| using System.Reflection; | using System.Reflection; | ||||||
|  |  | ||||||
| using ThingsGateway; | using ThingsGateway; | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
| using ThingsGateway.InstantMessaging; | using ThingsGateway.InstantMessaging; | ||||||
|  |  | ||||||
| namespace Microsoft.AspNetCore.Builder; | namespace Microsoft.AspNetCore.Builder; | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ public static class ILoggerExtensions | |||||||
|     /// 设置日志上下文 |     /// 设置日志上下文 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="logger"></param> |     /// <param name="logger"></param> | ||||||
|     /// <param name="properties">建议使用 ConcurrentDictionary 类型</param> |     /// <param name="properties">建议使用 NonBlockingDictionary 类型</param> | ||||||
|     /// <returns></returns> |     /// <returns></returns> | ||||||
|     public static IDisposable ScopeContext(this ILogger logger, IDictionary<string, object> properties) |     public static IDisposable ScopeContext(this ILogger logger, IDictionary<string, object> properties) | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ | |||||||
| // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 | // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 | ||||||
| // ------------------------------------------------------------------------ | // ------------------------------------------------------------------------ | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Logging; | namespace ThingsGateway.Logging; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -82,7 +82,7 @@ public static class StringLoggingExtensions | |||||||
|     /// 配置日志上下文 |     /// 配置日志上下文 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="message"></param> |     /// <param name="message"></param> | ||||||
|     /// <param name="properties">建议使用 ConcurrentDictionary 类型</param> |     /// <param name="properties">建议使用 NonBlockingDictionary 类型</param> | ||||||
|     /// <returns></returns> |     /// <returns></returns> | ||||||
|     public static StringLoggingPart ScopeContext(this string message, IDictionary<string, object> properties) |     public static StringLoggingPart ScopeContext(this string message, IDictionary<string, object> properties) | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -44,7 +44,7 @@ public sealed class FileLoggerProvider : ILoggerProvider, ISupportExternalScope | |||||||
|     /// 记录日志所有滚动文件名 |     /// 记录日志所有滚动文件名 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <remarks>只有 MaxRollingFiles 和 FileSizeLimitBytes 大于 0 有效</remarks> |     /// <remarks>只有 MaxRollingFiles 和 FileSizeLimitBytes 大于 0 有效</remarks> | ||||||
|     internal readonly ConcurrentDictionary<string, FileInfo> _rollingFileNames = new(); |     internal readonly NonBlockingDictionary<string, FileInfo> _rollingFileNames = new(); | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 文件日志写入器 |     /// 文件日志写入器 | ||||||
|   | |||||||
| @@ -36,7 +36,7 @@ using System.Text.Json; | |||||||
|  |  | ||||||
| using ThingsGateway; | using ThingsGateway; | ||||||
| using ThingsGateway.DataValidation; | using ThingsGateway.DataValidation; | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
| using ThingsGateway.FriendlyException; | using ThingsGateway.FriendlyException; | ||||||
| using ThingsGateway.JsonSerialization; | using ThingsGateway.JsonSerialization; | ||||||
| using ThingsGateway.Logging; | using ThingsGateway.Logging; | ||||||
|   | |||||||
| @@ -94,7 +94,7 @@ public sealed partial class StringLoggingPart | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 配置日志上下文 |     /// 配置日志上下文 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="properties">建议使用 ConcurrentDictionary 类型</param> |     /// <param name="properties">建议使用 NonBlockingDictionary 类型</param> | ||||||
|     /// <returns></returns> |     /// <returns></returns> | ||||||
|     public StringLoggingPart ScopeContext(IDictionary<string, object> properties) |     public StringLoggingPart ScopeContext(IDictionary<string, object> properties) | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -57,7 +57,7 @@ public static class Log | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 配置日志上下文 |     /// 配置日志上下文 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="properties">建议使用 ConcurrentDictionary 类型</param> |     /// <param name="properties">建议使用 NonBlockingDictionary 类型</param> | ||||||
|     /// <returns></returns> |     /// <returns></returns> | ||||||
|     public static (ILogger logger, IDisposable scope) ScopeContext(IDictionary<string, object> properties) |     public static (ILogger logger, IDisposable scope) ScopeContext(IDictionary<string, object> properties) | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions; | |||||||
| using System.Linq.Expressions; | using System.Linq.Expressions; | ||||||
| using System.Reflection; | using System.Reflection; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
| using ThingsGateway.Options; | using ThingsGateway.Options; | ||||||
|  |  | ||||||
| namespace Microsoft.Extensions.Options; | namespace Microsoft.Extensions.Options; | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ internal sealed class JobCancellationToken : IJobCancellationToken | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 取消作业执行 Token 集合 |     /// 取消作业执行 Token 集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private readonly ConcurrentDictionary<string, CancellationTokenSource> _cancellationTokenSources; |     private readonly NonBlockingDictionary<string, CancellationTokenSource> _cancellationTokenSources; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 作业调度器日志服务 |     /// 作业调度器日志服务 | ||||||
|   | |||||||
| Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 257 KiB | 
| After Width: | Height: | Size: 177 KiB | 
| @@ -167,7 +167,7 @@ public partial class JobDetail | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 带命名规则的数据库列名 |     /// 带命名规则的数据库列名 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private readonly ConcurrentDictionary<NamingConventions, string[]> _namingColumnNames = new(); |     private readonly NonBlockingDictionary<NamingConventions, string[]> _namingColumnNames = new(); | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 获取数据库列名 |     /// 获取数据库列名 | ||||||
|   | |||||||
| @@ -65,7 +65,7 @@ internal sealed partial class SchedulerFactory : ISchedulerFactory | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 作业计划集合 |     /// 作业计划集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private readonly ConcurrentDictionary<string, Scheduler> _schedulers = new(); |     private readonly NonBlockingDictionary<string, Scheduler> _schedulers = new(); | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 作业计划构建器集合 |     /// 作业计划构建器集合 | ||||||
|   | |||||||
| @@ -369,11 +369,13 @@ internal sealed class ScheduleHostedService : BackgroundService | |||||||
|                             // 写入作业执行详细日志 |                             // 写入作业执行详细日志 | ||||||
|                             if (executionException == null) |                             if (executionException == null) | ||||||
|                             { |                             { | ||||||
|                                 jobLogger?.LogInformation("{jobExecutingContext}", jobExecutingContext); |                                 if (jobLogger?.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information) == true) | ||||||
|  |                                     jobLogger?.LogInformation("{jobExecutingContext}", jobExecutingContext); | ||||||
|                             } |                             } | ||||||
|                             else |                             else | ||||||
|                             { |                             { | ||||||
|                                 jobLogger?.LogError(executionException, "{jobExecutingContext}", jobExecutingContext); |                                 if (jobLogger?.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Error) == true) | ||||||
|  |                                     jobLogger?.LogError(executionException, "{jobExecutingContext}", jobExecutingContext); | ||||||
|                             } |                             } | ||||||
|  |  | ||||||
|                             // 记录作业触发器运行信息 |                             // 记录作业触发器运行信息 | ||||||
|   | |||||||
| @@ -380,7 +380,7 @@ public partial class Trigger | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 带命名规则的数据库列名 |     /// 带命名规则的数据库列名 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private readonly ConcurrentDictionary<NamingConventions, string[]> _namingColumnNames = new(); |     private readonly NonBlockingDictionary<NamingConventions, string[]> _namingColumnNames = new(); | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 获取数据库列名 |     /// 获取数据库列名 | ||||||
|   | |||||||
| @@ -31,7 +31,7 @@ using System.Xml.Linq; | |||||||
| using System.Xml.XPath; | using System.Xml.XPath; | ||||||
|  |  | ||||||
| using ThingsGateway.DynamicApiController; | using ThingsGateway.DynamicApiController; | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
| using ThingsGateway.Reflection; | using ThingsGateway.Reflection; | ||||||
|  |  | ||||||
| namespace ThingsGateway.SpecificationDocument; | namespace ThingsGateway.SpecificationDocument; | ||||||
| @@ -83,11 +83,11 @@ public static class SpecificationDocumentBuilder | |||||||
|  |  | ||||||
|         // 初始化常量 |         // 初始化常量 | ||||||
|         _groupOrderRegex = new Regex(@"@(?<order>[0-9]+$)"); |         _groupOrderRegex = new Regex(@"@(?<order>[0-9]+$)"); | ||||||
|         GetActionGroupsCached = new ConcurrentDictionary<MethodInfo, IEnumerable<GroupExtraInfo>>(); |         GetActionGroupsCached = new NonBlockingDictionary<MethodInfo, IEnumerable<GroupExtraInfo>>(); | ||||||
|         GetControllerGroupsCached = new ConcurrentDictionary<Type, IEnumerable<GroupExtraInfo>>(); |         GetControllerGroupsCached = new NonBlockingDictionary<Type, IEnumerable<GroupExtraInfo>>(); | ||||||
|         GetGroupOpenApiInfoCached = new ConcurrentDictionary<string, SpecificationOpenApiInfo>(); |         GetGroupOpenApiInfoCached = new NonBlockingDictionary<string, SpecificationOpenApiInfo>(); | ||||||
|         GetControllerTagCached = new ConcurrentDictionary<ControllerActionDescriptor, string>(); |         GetControllerTagCached = new NonBlockingDictionary<ControllerActionDescriptor, string>(); | ||||||
|         GetActionTagCached = new ConcurrentDictionary<ApiDescription, string>(); |         GetActionTagCached = new NonBlockingDictionary<ApiDescription, string>(); | ||||||
|  |  | ||||||
|         // 默认分组,支持多个逗号分割 |         // 默认分组,支持多个逗号分割 | ||||||
|         DocumentGroupExtras = new List<GroupExtraInfo> { ResolveGroupExtraInfo(_specificationDocumentSettings.DefaultGroupName) }; |         DocumentGroupExtras = new List<GroupExtraInfo> { ResolveGroupExtraInfo(_specificationDocumentSettings.DefaultGroupName) }; | ||||||
| @@ -143,7 +143,7 @@ public static class SpecificationDocumentBuilder | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 获取分组信息缓存集合 |     /// 获取分组信息缓存集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private static readonly ConcurrentDictionary<string, SpecificationOpenApiInfo> GetGroupOpenApiInfoCached; |     private static readonly NonBlockingDictionary<string, SpecificationOpenApiInfo> GetGroupOpenApiInfoCached; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 获取分组配置信息 |     /// 获取分组配置信息 | ||||||
| @@ -738,7 +738,7 @@ public static class SpecificationDocumentBuilder | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 获取控制器组缓存集合 |     /// 获取控制器组缓存集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private static readonly ConcurrentDictionary<Type, IEnumerable<GroupExtraInfo>> GetControllerGroupsCached; |     private static readonly NonBlockingDictionary<Type, IEnumerable<GroupExtraInfo>> GetControllerGroupsCached; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 获取控制器分组列表 |     /// 获取控制器分组列表 | ||||||
| @@ -773,7 +773,7 @@ public static class SpecificationDocumentBuilder | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// <see cref="GetActionGroups(MethodInfo)"/> 缓存集合 |     /// <see cref="GetActionGroups(MethodInfo)"/> 缓存集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private static readonly ConcurrentDictionary<MethodInfo, IEnumerable<GroupExtraInfo>> GetActionGroupsCached; |     private static readonly NonBlockingDictionary<MethodInfo, IEnumerable<GroupExtraInfo>> GetActionGroupsCached; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 获取动作方法分组列表 |     /// 获取动作方法分组列表 | ||||||
| @@ -808,7 +808,7 @@ public static class SpecificationDocumentBuilder | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// <see cref="GetActionTag(ApiDescription)"/> 缓存集合 |     /// <see cref="GetActionTag(ApiDescription)"/> 缓存集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private static readonly ConcurrentDictionary<ControllerActionDescriptor, string> GetControllerTagCached; |     private static readonly NonBlockingDictionary<ControllerActionDescriptor, string> GetControllerTagCached; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 获取控制器标签 |     /// 获取控制器标签 | ||||||
| @@ -835,7 +835,7 @@ public static class SpecificationDocumentBuilder | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// <see cref="GetActionTag(ApiDescription)"/> 缓存集合 |     /// <see cref="GetActionTag(ApiDescription)"/> 缓存集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private static readonly ConcurrentDictionary<ApiDescription, string> GetActionTagCached; |     private static readonly NonBlockingDictionary<ApiDescription, string> GetActionTagCached; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 获取动作方法标签 |     /// 获取动作方法标签 | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ using System.Reflection; | |||||||
| using System.Text; | using System.Text; | ||||||
| using System.Text.RegularExpressions; | using System.Text.RegularExpressions; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
|  |  | ||||||
| namespace ThingsGateway.SpecificationDocument; | namespace ThingsGateway.SpecificationDocument; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|  |  | ||||||
| using System.Text.RegularExpressions; | using System.Text.RegularExpressions; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Templates.Extensions; | namespace ThingsGateway.Templates.Extensions; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ using Microsoft.AspNetCore.Http; | |||||||
|  |  | ||||||
| using System.Reflection; | using System.Reflection; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
| using ThingsGateway.UnifyResult; | using ThingsGateway.UnifyResult; | ||||||
|  |  | ||||||
| namespace Microsoft.AspNetCore.Mvc; | namespace Microsoft.AspNetCore.Mvc; | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ using Microsoft.Extensions.Options; | |||||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||||
| using System.Reflection; | using System.Reflection; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
| using ThingsGateway.FriendlyException; | using ThingsGateway.FriendlyException; | ||||||
|  |  | ||||||
| namespace ThingsGateway.UnifyResult; | namespace ThingsGateway.UnifyResult; | ||||||
| @@ -51,12 +51,12 @@ public static class UnifyContext | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 规范化结果提供器 |     /// 规范化结果提供器 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     internal static ConcurrentDictionary<string, UnifyMetadata> UnifyProviders = new(); |     internal static NonBlockingDictionary<string, UnifyMetadata> UnifyProviders = new(); | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 规范化序列化配置 |     /// 规范化序列化配置 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     internal static ConcurrentDictionary<string, object> UnifySerializerSettings = new(); |     internal static NonBlockingDictionary<string, object> UnifySerializerSettings = new(); | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 获取异常元数据 |     /// 获取异常元数据 | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ | |||||||
| using System.Text.Json; | using System.Text.Json; | ||||||
| using System.Text.Json.Serialization; | using System.Text.Json.Serialization; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Converters.Json; | namespace ThingsGateway.Converters.Json; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|  |  | ||||||
| using System.Reflection; | using System.Reflection; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     <see cref="Assembly" /> 拓展类 | ///     <see cref="Assembly" /> 拓展类 | ||||||
|   | |||||||
| @@ -11,10 +11,10 @@ | |||||||
|  |  | ||||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     <see cref="ConcurrentDictionary{TKey, TValue}" /> 拓展类 | ///     <see cref="NonBlockingDictionary{TKey, TValue}" /> 拓展类 | ||||||
| /// </summary> | /// </summary> | ||||||
| internal static class ConcurrentDictionaryExtensions | internal static class ConcurrentDictionaryExtensions | ||||||
| { | { | ||||||
| @@ -24,7 +24,7 @@ internal static class ConcurrentDictionaryExtensions | |||||||
|     /// <typeparam name="TKey">字典键类型</typeparam> |     /// <typeparam name="TKey">字典键类型</typeparam> | ||||||
|     /// <typeparam name="TValue">字典值类型</typeparam> |     /// <typeparam name="TValue">字典值类型</typeparam> | ||||||
|     /// <param name="dictionary"> |     /// <param name="dictionary"> | ||||||
|     ///     <see cref="ConcurrentDictionary{TKey, TValue}" /> |     ///     <see cref="NonBlockingDictionary{TKey, TValue}" /> | ||||||
|     /// </param> |     /// </param> | ||||||
|     /// <param name="key"> |     /// <param name="key"> | ||||||
|     ///     <typeparamref name="TKey" /> |     ///     <typeparamref name="TKey" /> | ||||||
| @@ -36,7 +36,7 @@ internal static class ConcurrentDictionaryExtensions | |||||||
|     /// <returns> |     /// <returns> | ||||||
|     ///     <see cref="bool" /> |     ///     <see cref="bool" /> | ||||||
|     /// </returns> |     /// </returns> | ||||||
|     internal static bool TryUpdate<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> dictionary |     internal static bool TryUpdate<TKey, TValue>(this NonBlockingDictionary<TKey, TValue> dictionary | ||||||
|         , TKey key |         , TKey key | ||||||
|         , Func<TValue, TValue> updateFactory |         , Func<TValue, TValue> updateFactory | ||||||
|         , out TValue? value) |         , out TValue? value) | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ using Microsoft.Extensions.Hosting; | |||||||
|  |  | ||||||
| using System.Reflection; | using System.Reflection; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     核心模块 <see cref="IServiceCollection" /> 拓展类 | ///     核心模块 <see cref="IServiceCollection" /> 拓展类 | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|  |  | ||||||
| using System.Data; | using System.Data; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     <see cref="DataTable" /> 和 <see cref="DataSet" /> 拓展类 | ///     <see cref="DataTable" /> 和 <see cref="DataSet" /> 拓展类 | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ | |||||||
| // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 | // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 | ||||||
| // ------------------------------------------------------------------------ | // ------------------------------------------------------------------------ | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     委托拓展类 | ///     委托拓展类 | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ | |||||||
| using System.ComponentModel; | using System.ComponentModel; | ||||||
| using System.Reflection; | using System.Reflection; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     枚举拓展类 | ///     枚举拓展类 | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ | |||||||
| // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 | // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 | ||||||
| // ------------------------------------------------------------------------ | // ------------------------------------------------------------------------ | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     <see cref="EventHandler{TEventArgs}" /> 拓展类 | ///     <see cref="EventHandler{TEventArgs}" /> 拓展类 | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|  |  | ||||||
| using System.Diagnostics.CodeAnalysis; | using System.Diagnostics.CodeAnalysis; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     <see cref="ICollection{T}" /> 拓展类 | ///     <see cref="ICollection{T}" /> 拓展类 | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|  |  | ||||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     <see cref="IDictionary{TKey, TValue}" /> 拓展类 | ///     <see cref="IDictionary{TKey, TValue}" /> 拓展类 | ||||||
| @@ -241,7 +241,7 @@ internal static class IDictionaryExtensions | |||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <remarks>其中键是由值通过给定的选择器函数生成的。</remarks> |     /// <remarks>其中键是由值通过给定的选择器函数生成的。</remarks> | ||||||
|     /// <param name="dictionary"> |     /// <param name="dictionary"> | ||||||
|     ///     <see cref="ConcurrentDictionary{TKey, TValue}" /> |     ///     <see cref="NonBlockingDictionary{TKey, TValue}" /> | ||||||
|     /// </param> |     /// </param> | ||||||
|     /// <param name="values"> |     /// <param name="values"> | ||||||
|     ///     <see cref="IEnumerable{T}" /> |     ///     <see cref="IEnumerable{T}" /> | ||||||
| @@ -249,7 +249,7 @@ internal static class IDictionaryExtensions | |||||||
|     /// <param name="keySelector">键选择器</param> |     /// <param name="keySelector">键选择器</param> | ||||||
|     /// <typeparam name="TKey">字典键类型</typeparam> |     /// <typeparam name="TKey">字典键类型</typeparam> | ||||||
|     /// <typeparam name="TValue">字典值类型</typeparam> |     /// <typeparam name="TValue">字典值类型</typeparam> | ||||||
|     internal static void TryAdd<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> dictionary, |     internal static void TryAdd<TKey, TValue>(this NonBlockingDictionary<TKey, TValue> dictionary, | ||||||
|         IEnumerable<TValue>? values, Func<TValue, TKey> keySelector) |         IEnumerable<TValue>? values, Func<TValue, TKey> keySelector) | ||||||
|         where TKey : notnull |         where TKey : notnull | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ | |||||||
| // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 | // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 | ||||||
| // ------------------------------------------------------------------------ | // ------------------------------------------------------------------------ | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     <see cref="IEnumerable" /> 拓展类 | ///     <see cref="IEnumerable" /> 拓展类 | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ using System.Text.RegularExpressions; | |||||||
| using System.Xml; | using System.Xml; | ||||||
| using System.Xml.Linq; | using System.Xml.Linq; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     System.Text.Json 拓展类 | ///     System.Text.Json 拓展类 | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ | |||||||
| using System.Linq.Expressions; | using System.Linq.Expressions; | ||||||
| using System.Reflection; | using System.Reflection; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     <see cref="Expression" /> 拓展类 | ///     <see cref="Expression" /> 拓展类 | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ | |||||||
| using System.Diagnostics.CodeAnalysis; | using System.Diagnostics.CodeAnalysis; | ||||||
| using System.Reflection; | using System.Reflection; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     <see cref="MethodInfo" /> 拓展类 | ///     <see cref="MethodInfo" /> 拓展类 | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ | |||||||
| // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 | // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 | ||||||
| // ------------------------------------------------------------------------ | // ------------------------------------------------------------------------ | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     数值类型拓展类 | ///     数值类型拓展类 | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ using System.Reflection; | |||||||
| using System.Text; | using System.Text; | ||||||
| using System.Text.RegularExpressions; | using System.Text.RegularExpressions; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     <see cref="string" /> 拓展类 | ///     <see cref="string" /> 拓展类 | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ using System.Reflection; | |||||||
| using System.Reflection.Emit; | using System.Reflection.Emit; | ||||||
| using System.Runtime.CompilerServices; | using System.Runtime.CompilerServices; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     <see cref="Type" /> 拓展类 | ///     <see cref="Type" /> 拓展类 | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ using System.Buffers; | |||||||
| using System.Text; | using System.Text; | ||||||
| using System.Text.Json; | using System.Text.Json; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     <see cref="Utf8JsonReader" /> 拓展类 | ///     <see cref="Utf8JsonReader" /> 拓展类 | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ using System.Text.Json; | |||||||
|  |  | ||||||
| using ThingsGateway.Utilities; | using ThingsGateway.Utilities; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Extensions; | namespace ThingsGateway.Extension; | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| ///     <see cref="object" /> 拓展类 | ///     <see cref="object" /> 拓展类 | ||||||
|   | |||||||
| @@ -21,20 +21,20 @@ internal sealed class CoreOptions | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     ///     已注册的组件元数据集合 |     ///     已注册的组件元数据集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     internal readonly ConcurrentDictionary<string, ComponentMetadata> _metadataOfRegistered; |     internal readonly NonBlockingDictionary<string, ComponentMetadata> _metadataOfRegistered; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     ///     子选项集合 |     ///     子选项集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     internal readonly ConcurrentDictionary<Type, object> _optionsInstances; |     internal readonly NonBlockingDictionary<Type, object> _optionsInstances; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     ///     <inheritdoc cref="CoreOptions" /> |     ///     <inheritdoc cref="CoreOptions" /> | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     internal CoreOptions() |     internal CoreOptions() | ||||||
|     { |     { | ||||||
|         _optionsInstances = new ConcurrentDictionary<Type, object>(); |         _optionsInstances = new NonBlockingDictionary<Type, object>(); | ||||||
|         _metadataOfRegistered = new ConcurrentDictionary<string, ComponentMetadata>(StringComparer.OrdinalIgnoreCase); |         _metadataOfRegistered = new NonBlockingDictionary<string, ComponentMetadata>(StringComparer.OrdinalIgnoreCase); | ||||||
|  |  | ||||||
|         EntryComponentTypes = []; |         EntryComponentTypes = []; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ using System.Collections.Concurrent; | |||||||
| using System.Diagnostics.CodeAnalysis; | using System.Diagnostics.CodeAnalysis; | ||||||
| using System.Reflection; | using System.Reflection; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Reflection; | namespace ThingsGateway.Reflection; | ||||||
|  |  | ||||||
| @@ -32,7 +32,7 @@ public sealed class ObjectPropertyGetter<T> where T : class | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     ///     对象类型实例属性值访问器集合 |     ///     对象类型实例属性值访问器集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     internal readonly ConcurrentDictionary<string, Func<object, object?>> _propertyGetters = new(); |     internal readonly NonBlockingDictionary<string, Func<object, object?>> _propertyGetters = new(); | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     ///     <inheritdoc cref="ObjectPropertyGetter{T}" /> |     ///     <inheritdoc cref="ObjectPropertyGetter{T}" /> | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ using System.Collections.Concurrent; | |||||||
| using System.Diagnostics.CodeAnalysis; | using System.Diagnostics.CodeAnalysis; | ||||||
| using System.Reflection; | using System.Reflection; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Reflection; | namespace ThingsGateway.Reflection; | ||||||
|  |  | ||||||
| @@ -32,7 +32,7 @@ public sealed class ObjectPropertySetter<T> where T : class | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     ///     对象类型实例属性值设置器集合 |     ///     对象类型实例属性值设置器集合 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     internal readonly ConcurrentDictionary<string, Action<object, object?>> _propertySetters = new(); |     internal readonly NonBlockingDictionary<string, Action<object, object?>> _propertySetters = new(); | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     ///     <inheritdoc cref="ObjectPropertySetter{T}" /> |     ///     <inheritdoc cref="ObjectPropertySetter{T}" /> | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|  |  | ||||||
| using System.Diagnostics.CodeAnalysis; | using System.Diagnostics.CodeAnalysis; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Utilities; | namespace ThingsGateway.Utilities; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|  |  | ||||||
| using System.Text; | using System.Text; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Utilities; | namespace ThingsGateway.Utilities; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ using Microsoft.Net.Http.Headers; | |||||||
| using System.Net.Mime; | using System.Net.Mime; | ||||||
|  |  | ||||||
| using ThingsGateway.AspNetCore.Extensions; | using ThingsGateway.AspNetCore.Extensions; | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
|  |  | ||||||
| using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue; | using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ | |||||||
| using System.Net.Http.Headers; | using System.Net.Http.Headers; | ||||||
| using System.Threading.Channels; | using System.Threading.Channels; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
| using ThingsGateway.Utilities; | using ThingsGateway.Utilities; | ||||||
|  |  | ||||||
| namespace ThingsGateway.HttpRemote; | namespace ThingsGateway.HttpRemote; | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ using System.Text; | |||||||
| using System.Text.Json; | using System.Text.Json; | ||||||
| using System.Threading.Channels; | using System.Threading.Channels; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
| using ThingsGateway.Utilities; | using ThingsGateway.Utilities; | ||||||
|  |  | ||||||
| namespace ThingsGateway.HttpRemote; | namespace ThingsGateway.HttpRemote; | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ using Microsoft.Extensions.Logging; | |||||||
| using System.Reflection; | using System.Reflection; | ||||||
| using System.Text; | using System.Text; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
|  |  | ||||||
| namespace ThingsGateway.HttpRemote; | namespace ThingsGateway.HttpRemote; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ using System.Net.Mime; | |||||||
| using System.Text; | using System.Text; | ||||||
| using System.Text.Json; | using System.Text.Json; | ||||||
|  |  | ||||||
| using ThingsGateway.Extensions; | using ThingsGateway.Extension; | ||||||
| using ThingsGateway.Utilities; | using ThingsGateway.Utilities; | ||||||
|  |  | ||||||
| using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue; | using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue; | ||||||
|   | |||||||