@daimazun/hardware-info 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +907 -0
- package/bin/OpenHardwareMonitorLib.dll +0 -0
- package/index.js +192 -0
- package/lib/allhardware.js +35 -0
- package/lib/backend-usage.js +103 -0
- package/lib/battery.js +76 -0
- package/lib/disk-watcher.js +156 -0
- package/lib/disk.js +77 -0
- package/lib/gpu.js +70 -0
- package/lib/hardware-service.js +284 -0
- package/lib/memory.js +59 -0
- package/lib/monitor.js +176 -0
- package/lib/network.js +75 -0
- package/lib/ohm-daemon.js +208 -0
- package/lib/ohm.js +41 -0
- package/lib/perfctr.js +43 -0
- package/lib/process-icon.js +76 -0
- package/lib/process-ops.js +218 -0
- package/lib/processes.js +232 -0
- package/lib/ps.js +93 -0
- package/lib/public-ip.js +124 -0
- package/lib/services.js +43 -0
- package/lib/sysinfo-daemon.js +188 -0
- package/lib/system.js +77 -0
- package/lib/usb.js +49 -0
- package/lib/win32-procs.js +241 -0
- package/package.json +50 -0
- package/scripts/get-all-hardware.ps1 +98 -0
- package/scripts/get-lhm-temp.ps1 +119 -0
- package/scripts/ohm-daemon.ps1 +141 -0
- package/scripts/sysinfo-daemon.ps1 +386 -0
- package/test/check-bugs.js +182 -0
- package/test/public/gj.pay.ali.jpg +0 -0
- package/test/public/gj.pay.wx.jpg +0 -0
- package/test/public/index.html +996 -0
- package/test/public/zdl.pay.ali.jpg +0 -0
- package/test/public/zdl.pay.wx.jpg +0 -0
- package/test/scan-encoding.js +77 -0
- package/test/server.js +255 -0
- package/test/test-all.js +180 -0
- package/test/test-daemon.js +51 -0
- package/test/test-kill-name.js +30 -0
- package/test/test-monitor.js +78 -0
- package/test/test-new-features.js +93 -0
- package/test/test-service.js +91 -0
- package/test/test-sysinfo-daemon.js +95 -0
- package/test/test.js +63 -0
package/lib/gpu.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const { runPsJson } = require('./ps');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 获取显卡和显示器信息
|
|
5
|
+
*/
|
|
6
|
+
async function getGpuInfo() {
|
|
7
|
+
const cmd = `
|
|
8
|
+
$gpus = Get-CimInstance Win32_VideoController
|
|
9
|
+
$monitors = Get-CimInstance Win32_DesktopMonitor
|
|
10
|
+
$desktop = Get-CimInstance Win32_Desktop | Select-Object -First 1
|
|
11
|
+
|
|
12
|
+
$gpuList = @()
|
|
13
|
+
foreach ($g in $gpus) {
|
|
14
|
+
$hasResolution = $g.CurrentHorizontalResolution -ne $null -and $g.CurrentHorizontalResolution -gt 0
|
|
15
|
+
$nameLower = $g.Name.ToLower()
|
|
16
|
+
$isVirtual = (-not $hasResolution) -or $nameLower -match 'idd|virtual|mirror|dummy|wddm|remote'
|
|
17
|
+
if ($hasResolution) {
|
|
18
|
+
$displayMode = "$($g.CurrentHorizontalResolution)x$($g.CurrentVerticalResolution)@$($g.CurrentRefreshRate)Hz"
|
|
19
|
+
} elseif ($isVirtual) {
|
|
20
|
+
$displayMode = 'Virtual Display'
|
|
21
|
+
} else {
|
|
22
|
+
$displayMode = 'No Active Output'
|
|
23
|
+
}
|
|
24
|
+
$gpuList += [PSCustomObject]@{
|
|
25
|
+
name = $g.Name
|
|
26
|
+
isVirtual = $isVirtual
|
|
27
|
+
displayMode = $displayMode
|
|
28
|
+
adapterCompatibility = $g.AdapterCompatibility
|
|
29
|
+
adapterRAMMB = if ($g.AdapterRAM) { [math]::Round($g.AdapterRAM / 1MB, 2) } else { $null }
|
|
30
|
+
driverVersion = $g.DriverVersion
|
|
31
|
+
driverDate = if ($g.DriverDate) { $g.DriverDate.ToString('o') } else { $null }
|
|
32
|
+
videoProcessor = $g.VideoProcessor
|
|
33
|
+
videoArchitecture = $g.VideoArchitecture
|
|
34
|
+
currentHorizontalResolution = $g.CurrentHorizontalResolution
|
|
35
|
+
currentVerticalResolution = $g.CurrentVerticalResolution
|
|
36
|
+
currentRefreshRate = $g.CurrentRefreshRate
|
|
37
|
+
currentBitsPerPixel = $g.CurrentBitsPerPixel
|
|
38
|
+
maxRefreshRate = $g.MaxRefreshRate
|
|
39
|
+
minRefreshRate = $g.MinRefreshRate
|
|
40
|
+
videoModeDescription = $g.VideoModeDescription
|
|
41
|
+
deviceId = $g.DeviceID
|
|
42
|
+
pnpDeviceId = $g.PNPDeviceID
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
$monitorList = @()
|
|
47
|
+
foreach ($m in $monitors) {
|
|
48
|
+
$monitorList += [PSCustomObject]@{
|
|
49
|
+
name = $m.Name
|
|
50
|
+
monitorManufacturer = $m.MonitorManufacturer
|
|
51
|
+
monitorType = $m.MonitorType
|
|
52
|
+
screenHeight = $m.ScreenHeight
|
|
53
|
+
screenWidth = $m.ScreenWidth
|
|
54
|
+
deviceId = $m.DeviceID
|
|
55
|
+
pnpDeviceId = $m.PNPDeviceID
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
[PSCustomObject]@{
|
|
60
|
+
gpus = $gpuList
|
|
61
|
+
monitors = $monitorList
|
|
62
|
+
gpuCount = $gpuList.Count
|
|
63
|
+
monitorCount = $monitorList.Count
|
|
64
|
+
primaryResolution = ($gpuList | Where-Object { -not $_.isVirtual -and $_.currentHorizontalResolution } | Select-Object -First 1).displayMode
|
|
65
|
+
} | ConvertTo-Json -Depth 5 -Compress
|
|
66
|
+
`;
|
|
67
|
+
return runPsJson(cmd);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = { getGpuInfo };
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hardware-service.js — 统一常驻监控器
|
|
3
|
+
*
|
|
4
|
+
* 一个入口整合所有常驻模块,对外统一调用,无需分别创建和管理:
|
|
5
|
+
* - SysInfoDaemon 系统信息全模块(system/memory/disk/network/battery/gpu/usb/processes/services)
|
|
6
|
+
* - CpuTempMonitor CPU 温度自动采样(EventEmitter)
|
|
7
|
+
* - 进程 CPU% 采样器 自动启停(getProcesses includeCpu)
|
|
8
|
+
* - DiskWatcher 磁盘插拔监听(EventEmitter)
|
|
9
|
+
* - 公网 IP 带 60s 缓存
|
|
10
|
+
*
|
|
11
|
+
* 用法:
|
|
12
|
+
* const monitor = await createHardwareService();
|
|
13
|
+
* const temp = monitor.getCpuTemp(); // 最新 CPU 温度(自动采样)
|
|
14
|
+
* const mem = await monitor.getMemory(); // 系统信息
|
|
15
|
+
* const procs = await monitor.getProcesses({ top: 10 }); // 进程(自动带 CPU%)
|
|
16
|
+
* monitor.on('disk-change', ({ added }) => {
|
|
17
|
+
* }); // 磁盘插拔事件
|
|
18
|
+
* monitor.close();
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const { EventEmitter } = require('events');
|
|
22
|
+
const { createSysInfoDaemon } = require('./sysinfo-daemon');
|
|
23
|
+
const { createCpuTempMonitor } = require('./monitor');
|
|
24
|
+
const { createCpuTempDaemon } = require('./ohm-daemon');
|
|
25
|
+
const { createDiskWatcher } = require('./disk-watcher');
|
|
26
|
+
const { getProcesses } = require('./processes');
|
|
27
|
+
const { getPublicIp } = require('./public-ip');
|
|
28
|
+
const { getBackendUsage } = require('./backend-usage');
|
|
29
|
+
const { killProcess, showItemInFolder, findPidsByPort, findPidsByName } = require('./process-ops');
|
|
30
|
+
|
|
31
|
+
class HardwareService extends EventEmitter {
|
|
32
|
+
constructor(options = {}) {
|
|
33
|
+
super();
|
|
34
|
+
this.options = options;
|
|
35
|
+
this._started = false;
|
|
36
|
+
this._closed = false;
|
|
37
|
+
|
|
38
|
+
// 配置
|
|
39
|
+
this._cpuTempInterval = options.cpuTempInterval ?? 1000;
|
|
40
|
+
this._diskWatchInterval = options.diskWatchInterval ?? 2000;
|
|
41
|
+
this._enableCpuTemp = options.enableCpuTemp ?? true;
|
|
42
|
+
this._enableDiskWatcher = options.enableDiskWatcher ?? true;
|
|
43
|
+
|
|
44
|
+
// 子模块实例
|
|
45
|
+
this._sysInfo = null;
|
|
46
|
+
this._cpuTempMonitor = null;
|
|
47
|
+
this._diskWatcher = null;
|
|
48
|
+
|
|
49
|
+
// 最新 CPU 温度缓存
|
|
50
|
+
this._cpuTemp = null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 启动所有子模块(createHardwareService 时自动调用)
|
|
55
|
+
*/
|
|
56
|
+
async start() {
|
|
57
|
+
if (this._started || this._closed) return;
|
|
58
|
+
this._started = true;
|
|
59
|
+
|
|
60
|
+
// 1. SysInfoDaemon(系统信息全模块)
|
|
61
|
+
this._sysInfo = await createSysInfoDaemon({
|
|
62
|
+
systemCacheTtl: this.options.systemCacheTtl,
|
|
63
|
+
networkCacheTtl: this.options.networkCacheTtl,
|
|
64
|
+
maxRestarts: this.options.maxRestarts
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// 2. CPU 温度自动采样(异步初始化,不阻塞启动;非管理员时降级可能较慢)
|
|
68
|
+
if (this._enableCpuTemp && this._cpuTempInterval > 0) {
|
|
69
|
+
this._initCpuTempMonitor();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 3. 磁盘插拔监听
|
|
73
|
+
if (this._enableDiskWatcher) {
|
|
74
|
+
this._diskWatcher = createDiskWatcher({ interval: this._diskWatchInterval });
|
|
75
|
+
this._diskWatcher.on('change', (info) => {
|
|
76
|
+
this.emit('disk-change', info);
|
|
77
|
+
});
|
|
78
|
+
this._diskWatcher.start();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ========== 系统信息(代理到 SysInfoDaemon) ==========
|
|
83
|
+
|
|
84
|
+
async getSystem() { return this._sysInfo.getSystem(); }
|
|
85
|
+
async getMemory() { return this._sysInfo.getMemory(); }
|
|
86
|
+
async getDisk() { return this._sysInfo.getDisk(); }
|
|
87
|
+
async getNetwork() { return this._sysInfo.getNetwork(); }
|
|
88
|
+
async getBattery() { return this._sysInfo.getBattery(); }
|
|
89
|
+
async getGpu() { return this._sysInfo.getGpu(); }
|
|
90
|
+
async getUsb() { return this._sysInfo.getUsb(); }
|
|
91
|
+
async getServices(options = {}) { return this._sysInfo.getServices(options); }
|
|
92
|
+
async getAll(options = {}) { return this._sysInfo.getAll(options); }
|
|
93
|
+
|
|
94
|
+
// ========== 进程(自动带 CPU%) ==========
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 获取进程列表(默认 includeCpu=true,自动启动常驻采样器)
|
|
98
|
+
*/
|
|
99
|
+
async getProcesses(options = {}) {
|
|
100
|
+
const opts = { includeCpu: true, ...options };
|
|
101
|
+
return getProcesses(opts);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ========== CPU 温度 ==========
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 异步初始化 CPU 温度监控器(不阻塞 start)
|
|
108
|
+
*/
|
|
109
|
+
async _initCpuTempMonitor() {
|
|
110
|
+
try {
|
|
111
|
+
this._cpuTempMonitor = await createCpuTempMonitor({ interval: this._cpuTempInterval });
|
|
112
|
+
this._cpuTempMonitor.on('data', (data) => {
|
|
113
|
+
this._cpuTemp = data;
|
|
114
|
+
this.emit('cpu-temp', data);
|
|
115
|
+
});
|
|
116
|
+
this._cpuTempMonitor.on('error', (err) => {
|
|
117
|
+
this.emit('error', { source: 'cpu-temp', error: err });
|
|
118
|
+
});
|
|
119
|
+
} catch (e) {
|
|
120
|
+
this.emit('error', { source: 'cpu-temp-init', error: e });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 获取最新 CPU 温度(自动采样模式下同步返回,无需 await)
|
|
126
|
+
* @returns {object|null} 最新 CPU 温度数据,未采样到则返回 null
|
|
127
|
+
*/
|
|
128
|
+
getCpuTemp() {
|
|
129
|
+
return this._cpuTemp;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 手动拉取一次 CPU 温度(cpuTempInterval=0 时用,或需要立即获取最新值)
|
|
134
|
+
*/
|
|
135
|
+
async fetchCpuTemp() {
|
|
136
|
+
if (this._cpuTempMonitor && this._cpuTemp) {
|
|
137
|
+
// 自动采样模式下已有最新值
|
|
138
|
+
return this._cpuTemp;
|
|
139
|
+
}
|
|
140
|
+
// 未启用自动采样时,临时创建 Daemon 拉一次
|
|
141
|
+
const daemon = await createCpuTempDaemon();
|
|
142
|
+
try {
|
|
143
|
+
this._cpuTemp = await daemon.getTemp();
|
|
144
|
+
return this._cpuTemp;
|
|
145
|
+
} finally {
|
|
146
|
+
await daemon.close();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ========== 公网 IP ==========
|
|
151
|
+
|
|
152
|
+
async getPublicIp(options = {}) {
|
|
153
|
+
return getPublicIp(options);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ========== 磁盘 ==========
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 获取当前所有盘符
|
|
160
|
+
*/
|
|
161
|
+
getDisks() {
|
|
162
|
+
return this._diskWatcher ? this._diskWatcher.getDisks() : [];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ========== 后端资源占用 ==========
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* 获取当前后端服务(Node.js + 所有子进程)的内存和 CPU 占用
|
|
169
|
+
* 同步,~5-10ms,高频安全
|
|
170
|
+
*/
|
|
171
|
+
getBackendUsage() {
|
|
172
|
+
return getBackendUsage();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ========== 进程操作 ==========
|
|
176
|
+
|
|
177
|
+
async killProcess(pid) {
|
|
178
|
+
return killProcess(pid);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
showItemInFolder(filePath) {
|
|
182
|
+
return showItemInFolder(filePath);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* 查找占用指定端口的进程 PID
|
|
187
|
+
* @param {number} port - 端口号
|
|
188
|
+
* @param {'tcp'|'udp'|'all'} [protocol='all']
|
|
189
|
+
* @returns {Promise<number[]>}
|
|
190
|
+
*/
|
|
191
|
+
findPidsByPort(port, protocol) {
|
|
192
|
+
return findPidsByPort(port, protocol);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* 按进程名查找 PID
|
|
197
|
+
* @param {string|string[]} name - 进程名或进程名数组
|
|
198
|
+
* @returns {number[]}
|
|
199
|
+
*/
|
|
200
|
+
findPidsByName(name) {
|
|
201
|
+
return findPidsByName(name);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ========== 控制 ==========
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 暂停所有采样(保留状态,可 resume)
|
|
208
|
+
*/
|
|
209
|
+
pause() {
|
|
210
|
+
if (this._cpuTempMonitor) this._cpuTempMonitor.stop();
|
|
211
|
+
if (this._diskWatcher) this._diskWatcher.stop();
|
|
212
|
+
this.emit('pause');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* 恢复采样
|
|
217
|
+
*/
|
|
218
|
+
resume() {
|
|
219
|
+
if (this._cpuTempMonitor) this._cpuTempMonitor.start();
|
|
220
|
+
if (this._diskWatcher) this._diskWatcher.start();
|
|
221
|
+
this.emit('resume');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* 获取各子模块状态
|
|
226
|
+
*/
|
|
227
|
+
getStatus() {
|
|
228
|
+
return {
|
|
229
|
+
started: this._started,
|
|
230
|
+
closed: this._closed,
|
|
231
|
+
sysInfoReady: !!this._sysInfo,
|
|
232
|
+
cpuTempEnabled: this._enableCpuTemp,
|
|
233
|
+
cpuTempRunning: !!(this._cpuTempMonitor && this._cpuTempMonitor.running),
|
|
234
|
+
cpuTempHasData: !!this._cpuTemp,
|
|
235
|
+
diskWatcherEnabled: this._enableDiskWatcher,
|
|
236
|
+
diskWatcherRunning: !!this._diskWatcher,
|
|
237
|
+
currentDisks: this.getDisks()
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* 关闭所有子模块,释放资源
|
|
243
|
+
*/
|
|
244
|
+
async close() {
|
|
245
|
+
if (this._closed) return;
|
|
246
|
+
this._closed = true;
|
|
247
|
+
this._started = false;
|
|
248
|
+
|
|
249
|
+
if (this._cpuTempMonitor) {
|
|
250
|
+
this._cpuTempMonitor.stop();
|
|
251
|
+
this._cpuTempMonitor = null;
|
|
252
|
+
}
|
|
253
|
+
if (this._diskWatcher) {
|
|
254
|
+
this._diskWatcher.stop();
|
|
255
|
+
this._diskWatcher = null;
|
|
256
|
+
}
|
|
257
|
+
if (this._sysInfo) {
|
|
258
|
+
await this._sysInfo.close();
|
|
259
|
+
this._sysInfo = null;
|
|
260
|
+
}
|
|
261
|
+
this._cpuTemp = null;
|
|
262
|
+
this.emit('close');
|
|
263
|
+
this.removeAllListeners();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* 创建统一常驻监控器(自动启动)
|
|
269
|
+
* @param {Object} options
|
|
270
|
+
* @param {number} options.cpuTempInterval - CPU 温度采样间隔 ms,默认 1000,设 0 不自动采样
|
|
271
|
+
* @param {number} options.diskWatchInterval - 磁盘监听间隔 ms,默认 2000
|
|
272
|
+
* @param {number} options.systemCacheTtl - system 缓存 TTL ms,默认 30000
|
|
273
|
+
* @param {number} options.networkCacheTtl - 网卡配置缓存 TTL ms,默认 30000
|
|
274
|
+
* @param {boolean} options.enableCpuTemp - 是否启用 CPU 温度,默认 true
|
|
275
|
+
* @param {boolean} options.enableDiskWatcher - 是否启用磁盘监听,默认 true
|
|
276
|
+
* @returns {Promise<HardwareService>}
|
|
277
|
+
*/
|
|
278
|
+
async function createHardwareService(options = {}) {
|
|
279
|
+
const monitor = new HardwareService(options);
|
|
280
|
+
await monitor.start();
|
|
281
|
+
return monitor;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
module.exports = { createHardwareService, HardwareService };
|
package/lib/memory.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const { runPsJson } = require('./ps');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 获取内存详细信息:物理内存条(型号/频率/时序)、内存阵列、使用情况
|
|
5
|
+
*/
|
|
6
|
+
async function getMemoryInfo() {
|
|
7
|
+
const cmd = `
|
|
8
|
+
$sticks = Get-CimInstance Win32_PhysicalMemory
|
|
9
|
+
$array = Get-CimInstance Win32_PhysicalMemoryArray | Select-Object -First 1
|
|
10
|
+
$os = Get-CimInstance Win32_OperatingSystem
|
|
11
|
+
|
|
12
|
+
$stickList = @()
|
|
13
|
+
foreach ($s in $sticks) {
|
|
14
|
+
$stickList += [PSCustomObject]@{
|
|
15
|
+
deviceLocator = $s.DeviceLocator
|
|
16
|
+
bankLabel = $s.BankLabel
|
|
17
|
+
capacityGB = [math]::Round($s.Capacity / 1GB, 2)
|
|
18
|
+
speedMHz = $s.Speed
|
|
19
|
+
configuredSpeedMHz = $s.ConfiguredClockSpeed
|
|
20
|
+
memoryType = if ($s.MemoryType) { $s.MemoryType } else { $null }
|
|
21
|
+
typeDetail = $s.TypeDetail
|
|
22
|
+
manufacturer = $s.Manufacturer
|
|
23
|
+
partNumber = $s.PartNumber.Trim()
|
|
24
|
+
serialNumber = $s.SerialNumber
|
|
25
|
+
dataWidth = $s.DataWidth
|
|
26
|
+
totalWidth = $s.TotalWidth
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
$totalPhysicalGB = [math]::Round($os.TotalVisibleMemorySize / 1MB, 2)
|
|
31
|
+
$freePhysicalGB = [math]::Round($os.FreePhysicalMemory / 1MB, 2)
|
|
32
|
+
$usedPhysicalGB = [math]::Round($totalPhysicalGB - $freePhysicalGB, 2)
|
|
33
|
+
|
|
34
|
+
$totalVirtualGB = [math]::Round($os.TotalVirtualMemorySize / 1MB, 2)
|
|
35
|
+
$freeVirtualGB = [math]::Round($os.FreeVirtualMemory / 1MB, 2)
|
|
36
|
+
|
|
37
|
+
[PSCustomObject]@{
|
|
38
|
+
totalPhysicalGB = $totalPhysicalGB
|
|
39
|
+
usedPhysicalGB = $usedPhysicalGB
|
|
40
|
+
freePhysicalGB = $freePhysicalGB
|
|
41
|
+
usedPercent = [math]::Round(($usedPhysicalGB / $totalPhysicalGB) * 100, 2)
|
|
42
|
+
totalVirtualGB = $totalVirtualGB
|
|
43
|
+
freeVirtualGB = $freeVirtualGB
|
|
44
|
+
sticks = $stickList
|
|
45
|
+
stickCount = $stickList.Count
|
|
46
|
+
array = if ($array) {
|
|
47
|
+
[PSCustomObject]@{
|
|
48
|
+
maxCapacityGB = [math]::Round($array.MaxCapacity / 1MB, 2)
|
|
49
|
+
memoryDevices = $array.MemoryDevices
|
|
50
|
+
use = $array.Use
|
|
51
|
+
location = $array.Location
|
|
52
|
+
}
|
|
53
|
+
} else { $null }
|
|
54
|
+
} | ConvertTo-Json -Depth 5 -Compress
|
|
55
|
+
`;
|
|
56
|
+
return runPsJson(cmd);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = { getMemoryInfo };
|
package/lib/monitor.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
const EventEmitter = require('events');
|
|
2
|
+
const { createCpuTempDaemon } = require('./ohm-daemon');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* CPU 温度高频监控器
|
|
6
|
+
* 基于常驻进程模式,定时轮询并通过事件推送数据
|
|
7
|
+
*
|
|
8
|
+
* 事件:
|
|
9
|
+
* 'start' - 监控启动
|
|
10
|
+
* 'data' - 每次读取到数据 (data)
|
|
11
|
+
* 'error' - 读取出错 (error)
|
|
12
|
+
* 'pause' - 已暂停
|
|
13
|
+
* 'resume' - 已恢复
|
|
14
|
+
* 'stop' - 监控停止
|
|
15
|
+
*/
|
|
16
|
+
class CpuTempMonitor extends EventEmitter {
|
|
17
|
+
/**
|
|
18
|
+
* @param {Object} options
|
|
19
|
+
* @param {number} options.interval - 轮询间隔(毫秒),默认 1000
|
|
20
|
+
* @param {number} options.maxRestarts - 常驻进程最大重启次数,默认 3
|
|
21
|
+
* @param {boolean} options.verbose - 是否输出详细日志
|
|
22
|
+
*/
|
|
23
|
+
constructor(options = {}) {
|
|
24
|
+
super();
|
|
25
|
+
this.interval = options.interval || 1000;
|
|
26
|
+
this.maxRestarts = options.maxRestarts || 3;
|
|
27
|
+
this.verbose = options.verbose || false;
|
|
28
|
+
|
|
29
|
+
this.daemon = null;
|
|
30
|
+
this.timer = null;
|
|
31
|
+
this.running = false;
|
|
32
|
+
this.paused = false;
|
|
33
|
+
this.lastData = null;
|
|
34
|
+
this._reading = false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 启动监控
|
|
39
|
+
*/
|
|
40
|
+
async start() {
|
|
41
|
+
if (this.running) return;
|
|
42
|
+
|
|
43
|
+
this.daemon = await createCpuTempDaemon({
|
|
44
|
+
maxRestarts: this.maxRestarts,
|
|
45
|
+
verbose: this.verbose
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
this.running = true;
|
|
49
|
+
this.paused = false;
|
|
50
|
+
this.emit('start');
|
|
51
|
+
this._scheduleNext();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 立即读取一次(不等待下一个轮询周期)
|
|
56
|
+
*/
|
|
57
|
+
async readNow() {
|
|
58
|
+
if (!this.daemon) throw new Error('Monitor not started');
|
|
59
|
+
const data = await this.daemon.getCpuTemp();
|
|
60
|
+
this.lastData = data;
|
|
61
|
+
this.emit('data', data);
|
|
62
|
+
return data;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 动态调整轮询间隔
|
|
67
|
+
* @param {number} ms - 新的间隔(毫秒)
|
|
68
|
+
*/
|
|
69
|
+
setInterval(ms) {
|
|
70
|
+
if (typeof ms !== 'number' || ms < 10) {
|
|
71
|
+
throw new Error('Interval must be >= 10ms');
|
|
72
|
+
}
|
|
73
|
+
this.interval = ms;
|
|
74
|
+
if (this.verbose) console.log(`[monitor] interval changed to ${ms}ms`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 暂停轮询(进程保持运行,不读数据)
|
|
79
|
+
*/
|
|
80
|
+
pause() {
|
|
81
|
+
if (!this.running || this.paused) return;
|
|
82
|
+
this.paused = true;
|
|
83
|
+
if (this.timer) {
|
|
84
|
+
clearTimeout(this.timer);
|
|
85
|
+
this.timer = null;
|
|
86
|
+
}
|
|
87
|
+
this.emit('pause');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 恢复轮询
|
|
92
|
+
*/
|
|
93
|
+
resume() {
|
|
94
|
+
if (!this.running || !this.paused) return;
|
|
95
|
+
this.paused = false;
|
|
96
|
+
this.emit('resume');
|
|
97
|
+
this._scheduleNext();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 停止监控,关闭常驻进程
|
|
102
|
+
*/
|
|
103
|
+
async stop() {
|
|
104
|
+
if (!this.running) return;
|
|
105
|
+
|
|
106
|
+
this.running = false;
|
|
107
|
+
this.paused = false;
|
|
108
|
+
if (this.timer) {
|
|
109
|
+
clearTimeout(this.timer);
|
|
110
|
+
this.timer = null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (this.daemon) {
|
|
114
|
+
await this.daemon.close();
|
|
115
|
+
this.daemon = null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
this.emit('stop');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 获取最近一次数据
|
|
123
|
+
*/
|
|
124
|
+
getLastData() {
|
|
125
|
+
return this.lastData;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 是否正在运行
|
|
130
|
+
*/
|
|
131
|
+
isRunning() {
|
|
132
|
+
return this.running && !this.paused;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
_scheduleNext() {
|
|
136
|
+
if (!this.running || this.paused) return;
|
|
137
|
+
|
|
138
|
+
this.timer = setTimeout(() => {
|
|
139
|
+
this._doRead();
|
|
140
|
+
}, this.interval);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async _doRead() {
|
|
144
|
+
if (!this.running || this.paused) return;
|
|
145
|
+
if (this._reading) {
|
|
146
|
+
// 上一次还没读完,跳过本次,避免堆积
|
|
147
|
+
this._scheduleNext();
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
this._reading = true;
|
|
152
|
+
try {
|
|
153
|
+
const data = await this.daemon.getCpuTemp();
|
|
154
|
+
this.lastData = data;
|
|
155
|
+
this.emit('data', data);
|
|
156
|
+
} catch (e) {
|
|
157
|
+
this.emit('error', e);
|
|
158
|
+
} finally {
|
|
159
|
+
this._reading = false;
|
|
160
|
+
this._scheduleNext();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 创建并启动一个 CPU 温度监控器
|
|
167
|
+
* @param {Object} options - 同 CpuTempMonitor 构造参数
|
|
168
|
+
* @returns {Promise<CpuTempMonitor>}
|
|
169
|
+
*/
|
|
170
|
+
async function createCpuTempMonitor(options = {}) {
|
|
171
|
+
const monitor = new CpuTempMonitor(options);
|
|
172
|
+
await monitor.start();
|
|
173
|
+
return monitor;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
module.exports = { CpuTempMonitor, createCpuTempMonitor };
|
package/lib/network.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const { runPsJson } = require('./ps');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 获取网络信息:网卡、IP、实时网速、WiFi
|
|
5
|
+
*/
|
|
6
|
+
async function getNetworkInfo() {
|
|
7
|
+
const cmd = `
|
|
8
|
+
$adapters = Get-CimInstance Win32_NetworkAdapter | Where-Object { $_.PhysicalAdapter -eq $true -or $_.NetConnectionID }
|
|
9
|
+
$configs = Get-CimInstance Win32_NetworkAdapterConfiguration | Where-Object { $_.IPEnabled }
|
|
10
|
+
$ifStats = Get-CimInstance Win32_PerfFormattedData_Tcpip_NetworkInterface
|
|
11
|
+
|
|
12
|
+
# WiFi 信息
|
|
13
|
+
$wifi = $null
|
|
14
|
+
try {
|
|
15
|
+
$wlan = netsh wlan show interfaces 2>$null
|
|
16
|
+
if ($wlan -match 'SSID') {
|
|
17
|
+
$ssid = ($wlan | Select-String 'SSID' | Select-Object -First 1).ToString() -replace '.*:\s*', ''
|
|
18
|
+
$signal = ($wlan | Select-String 'Signal' | Select-Object -First 1).ToString() -replace '.*:\s*', '' -replace '%', ''
|
|
19
|
+
$channel = ($wlan | Select-String 'Channel' | Select-Object -First 1).ToString() -replace '.*:\s*', ''
|
|
20
|
+
$wifi = [PSCustomObject]@{
|
|
21
|
+
ssId = $ssid.Trim()
|
|
22
|
+
signalPercent = if ($signal) { [int]$signal.Trim() } else { $null }
|
|
23
|
+
channel = if ($channel) { [int]$channel.Trim() } else { $null }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
} catch {}
|
|
27
|
+
|
|
28
|
+
$adapterList = @()
|
|
29
|
+
foreach ($a in $adapters) {
|
|
30
|
+
$cfg = $configs | Where-Object { $_.Index -eq $a.Index } | Select-Object -First 1
|
|
31
|
+
# 性能计数器会把网卡名中的圆括号替换成方括号,如 Intel(R) -> Intel[R]
|
|
32
|
+
$counterName = $a.Name -replace '\(', '[' -replace '\)', ']'
|
|
33
|
+
$stat = $ifStats | Where-Object { $_.Name -eq $a.NetConnectionID -or $_.Name -eq $counterName -or $_.Name -like "*$counterName*" } | Select-Object -First 1
|
|
34
|
+
|
|
35
|
+
$adapterList += [PSCustomObject]@{
|
|
36
|
+
name = $a.Name
|
|
37
|
+
connectionName = $a.NetConnectionID
|
|
38
|
+
description = $a.Description
|
|
39
|
+
manufacturer = $a.Manufacturer
|
|
40
|
+
macAddress = $a.MACAddress
|
|
41
|
+
adapterType = $a.AdapterType
|
|
42
|
+
speedMbps = if ($a.Speed) { [math]::Round($a.Speed / 1MB, 0) } else { $null }
|
|
43
|
+
status = $a.NetConnectionStatus
|
|
44
|
+
isPhysical = $a.PhysicalAdapter
|
|
45
|
+
interfaceIndex = $a.InterfaceIndex
|
|
46
|
+
ipAddresses = if ($cfg) { $cfg.IPAddress } else { @() }
|
|
47
|
+
ipSubnets = if ($cfg) { $cfg.IPSubnet } else { @() }
|
|
48
|
+
defaultIpGateway = if ($cfg) { $cfg.DefaultIPGateway } else { @() }
|
|
49
|
+
dnsServers = if ($cfg) { $cfg.DNSServerSearchOrder } else { @() }
|
|
50
|
+
dhcpEnabled = if ($cfg) { $cfg.DHCPEnabled } else { $null }
|
|
51
|
+
dhcpServer = if ($cfg) { $cfg.DHCPServer } else { $null }
|
|
52
|
+
bytesReceivedPerSec = if ($stat) { $stat.BytesReceivedPerSec } else { $null }
|
|
53
|
+
bytesSentPerSec = if ($stat) { $stat.BytesSentPerSec } else { $null }
|
|
54
|
+
packetsReceivedPerSec = if ($stat) { $stat.PacketsReceivedPerSec } else { $null }
|
|
55
|
+
packetsSentPerSec = if ($stat) { $stat.PacketsSentPerSec } else { $null }
|
|
56
|
+
totalBytesReceived = if ($stat) { $stat.BytesTotalPersec } else { $null }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
$activeAdapters = $adapterList | Where-Object { $_.status -eq 2 -and $_.ipAddresses.Count -gt 0 }
|
|
61
|
+
|
|
62
|
+
[PSCustomObject]@{
|
|
63
|
+
adapters = $adapterList
|
|
64
|
+
activeAdapters = $activeAdapters
|
|
65
|
+
adapterCount = $adapterList.Count
|
|
66
|
+
activeAdapterCount = @($activeAdapters).Count
|
|
67
|
+
wifi = $wifi
|
|
68
|
+
totalDownloadBytesPerSec = ($activeAdapters | Measure-Object -Property bytesReceivedPerSec -Sum).Sum
|
|
69
|
+
totalUploadBytesPerSec = ($activeAdapters | Measure-Object -Property bytesSentPerSec -Sum).Sum
|
|
70
|
+
} | ConvertTo-Json -Depth 6 -Compress
|
|
71
|
+
`;
|
|
72
|
+
return runPsJson(cmd, { timeout: 15000 });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = { getNetworkInfo };
|