@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.
Files changed (47) hide show
  1. package/README.md +907 -0
  2. package/bin/OpenHardwareMonitorLib.dll +0 -0
  3. package/index.js +192 -0
  4. package/lib/allhardware.js +35 -0
  5. package/lib/backend-usage.js +103 -0
  6. package/lib/battery.js +76 -0
  7. package/lib/disk-watcher.js +156 -0
  8. package/lib/disk.js +77 -0
  9. package/lib/gpu.js +70 -0
  10. package/lib/hardware-service.js +284 -0
  11. package/lib/memory.js +59 -0
  12. package/lib/monitor.js +176 -0
  13. package/lib/network.js +75 -0
  14. package/lib/ohm-daemon.js +208 -0
  15. package/lib/ohm.js +41 -0
  16. package/lib/perfctr.js +43 -0
  17. package/lib/process-icon.js +76 -0
  18. package/lib/process-ops.js +218 -0
  19. package/lib/processes.js +232 -0
  20. package/lib/ps.js +93 -0
  21. package/lib/public-ip.js +124 -0
  22. package/lib/services.js +43 -0
  23. package/lib/sysinfo-daemon.js +188 -0
  24. package/lib/system.js +77 -0
  25. package/lib/usb.js +49 -0
  26. package/lib/win32-procs.js +241 -0
  27. package/package.json +50 -0
  28. package/scripts/get-all-hardware.ps1 +98 -0
  29. package/scripts/get-lhm-temp.ps1 +119 -0
  30. package/scripts/ohm-daemon.ps1 +141 -0
  31. package/scripts/sysinfo-daemon.ps1 +386 -0
  32. package/test/check-bugs.js +182 -0
  33. package/test/public/gj.pay.ali.jpg +0 -0
  34. package/test/public/gj.pay.wx.jpg +0 -0
  35. package/test/public/index.html +996 -0
  36. package/test/public/zdl.pay.ali.jpg +0 -0
  37. package/test/public/zdl.pay.wx.jpg +0 -0
  38. package/test/scan-encoding.js +77 -0
  39. package/test/server.js +255 -0
  40. package/test/test-all.js +180 -0
  41. package/test/test-daemon.js +51 -0
  42. package/test/test-kill-name.js +30 -0
  43. package/test/test-monitor.js +78 -0
  44. package/test/test-new-features.js +93 -0
  45. package/test/test-service.js +91 -0
  46. package/test/test-sysinfo-daemon.js +95 -0
  47. 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 };