mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-23 11:51:09 +08:00
Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0e44bc67cd |
14
README.md
14
README.md
@@ -1,11 +1,18 @@
|
||||
# ThingsGateway
|
||||
|
||||
|
||||
[](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
|
||||
|
||||
|
||||
A cross-platform, high-performance edge data collection gateway based on net9.
|
||||
A cross-platform, high-performance edge data collection gateway based on net8/10.
|
||||
|
||||
|
||||
## Documentation
|
||||
@@ -29,7 +36,6 @@ Account: **SuperAdmin**
|
||||
Password: **111111**
|
||||
|
||||
|
||||
**In the upper-right corner, switch to the IoT Gateway module in the personal popup box**
|
||||
|
||||
## Docker
|
||||
|
||||
|
@@ -1,8 +1,18 @@
|
||||
# ThingsGateway
|
||||
|
||||
[](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 +29,6 @@
|
||||
|
||||
密码 : **111111**
|
||||
|
||||
**右上角个人弹出框中,切换到物联网关模块**
|
||||
|
||||
## Docker
|
||||
|
||||
|
@@ -8,6 +8,9 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Runtime;
|
||||
|
||||
using ThingsGateway.NewLife;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
@@ -19,11 +22,7 @@ public class HardwareInfo
|
||||
/// 当前磁盘信息
|
||||
/// </summary>
|
||||
public DriveInfo DriveInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 硬件信息获取
|
||||
/// </summary>
|
||||
public MachineInfo? MachineInfo { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 主机环境
|
||||
@@ -40,19 +39,118 @@ public class HardwareInfo
|
||||
/// </summary>
|
||||
public string OsArchitecture { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 唯一编码
|
||||
/// </summary>
|
||||
public string UUID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 进程占用内存
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public int WorkingSet { get; set; }
|
||||
/// <summary>系统名称</summary>
|
||||
public String OSName { get; set; }
|
||||
|
||||
/// <summary>系统版本</summary>
|
||||
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>
|
||||
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
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using ThingsGateway.Extension;
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.NewLife.Caching;
|
||||
using ThingsGateway.NewLife.Threading;
|
||||
@@ -43,7 +41,7 @@ public class HardwareJob : IJob, IHardwareJob
|
||||
/// <summary>
|
||||
/// 运行信息获取
|
||||
/// </summary>
|
||||
public HardwareInfo HardwareInfo { get; } = new();
|
||||
public HardwareInfo HardwareInfo { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public HardwareInfoOptions HardwareInfoOptions { get; private set; }
|
||||
@@ -76,9 +74,10 @@ public class HardwareJob : IJob, IHardwareJob
|
||||
{
|
||||
try
|
||||
{
|
||||
if (HardwareInfo.MachineInfo == null)
|
||||
var machine = MachineInfo.GetCurrent();
|
||||
if (HardwareInfo == null)
|
||||
{
|
||||
HardwareInfo.MachineInfo = MachineInfo.GetCurrent();
|
||||
HardwareInfo=machine.AdaptHardwareInfo();
|
||||
|
||||
string currentPath = Directory.GetCurrentDirectory();
|
||||
DriveInfo drive = new(Path.GetPathRoot(currentPath));
|
||||
@@ -88,10 +87,9 @@ public class HardwareJob : IJob, IHardwareJob
|
||||
HardwareInfo.DriveInfo = drive;
|
||||
HardwareInfo.OsArchitecture = Environment.OSVersion.Platform.ToString() + " " + RuntimeInformation.OSArchitecture.ToString(); // 系统架构
|
||||
HardwareInfo.FrameworkDescription = RuntimeInformation.FrameworkDescription; // NET框架
|
||||
HardwareInfo.Environment = App.HostEnvironment.IsDevelopment() ? "Development" : "Production";
|
||||
HardwareInfo.UUID = HardwareInfo.MachineInfo.UUID;
|
||||
HardwareInfo.Environment = App.HostEnvironment.EnvironmentName;
|
||||
|
||||
HardwareInfo.UpdateTime = TimerX.Now.ToDefaultDateTimeFormat();
|
||||
HardwareInfo.UpdateTime = TimerX.Now;
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -99,9 +97,12 @@ public class HardwareJob : IJob, IHardwareJob
|
||||
}
|
||||
try
|
||||
{
|
||||
HardwareInfo.MachineInfo.Refresh();
|
||||
HardwareInfo.UpdateTime = TimerX.Now.ToDefaultDateTimeFormat();
|
||||
HardwareInfo.WorkingSet = (Environment.WorkingSet / 1024.0 / 1024.0).ToInt();
|
||||
var machine = MachineInfo.GetCurrent();
|
||||
machine.Refresh();
|
||||
machine.AdaptHardwareInfo(HardwareInfo);
|
||||
HardwareInfo.AppRunTotalMinute = (ulong)Runtime.AppTickCount64 / 1000 /60;
|
||||
HardwareInfo.SystemRunTotalMinute = (ulong)Runtime.TickCount64 / 1000 /60;
|
||||
HardwareInfo.UpdateTime = TimerX.Now;
|
||||
error = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -123,10 +124,10 @@ public class HardwareJob : IJob, IHardwareJob
|
||||
{
|
||||
Date = TimerX.Now,
|
||||
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),
|
||||
CpuUsage = (HardwareInfo.MachineInfo.CpuRate * 100).ToInt(),
|
||||
Temperature = (HardwareInfo.MachineInfo.Temperature).ToInt(),
|
||||
CpuUsage = (HardwareInfo.CpuRate * 100).ToInt(),
|
||||
Temperature = (HardwareInfo.Temperature).ToInt(),
|
||||
};
|
||||
await _db.InsertableT(his).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false);
|
||||
MemoryCache.Remove(CacheKey);
|
||||
|
@@ -21,7 +21,7 @@ public class HistoryHardwareInfo
|
||||
|
||||
/// <inheritdoc/>
|
||||
[SugarColumn(ColumnDescription = "内存")]
|
||||
public int MemoryUsage { get; set; }
|
||||
public UInt64 MemoryUsage { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[SugarColumn(ColumnDescription = "CPU使用率")]
|
||||
|
@@ -21,7 +21,7 @@
|
||||
"UserNoModule": "This account has not been assigned a module. Please contact the administrator",
|
||||
"UserNull": "User {0} does not exist"
|
||||
},
|
||||
|
||||
|
||||
"ThingsGateway.Admin.Application.BlazorAuthenticationHandler": {
|
||||
"UserExpire": "User expired, please login again"
|
||||
},
|
||||
@@ -46,12 +46,44 @@
|
||||
"FileTypeError": "Not supported format {0}"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.HardwareInfo": {
|
||||
"Environment": "HostEnvironment",
|
||||
"FrameworkDescription": ".NETFramework",
|
||||
"OsArchitecture": "System Architecture",
|
||||
"UpdateTime": "UpdateTime",
|
||||
"UUID": "UUID"
|
||||
"DriveInfo": "Current Disk Info",
|
||||
"AppRunTotalMinute": "AppRunTotalMinute(min)",
|
||||
"SystemRunTotalMinute": "SystemRunTotalMinute(min)",
|
||||
"Environment": "Host Environment",
|
||||
"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": {
|
||||
"GetHardwareInfoFail": "Get Hardwareinfo Fail"
|
||||
},
|
||||
|
@@ -46,11 +46,42 @@
|
||||
"FileTypeError": "不支持 {0} 格式"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.HardwareInfo": {
|
||||
"DriveInfo": "当前磁盘信息",
|
||||
"AppRunTotalMinute": "软件运行时长(min)",
|
||||
"SystemRunTotalMinute": "系统运行时长(min)",
|
||||
"Environment": "主机环境",
|
||||
"FrameworkDescription": "NET框架",
|
||||
"FrameworkDescription": ".NET 框架",
|
||||
"OsArchitecture": "系统架构",
|
||||
"UpdateTime": "更新时间",
|
||||
"UUID": "唯一编码"
|
||||
"OSName": "系统名称",
|
||||
"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": {
|
||||
"GetHardwareInfoFail": "获取硬件信息出错"
|
||||
|
@@ -8,13 +8,20 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using Riok.Mapperly.Abstractions;
|
||||
|
||||
using ThingsGateway.NewLife;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
[Mapper(UseDeepCloning = true, EnumMappingStrategy = EnumMappingStrategy.ByName, RequiredMappingStrategy = RequiredMappingStrategy.None)]
|
||||
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 OpenApiLoginOutput AdaptOpenApiLoginOutput(this LoginOutput src);
|
||||
public static partial SessionOutput AdaptSessionOutput(this SysUser src);
|
||||
|
@@ -22,6 +22,9 @@
|
||||
"SetDefaultModule": "Set as default module"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.HardwareInfoPage": {
|
||||
"GCMemoryInfoConfig": "GCMemoryInfoConfig(MB)",
|
||||
"GCMemoryInfo": "GCMemoryInfo(MB)",
|
||||
"GCAnalyzeInfo": "GCAnalyzeInfo",
|
||||
"AvailableDisk": "Available Disk",
|
||||
"AvailableMemory": "Available Memory",
|
||||
"CpuUsage": "CPU Usage",
|
||||
|
@@ -22,6 +22,10 @@
|
||||
"SetDefaultModule": "设置为默认模块"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.HardwareInfoPage": {
|
||||
"GCMemoryInfoConfig": "GC配置信息(MB)",
|
||||
"GCMemoryInfo": "GC内存信息(MB)",
|
||||
"GCAnalyzeInfo": "GC统计",
|
||||
|
||||
"AvailableDisk": "可用磁盘",
|
||||
"AvailableMemory": "可用内存",
|
||||
"CpuUsage": "CPU使用率",
|
||||
|
@@ -5,131 +5,189 @@
|
||||
@using ThingsGateway.Admin.Application
|
||||
@namespace ThingsGateway.Admin.Razor
|
||||
<div class="row g-2 mx-1 form-inline">
|
||||
|
||||
<div class="col-12 col-md-4">
|
||||
<div class="col-12 col-md-12">
|
||||
|
||||
<Card IsShadow=true class="m-2 flex-fill" Color="Color.Primary">
|
||||
<HeaderTemplate>
|
||||
@Localizer["SystemInfo"]
|
||||
</HeaderTemplate>
|
||||
|
||||
<BodyTemplate>
|
||||
<EditorFormObject IsDisplay Model="HardwareJob.HardwareInfo" ItemsPerRow="1" RowType=RowType.Inline LabelWidth="160">
|
||||
<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 class="d-flex justify-content-between align-items-center w-100">
|
||||
<span>@Localizer["SystemInfo"]</span>
|
||||
<small class="text-muted">
|
||||
@HardwareJob.HardwareInfo.UpdateTime.ToString("yyyy-MM-dd HH:mm:ss")
|
||||
</small>
|
||||
</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>
|
||||
</Card>
|
||||
|
||||
</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="col-12 col-md-12">
|
||||
@@ -140,7 +198,7 @@
|
||||
</HeaderTemplate>
|
||||
|
||||
<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>
|
||||
</Card>
|
||||
</div>
|
||||
|
@@ -104,37 +104,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>
|
||||
|
@@ -14,10 +14,16 @@ using ThingsGateway.NewLife.Reflection;
|
||||
using ThingsGateway.NewLife.Serialization;
|
||||
using ThingsGateway.NewLife.Windows;
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime;
|
||||
|
||||
|
||||
#if NETFRAMEWORK
|
||||
using System.Management;
|
||||
|
||||
using Microsoft.VisualBasic.Devices;
|
||||
|
||||
|
||||
#endif
|
||||
#if NETFRAMEWORK || NET6_0_OR_GREATER
|
||||
using Microsoft.Win32;
|
||||
@@ -42,7 +48,7 @@ public interface IMachineInfo
|
||||
///
|
||||
/// 刷新信息成本较高,建议采用单例模式
|
||||
/// </remarks>
|
||||
public class MachineInfo : IExtend
|
||||
public class MachineInfo
|
||||
{
|
||||
#region 属性
|
||||
/// <summary>系统名称</summary>
|
||||
@@ -88,11 +94,11 @@ public class MachineInfo : IExtend
|
||||
[DisplayName("磁盘序列号")]
|
||||
public String? DiskID { get; set; }
|
||||
|
||||
/// <summary>内存总量。单位KB</summary>
|
||||
/// <summary>内存总量。单位MB</summary>
|
||||
[DisplayName("内存总量")]
|
||||
public UInt64 Memory { get; set; }
|
||||
|
||||
/// <summary>可用内存。单位KB</summary>
|
||||
/// <summary>可用内存。单位MB</summary>
|
||||
[DisplayName("可用内存")]
|
||||
public UInt64 AvailableMemory { get; set; }
|
||||
|
||||
@@ -116,13 +122,98 @@ public class MachineInfo : IExtend
|
||||
[DisplayName("电池剩余")]
|
||||
public Double Battery { get; set; }
|
||||
|
||||
private readonly Dictionary<String, Object?> _items = [];
|
||||
IDictionary<String, Object?> IExtend.Items => _items;
|
||||
#if NET6_0_OR_GREATER
|
||||
#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; }
|
||||
#if NET8_0_OR_GREATER
|
||||
/// <summary>GC 暂停累计时间。单位:毫秒</summary>
|
||||
[DisplayName("GC累计暂停时间")]
|
||||
public UInt64 TotalPauseDurationMs { get; set; }
|
||||
#endif
|
||||
/// <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
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/// <summary>获取 或 设置 扩展属性数据</summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public Object? this[String key] { get => _items.TryGetValue(key, out var obj) ? obj : null; set => _items[key] = value; }
|
||||
#endregion
|
||||
|
||||
#region 全局静态
|
||||
@@ -569,10 +660,50 @@ public class MachineInfo : IExtend
|
||||
else if (Runtime.Linux)
|
||||
RefreshLinux();
|
||||
|
||||
// 刷新 GC 与进程内存信息
|
||||
RefreshMemoryInfo();
|
||||
|
||||
RefreshSpeed();
|
||||
|
||||
Provider?.Refresh(this);
|
||||
}
|
||||
/// <summary>
|
||||
/// 刷新 GC 与进程内存相关信息
|
||||
/// </summary>
|
||||
private void RefreshMemoryInfo()
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
var info = GC.GetGCMemoryInfo();
|
||||
var proc = Process.GetCurrentProcess();
|
||||
|
||||
// GC 信息(单位:MB)
|
||||
HighMemoryLoadThreshold = (ulong)(info.HighMemoryLoadThresholdBytes / 1024 / 1024);
|
||||
TotalAvailableMemory = (ulong)(info.TotalAvailableMemoryBytes / 1024 / 1024);
|
||||
HeapSize = (ulong)(info.HeapSizeBytes / 1024 / 1024);
|
||||
TotalMemory = (ulong)(GC.GetTotalMemory(false) / 1024 / 1024);
|
||||
FragmentedBytes = (ulong)(info.FragmentedBytes / 1024 / 1024);
|
||||
GCAvailableMemory = (ulong)(info.TotalAvailableMemoryBytes - info.MemoryLoadBytes) / 1024 / 1024;
|
||||
CommittedBytes = (ulong)(info.TotalCommittedBytes / 1024 / 1024);
|
||||
TotalAllocatedBytes = (ulong)(GC.GetTotalAllocatedBytes(false) / 1024 / 1024);
|
||||
#if NET8_0_OR_GREATER
|
||||
TotalPauseDurationMs = (ulong)GC.GetTotalPauseDuration().TotalMilliseconds;
|
||||
#endif
|
||||
GcGen0Count = GC.CollectionCount(0);
|
||||
GcGen1Count = GC.CollectionCount(1);
|
||||
GcGen2Count = GC.CollectionCount(2);
|
||||
IsServerGC = System.Runtime.GCSettings.IsServerGC;
|
||||
GCLatencyMode = System.Runtime.GCSettings.LatencyMode;
|
||||
PinnedObjectsCount = info.PinnedObjectsCount;
|
||||
FinalizationPendingCount = info.FinalizationPendingCount;
|
||||
|
||||
// 进程信息(单位:MB)
|
||||
VirtualMemory = (ulong)(proc.VirtualMemorySize64 / 1024 / 1024);
|
||||
PrivateMemory = (ulong)(proc.PrivateMemorySize64 / 1024 / 1024);
|
||||
PeakWorkingSet = (ulong)(proc.PeakWorkingSet64 / 1024 / 1024);
|
||||
WorkingSet = (ulong)(proc.WorkingSet64 / 1024 / 1024);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
private void RefreshWindows()
|
||||
{
|
||||
@@ -580,8 +711,8 @@ public class MachineInfo : IExtend
|
||||
ms.Init();
|
||||
if (GlobalMemoryStatusEx(ref ms))
|
||||
{
|
||||
Memory = (ulong)(ms.ullTotalPhys / 1024.0);
|
||||
AvailableMemory = (ulong)(ms.ullAvailPhys / 1024.0);
|
||||
Memory = ms.ullTotalPhys / 1024 / 1024;
|
||||
AvailableMemory = ms.ullAvailPhys / 1024 / 1024;
|
||||
}
|
||||
|
||||
GetSystemTimes(out var idleTime, out var kernelTime, out var userTime);
|
||||
@@ -675,28 +806,87 @@ public class MachineInfo : IExtend
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 🐳 容器内存使用
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static (ulong Total, ulong Used) GetCGroupMemoryUsage()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
string[] limitPaths = {
|
||||
"/sys/fs/cgroup/memory/memory.limit_in_bytes", // cgroup v1
|
||||
"/sys/fs/cgroup/memory.max" // cgroup v2
|
||||
};
|
||||
|
||||
string[] usagePaths = {
|
||||
"/sys/fs/cgroup/memory/memory.usage_in_bytes", // cgroup v1
|
||||
"/sys/fs/cgroup/memory.current" // cgroup v2
|
||||
};
|
||||
|
||||
ulong total = ReadFirstAvailable(limitPaths);
|
||||
ulong used = ReadFirstAvailable(usagePaths);
|
||||
|
||||
// 容器无内存限制时 total 通常是 2^63-1,忽略
|
||||
if (total > (1UL << 60)) total = 0;
|
||||
|
||||
return (total, used);
|
||||
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return (0, 0);
|
||||
}
|
||||
static ulong ReadFirstAvailable(string[] paths)
|
||||
{
|
||||
foreach (var path in paths)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var content = File.ReadAllText(path).Trim();
|
||||
if (content == "max") return ulong.MaxValue;
|
||||
if (ulong.TryParse(content, out var value)) return value;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void RefreshLinux()
|
||||
{
|
||||
var dic = ReadInfo("/proc/meminfo");
|
||||
if (dic != null)
|
||||
var (totalMemory, usedMemory) = GetCGroupMemoryUsage();
|
||||
if (totalMemory > 0 && usedMemory > 0 && totalMemory < ulong.MaxValue / 2)
|
||||
{
|
||||
if (dic.TryGetValue("MemTotal", out var str) && !str.IsNullOrEmpty())
|
||||
Memory = (UInt64)str.TrimEnd(" kB").ToLong();
|
||||
|
||||
ulong ma = 0;
|
||||
if (dic.TryGetValue("MemAvailable", out str) && !str.IsNullOrEmpty())
|
||||
Memory = totalMemory / 1024 / 1024;
|
||||
AvailableMemory = (totalMemory - usedMemory) / 1024 / 1024;
|
||||
}
|
||||
else
|
||||
{
|
||||
var dic = ReadInfo("/proc/meminfo");
|
||||
if (dic != null)
|
||||
{
|
||||
ma = (UInt64)(str.TrimEnd(" kB").ToLong());
|
||||
if (dic.TryGetValue("MemTotal", out var str) && !str.IsNullOrEmpty())
|
||||
Memory = (UInt64)str.TrimEnd(" kB").ToLong();
|
||||
|
||||
ulong ma = 0;
|
||||
if (dic.TryGetValue("MemAvailable", out str) && !str.IsNullOrEmpty())
|
||||
{
|
||||
ma = (UInt64)(str.TrimEnd(" kB").ToLong() / 1024);
|
||||
}
|
||||
|
||||
//低于3.14内核的版本用 free+cache
|
||||
var mf = (UInt64)(dic["MemFree"]?.TrimEnd(" kB").ToLong() / 1024 ?? 0);
|
||||
var mc = (UInt64)(dic["Cached"]?.TrimEnd(" kB").ToLong() / 1024 ?? 0);
|
||||
var bf = (UInt64)(dic["Buffers"]?.TrimEnd(" kB").ToLong() / 1024 ?? 0);
|
||||
|
||||
var free = mf + mc + bf;
|
||||
|
||||
AvailableMemory = ma > free ? ma : free;
|
||||
}
|
||||
|
||||
//低于3.14内核的版本用 free+cache
|
||||
var mf = (UInt64)(dic["MemFree"]?.TrimEnd(" kB").ToLong() ?? 0);
|
||||
var mc = (UInt64)(dic["Cached"]?.TrimEnd(" kB").ToLong() ?? 0);
|
||||
var bf = (UInt64)(dic["Buffers"]?.TrimEnd(" kB").ToLong() ?? 0);
|
||||
|
||||
var free = mf + mc + bf;
|
||||
|
||||
AvailableMemory = ma > free ? ma : free;
|
||||
}
|
||||
|
||||
// A2/A4温度获取,Buildroot,CPU温度和主板温度
|
||||
|
@@ -180,6 +180,12 @@ public static class Runtime
|
||||
#endregion
|
||||
|
||||
#region 扩展
|
||||
|
||||
public static Int64 AppStartTick = TickCount64;
|
||||
|
||||
/// <summary>软件启动以来的毫秒数</summary>
|
||||
public static Int64 AppTickCount64 => TickCount64-AppStartTick;
|
||||
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
/// <summary>系统启动以来的毫秒数</summary>
|
||||
public static Int64 TickCount64 => Environment.TickCount64;
|
||||
@@ -196,6 +202,8 @@ public static class Runtime
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/// <summary>获取当前UTC时间。基于全局时间提供者,在星尘应用中会屏蔽服务器时间差</summary>
|
||||
/// <returns></returns>
|
||||
public static DateTimeOffset UtcNow => TimerScheduler.GlobalTimeProvider.GetUtcNow();
|
||||
@@ -248,7 +256,7 @@ public static class Runtime
|
||||
|
||||
return dic;
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region 设置
|
||||
private static Boolean? _createConfigOnMissing;
|
||||
|
@@ -94,6 +94,7 @@ public static class XTrace
|
||||
|
||||
static XTrace()
|
||||
{
|
||||
_ = Runtime.AppTickCount64;
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
|
||||
AppDomain.CurrentDomain.ProcessExit += OnProcessExit;
|
||||
|
@@ -1,9 +1,9 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<PluginVersion>10.12.6</PluginVersion>
|
||||
<ProPluginVersion>10.12.6</ProPluginVersion>
|
||||
<DefaultVersion>10.12.6</DefaultVersion>
|
||||
<PluginVersion>10.12.7</PluginVersion>
|
||||
<ProPluginVersion>10.12.7</ProPluginVersion>
|
||||
<DefaultVersion>10.12.7</DefaultVersion>
|
||||
<AuthenticationVersion>10.11.6</AuthenticationVersion>
|
||||
<SourceGeneratorVersion>10.11.6</SourceGeneratorVersion>
|
||||
<NET8Version>8.0.21</NET8Version>
|
||||
|
@@ -386,7 +386,7 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
ManageHelper.CheckChannelCount(insertData.Count);
|
||||
|
||||
using var db = GetDB();
|
||||
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory < 2 * 1024 * 1024 || WebEnableVariable.WebEnable == false)
|
||||
if (GlobalData.HardwareJob.HardwareInfo.AvailableMemory < 2 * 1024 * 1024 || WebEnableVariable.WebEnable == false)
|
||||
{
|
||||
await db.BulkCopyAsync(insertData, 10000).ConfigureAwait(false);
|
||||
await db.BulkUpdateAsync(upData, 10000).ConfigureAwait(false);
|
||||
|
@@ -387,7 +387,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
ManageHelper.CheckDeviceCount(insertData.Count);
|
||||
|
||||
using var db = GetDB();
|
||||
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory < 2 * 1024 * 1024 || WebEnableVariable.WebEnable == false)
|
||||
if (GlobalData.HardwareJob.HardwareInfo.AvailableMemory < 2 * 1024 * 1024 || WebEnableVariable.WebEnable == false)
|
||||
{
|
||||
await db.BulkCopyAsync(insertData, 10000).ConfigureAwait(false);
|
||||
await db.BulkUpdateAsync(upData, 10000).ConfigureAwait(false);
|
||||
|
@@ -63,7 +63,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
|
||||
const int highMemorySize = 100000;
|
||||
const long memoryThreshold = 2L * 1024 * 1024; // 2GB,单位KB
|
||||
|
||||
return (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory > memoryThreshold && WebEnableVariable.WebEnable == true)
|
||||
return (GlobalData.HardwareJob.HardwareInfo.AvailableMemory > memoryThreshold && WebEnableVariable.WebEnable == true)
|
||||
? highMemorySize
|
||||
: defaultSize;
|
||||
}
|
||||
|
@@ -207,7 +207,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
|
||||
var result = await db.UseTranAsync(async () =>
|
||||
{
|
||||
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory < 2 * 1024 * 1024 || WebEnableVariable.WebEnable == false)
|
||||
if (GlobalData.HardwareJob.HardwareInfo.AvailableMemory < 2 * 1024 * 1024 || WebEnableVariable.WebEnable == false)
|
||||
{
|
||||
await db.BulkCopyAsync(newChannels, 10000).ConfigureAwait(false);
|
||||
await db.BulkCopyAsync(newDevices, 10000).ConfigureAwait(false);
|
||||
@@ -342,7 +342,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
|
||||
var result = await db.UseTranAsync(async () =>
|
||||
{
|
||||
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory < 2 * 1024 * 1024 || WebEnableVariable.WebEnable == false)
|
||||
if (GlobalData.HardwareJob.HardwareInfo.AvailableMemory < 2 * 1024 * 1024 || WebEnableVariable.WebEnable == false)
|
||||
{
|
||||
await db.BulkCopyAsync(newChannels, 10000).ConfigureAwait(false);
|
||||
await db.BulkCopyAsync(newDevices, 10000).ConfigureAwait(false);
|
||||
@@ -620,7 +620,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
[OperDesc("ExportVariable", isRecordPar: false, localizerType: typeof(Variable))]
|
||||
public async Task<Dictionary<string, object>> ExportVariableAsync(GatewayExportFilter exportFilter)
|
||||
{
|
||||
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory < 4 * 1024 * 1024 || WebEnableVariable.WebEnable == false)
|
||||
if (GlobalData.HardwareJob.HardwareInfo.AvailableMemory < 4 * 1024 * 1024 || WebEnableVariable.WebEnable == false)
|
||||
{
|
||||
var whereQuery = await GetWhereEnumerableFunc(exportFilter).ConfigureAwait(false);
|
||||
//导出
|
||||
@@ -694,7 +694,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
{
|
||||
ManageHelper.CheckVariableCount(insertData.Count);
|
||||
using var db = GetDB();
|
||||
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory < 2 * 1024 * 1024 || WebEnableVariable.WebEnable == false)
|
||||
if (GlobalData.HardwareJob.HardwareInfo.AvailableMemory < 2 * 1024 * 1024 || WebEnableVariable.WebEnable == false)
|
||||
{
|
||||
await db.BulkCopyAsync(insertData, 10000).ConfigureAwait(false);
|
||||
await db.BulkUpdateAsync(upData, 10000).ConfigureAwait(false);
|
||||
|
@@ -17,6 +17,8 @@ using Photino.Blazor;
|
||||
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
using ThingsGateway.NewLife.Log;
|
||||
|
||||
namespace ThingsGateway.Server;
|
||||
@@ -28,6 +30,23 @@ internal sealed class Program
|
||||
[STAThread]
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
|
||||
{
|
||||
if (e.ExceptionObject is OutOfMemoryException)
|
||||
{
|
||||
try
|
||||
{
|
||||
XTrace.WriteLine($"[OOM DETECTED]");
|
||||
XTrace.WriteLine(MachineInfo.GetCurrent().ToJsonNetString());
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
//当前工作目录设为程序集的基目录
|
||||
System.IO.Directory.SetCurrentDirectory(AppContext.BaseDirectory);
|
||||
// 增加中文编码支持
|
||||
|
@@ -16,6 +16,7 @@ using System.Text;
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.DB;
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
using ThingsGateway.NewLife.Log;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
@@ -27,6 +28,24 @@ public class Program
|
||||
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
|
||||
{
|
||||
if (e.ExceptionObject is OutOfMemoryException)
|
||||
{
|
||||
try
|
||||
{
|
||||
XTrace.WriteLine($"[OOM DETECTED]");
|
||||
XTrace.WriteLine(MachineInfo.GetCurrent().ToJsonNetString());
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
await Task.Delay(2000).ConfigureAwait(false);
|
||||
//当前工作目录设为程序集的基目录
|
||||
System.IO.Directory.SetCurrentDirectory(AppContext.BaseDirectory);
|
||||
|
Reference in New Issue
Block a user