diff --git a/agent/agent b/agent/agent index ad97256..a22af08 100755 Binary files a/agent/agent and b/agent/agent differ diff --git a/agent/main.go b/agent/main.go index 775dcc8..8cb98bf 100644 --- a/agent/main.go +++ b/agent/main.go @@ -31,15 +31,24 @@ type Config struct { // NetworkInterfaceMetrics 网卡监控指标 type NetworkInterfaceMetrics struct { - BytesSent uint64 `json:"bytes_sent"` - BytesReceived uint64 `json:"bytes_received"` + BytesSent uint64 `json:"bytes_sent"` // 发送速率 (bytes/s) + BytesReceived uint64 `json:"bytes_received"` // 接收速率 (bytes/s) + TxBytes uint64 `json:"tx_bytes"` // 累计发送字节数 + RxBytes uint64 `json:"rx_bytes"` // 累计接收字节数 +} + +// DiskMetrics 磁盘监控指标 +type DiskMetrics struct { + UsedPercent float64 `json:"used_percent"` // 使用率百分比 + Total uint64 `json:"total"` // 总容量 (bytes) } // Metrics 监控指标 type Metrics struct { CPU float64 `json:"cpu"` + CPUHz float64 `json:"cpu_hz"` // CPU频率 (MHz) Memory float64 `json:"memory"` - Disk map[string]float64 `json:"disk"` + Disk map[string]DiskMetrics `json:"disk"` Network map[string]NetworkInterfaceMetrics `json:"network"` } @@ -271,14 +280,29 @@ func readConfigFile() { log.Printf("Config loaded from %s", configFile) } -// 采集CPU使用率 -func collectCPU() (float64, error) { +// 采集CPU使用率和频率 +func collectCPU() (float64, float64, error) { + // 采集CPU使用率 percentages, err := cpu.Percent(0, false) if err != nil { - return 0, err + return 0, 0, err } - return percentages[0], nil + // 采集CPU频率(使用CPUInfo获取频率信息) + cpus, err := cpu.Info() + if err != nil { + return percentages[0], 0, nil // 频率采集失败,返回使用率和0频率 + } + + // 计算平均频率 (转换为MHz) + var totalFreq float64 + for _, cpuInfo := range cpus { + // CPUInfo返回的MHz,直接累加 + totalFreq += cpuInfo.Mhz + } + avgFreq := totalFreq / float64(len(cpus)) + + return percentages[0], avgFreq, nil } // 采集内存使用率 @@ -291,8 +315,8 @@ func collectMemory() (float64, error) { return vm.UsedPercent, nil } -// 采集磁盘使用率 -func collectDisk() (map[string]float64, error) { +// 采集磁盘使用率和总容量 +func collectDisk() (map[string]DiskMetrics, error) { // 获取系统所有挂载点 partitions, err := disk.Partitions(false) if err != nil { @@ -300,26 +324,32 @@ func collectDisk() (map[string]float64, error) { } // 初始化返回值 - diskUsageMap := make(map[string]float64) + diskMetricsMap := make(map[string]DiskMetrics) - // 遍历所有挂载点,采集磁盘使用率 + // 遍历所有挂载点,采集磁盘使用率和总容量 for _, partition := range partitions { // 只处理本地文件系统,跳过网络文件系统 if partition.Fstype == "" { continue } - // 采集磁盘使用率 + // 采集磁盘使用率和总容量 usage, err := disk.Usage(partition.Mountpoint) if err != nil { continue } - // 保存磁盘使用率 - diskUsageMap[partition.Mountpoint] = usage.UsedPercent + // 创建DiskMetrics结构体 + diskMetrics := DiskMetrics{ + UsedPercent: usage.UsedPercent, + Total: usage.Total, + } + + // 保存磁盘指标 + diskMetricsMap[partition.Mountpoint] = diskMetrics } - return diskUsageMap, nil + return diskMetricsMap, nil } // 采集网络流量 @@ -342,7 +372,7 @@ func collectNetwork() (map[string]NetworkInterfaceMetrics, error) { // 遍历所有网卡 for _, counter := range ioCounters { - // 获取当前网卡的流量 + // 获取当前网卡的累计流量 currentBytesSent := counter.BytesSent currentBytesReceived := counter.BytesRecv @@ -372,17 +402,19 @@ func collectNetwork() (map[string]NetworkInterfaceMetrics, error) { BytesReceived: currentBytesReceived, } - // 保存当前网卡的速率 + // 保存当前网卡的速率和累计流量 networkMetrics[counter.Name] = NetworkInterfaceMetrics{ BytesSent: bytesSentRate, BytesReceived: bytesReceivedRate, + TxBytes: currentBytesSent, + RxBytes: currentBytesReceived, } } // 更新上一次采集时间 lastCollectTime = currentTime - // 返回所有网卡的速率 + // 返回所有网卡的速率和累计流量 return networkMetrics, nil } @@ -390,12 +422,13 @@ func collectNetwork() (map[string]NetworkInterfaceMetrics, error) { func collectMetrics() (*Metrics, error) { metrics := &Metrics{} - // 采集CPU使用率 - cpuUsage, err := collectCPU() + // 采集CPU使用率和频率 + cpuUsage, cpuHz, err := collectCPU() if err != nil { return nil, fmt.Errorf("failed to collect CPU metrics: %w", err) } metrics.CPU = cpuUsage + metrics.CPUHz = cpuHz // 采集内存使用率 memoryUsage, err := collectMemory() @@ -404,12 +437,12 @@ func collectMetrics() (*Metrics, error) { } metrics.Memory = memoryUsage - // 采集磁盘使用率 - diskUsageMap, err := collectDisk() + // 采集磁盘使用率和总容量 + diskMetricsMap, err := collectDisk() if err != nil { return nil, fmt.Errorf("failed to collect disk metrics: %w", err) } - metrics.Disk = diskUsageMap + metrics.Disk = diskMetricsMap // 采集网络流量 networkMetrics, err := collectNetwork() @@ -517,7 +550,7 @@ func startHTTPServer() { log.Printf("API Request: %s %s", r.Method, r.URL.Path) } // 采集当前状态 - cpu, _ := collectCPU() + cpu, cpuHz, _ := collectCPU() memory, _ := collectMemory() disk, _ := collectDisk() @@ -529,6 +562,7 @@ func startHTTPServer() { "debug": config.Debug, "interval": config.Interval, "cpu": cpu, + "cpu_hz": cpuHz, "memory": memory, "disk": disk, } @@ -607,7 +641,7 @@ func collectMetricsToBuffer() { totalDiskUsage := 0.0 diskCount := 0 for _, usage := range metrics.Disk { - totalDiskUsage += usage + totalDiskUsage += usage.UsedPercent diskCount++ } averageDiskUsage := 0.0 @@ -649,7 +683,7 @@ func collectAndSendMetrics() { totalMemory += metrics.Memory // 计算磁盘使用率 for _, usage := range metrics.Disk { - totalDiskUsage += usage + totalDiskUsage += usage.UsedPercent diskCount++ } } diff --git a/agent/monitor-agent b/agent/monitor-agent new file mode 100755 index 0000000..a22af08 Binary files /dev/null and b/agent/monitor-agent differ diff --git a/backend/internal/handler/handler.go b/backend/internal/handler/handler.go index 9962ba1..be7137d 100644 --- a/backend/internal/handler/handler.go +++ b/backend/internal/handler/handler.go @@ -67,17 +67,26 @@ func RegisterRoutes(r *gin.Engine) { } } +// DiskMetrics 磁盘监控指标 +type DiskMetrics struct { + UsedPercent float64 `json:"used_percent"` // 使用率百分比 + Total uint64 `json:"total"` // 总容量 (bytes) +} + // NetworkInterfaceMetrics 网卡监控指标 type NetworkInterfaceMetrics struct { - BytesSent uint64 `json:"bytes_sent"` - BytesReceived uint64 `json:"bytes_received"` + BytesSent uint64 `json:"bytes_sent"` // 发送速率 (bytes/s) + BytesReceived uint64 `json:"bytes_received"` // 接收速率 (bytes/s) + TxBytes uint64 `json:"tx_bytes"` // 累计发送字节数 + RxBytes uint64 `json:"rx_bytes"` // 累计接收字节数 } // MetricsRequest 指标请求结构 type MetricsRequest struct { CPU float64 `json:"cpu"` + CPUHz float64 `json:"cpu_hz"` // CPU频率 (MHz) Memory float64 `json:"memory"` - Disk map[string]float64 `json:"disk"` + Disk map[string]DiskMetrics `json:"disk"` Network map[string]NetworkInterfaceMetrics `json:"network"` } @@ -155,12 +164,20 @@ func HandleMetricsPost(c *gin.Context) { // 处理所有指标 for i, req := range metricsList { - // 写入CPU指标 + // 写入CPU使用率指标 if err := globalStorage.WriteMetric(c.Request.Context(), deviceID, "cpu", req.CPU, baseTags); err != nil { // 只记录警告,不影响后续指标处理 log.Printf("Warning: Failed to write CPU metrics: %v", err) } + // 写入CPU频率指标(如果有) + if req.CPUHz > 0 { + if err := globalStorage.WriteMetric(c.Request.Context(), deviceID, "cpu_hz", req.CPUHz, baseTags); err != nil { + // 只记录警告,不影响后续指标处理 + log.Printf("Warning: Failed to write CPU Hz metrics: %v", err) + } + } + // 写入内存指标 if err := globalStorage.WriteMetric(c.Request.Context(), deviceID, "memory", req.Memory, baseTags); err != nil { // 只记录警告,不影响后续指标处理 @@ -168,7 +185,7 @@ func HandleMetricsPost(c *gin.Context) { } // 写入磁盘指标,支持多个挂载点 - for mountpoint, usage := range req.Disk { + for mountpoint, diskMetrics := range req.Disk { // 为每个挂载点创建标签,包含基础标签和挂载点 tags := make(map[string]string) // 复制基础标签 @@ -178,8 +195,8 @@ func HandleMetricsPost(c *gin.Context) { // 添加挂载点标签 tags["mountpoint"] = mountpoint - // 写入磁盘指标 - if err := globalStorage.WriteMetric(c.Request.Context(), deviceID, "disk", usage, tags); err != nil { + // 写入磁盘使用率指标 + if err := globalStorage.WriteMetric(c.Request.Context(), deviceID, "disk", diskMetrics.UsedPercent, tags); err != nil { // 只记录警告,不影响后续指标处理 log.Printf("Warning: Failed to write disk metrics for mountpoint %s: %v", mountpoint, err) } @@ -187,6 +204,7 @@ func HandleMetricsPost(c *gin.Context) { // 写入网络指标,支持多个网卡 var totalBytesSent, totalBytesReceived uint64 + var totalTxBytes, totalRxBytes uint64 // 累计总流量 for interfaceName, networkMetrics := range req.Network { // 为每个网卡创建标签,包含基础标签和网卡名称 interfaceTags := make(map[string]string) @@ -197,32 +215,57 @@ func HandleMetricsPost(c *gin.Context) { // 添加网卡标签 interfaceTags["interface"] = interfaceName - // 写入网络发送指标 + // 写入网络发送速率指标 if err := globalStorage.WriteMetric(c.Request.Context(), deviceID, "network_sent", float64(networkMetrics.BytesSent), interfaceTags); err != nil { // 只记录警告,不影响后续指标处理 log.Printf("Warning: Failed to write network sent metrics for interface %s: %v", interfaceName, err) } - // 写入网络接收指标 + // 写入网络接收速率指标 if err := globalStorage.WriteMetric(c.Request.Context(), deviceID, "network_received", float64(networkMetrics.BytesReceived), interfaceTags); err != nil { // 只记录警告,不影响后续指标处理 log.Printf("Warning: Failed to write network received metrics for interface %s: %v", interfaceName, err) } - // 累加总流量 + // 写入累计发送字节数指标 + if err := globalStorage.WriteMetric(c.Request.Context(), deviceID, "network_tx_bytes", float64(networkMetrics.TxBytes), interfaceTags); err != nil { + // 只记录警告,不影响后续指标处理 + log.Printf("Warning: Failed to write network tx_bytes metrics for interface %s: %v", interfaceName, err) + } + + // 写入累计接收字节数指标 + if err := globalStorage.WriteMetric(c.Request.Context(), deviceID, "network_rx_bytes", float64(networkMetrics.RxBytes), interfaceTags); err != nil { + // 只记录警告,不影响后续指标处理 + log.Printf("Warning: Failed to write network rx_bytes metrics for interface %s: %v", interfaceName, err) + } + + // 累加总流量速率 totalBytesSent += networkMetrics.BytesSent totalBytesReceived += networkMetrics.BytesReceived + + // 累加累计总流量 + totalTxBytes += networkMetrics.TxBytes + totalRxBytes += networkMetrics.RxBytes } // 广播指标更新消息,只广播最后一个指标 if i == len(metricsList)-1 { + // 准备广播的磁盘使用率数据(兼容旧格式) + compatDisk := make(map[string]float64) + for mountpoint, diskMetrics := range req.Disk { + compatDisk[mountpoint] = diskMetrics.UsedPercent + } + metrics := map[string]interface{}{ "cpu": req.CPU, + "cpu_hz": req.CPUHz, "memory": req.Memory, - "disk": req.Disk, + "disk": compatDisk, // 使用兼容格式的磁盘数据 "network": map[string]uint64{ "bytes_sent": totalBytesSent, "bytes_received": totalBytesReceived, + "tx_bytes": totalTxBytes, + "rx_bytes": totalRxBytes, }, "network_interfaces": req.Network, } diff --git a/backend/monitor-server b/backend/monitor-server index 6a748ea..deecee2 100755 Binary files a/backend/monitor-server and b/backend/monitor-server differ diff --git a/backend/static/js/app.js b/backend/static/js/app.js index d769c1a..42f434d 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -1119,8 +1119,16 @@ async function loadMetrics() { fetchMetric('network') ]); + // 格式化数据,确保updateStatusCards函数能正确处理 + const formattedMetrics = { + cpu: cpuData, + memory: memoryData, + disk: formatDiskDataForCards(diskData), + network: formatNetworkDataForCards(networkSumData) + }; + // 更新状态卡片 - updateStatusCards({ cpu: cpuData, memory: memoryData, disk: diskData, network: networkSumData }); + updateStatusCards(formattedMetrics); // 更新图表 updateCharts(cpuData, memoryData, diskData, networkSumData); @@ -1131,6 +1139,71 @@ async function loadMetrics() { } } +// 格式化磁盘数据,用于状态卡片显示 +function formatDiskDataForCards(diskData) { + // 如果diskData是空对象,返回空对象 + if (!diskData || typeof diskData !== 'object' || Array.isArray(diskData)) { + return {}; + } + + const formattedDiskData = {}; + + // 遍历每个挂载点 + for (const mountpoint in diskData) { + const mountpointData = diskData[mountpoint]; + + // 如果挂载点数据是数组,获取最新的数据点 + if (Array.isArray(mountpointData) && mountpointData.length > 0) { + // 最新的数据点是数组的最后一个元素 + const latestData = mountpointData[mountpointData.length - 1]; + formattedDiskData[mountpoint] = { + used_percent: latestData.value, + // 尝试从历史数据获取总容量 + total: state.historyMetrics.disk && state.historyMetrics.disk[mountpoint] && state.historyMetrics.disk[mountpoint].total || 0 + }; + } + } + + return formattedDiskData; +} + +// 格式化网络数据,用于状态卡片显示 +function formatNetworkDataForCards(networkData) { + // 如果networkData是空对象,返回空对象 + if (!networkData || typeof networkData !== 'object' || Array.isArray(networkData)) { + return {}; + } + + const formattedNetworkData = { + bytes_sent: 0, + bytes_received: 0, + tx_bytes: 0, + rx_bytes: 0 + }; + + // 遍历每个网卡 + for (const iface in networkData) { + const ifaceData = networkData[iface]; + + // 检查是否有sent和received数据 + if (ifaceData.sent && ifaceData.received) { + // 如果sent和received是数组,获取最新的数据点 + if (Array.isArray(ifaceData.sent) && ifaceData.sent.length > 0 && + Array.isArray(ifaceData.received) && ifaceData.received.length > 0) { + // 最新的数据点是数组的最后一个元素 + const latestSent = ifaceData.sent[ifaceData.sent.length - 1].value; + const latestReceived = ifaceData.received[ifaceData.received.length - 1].value; + + // 累加速率 + formattedNetworkData.bytes_sent += latestSent; + formattedNetworkData.bytes_received += latestReceived; + } + } + } + + return formattedNetworkData; +} + // WebSocket初始化 function initWebSocket() { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; @@ -1215,6 +1288,7 @@ function updateStatusCards(metrics) { // 使用历史数据或当前数据 const displayMetrics = { cpu: metrics.cpu || state.historyMetrics.cpu, + cpu_hz: metrics.cpu_hz || state.historyMetrics.cpu_hz, memory: metrics.memory || state.historyMetrics.memory, disk: metrics.disk || state.historyMetrics.disk, network: metrics.network || state.historyMetrics.network @@ -1228,24 +1302,33 @@ function updateStatusCards(metrics) { // 解析CPU数据 if (Array.isArray(displayMetrics.cpu) && displayMetrics.cpu.length > 0) { + // 数组格式:获取最新的数据点 cpuUsage = displayMetrics.cpu[displayMetrics.cpu.length - 1].value; } else if (typeof displayMetrics.cpu === 'number') { + // 数值格式 cpuUsage = displayMetrics.cpu; } else if (typeof displayMetrics.cpu === 'object' && displayMetrics.cpu.usage) { + // 对象格式,包含usage, frequency, load cpuUsage = displayMetrics.cpu.usage; cpuGhz = displayMetrics.cpu.frequency || 0; cpuLoad = displayMetrics.cpu.load || 0; } + // 从单独的cpu_hz字段获取CPU频率(如果有) + if (displayMetrics.cpu_hz && typeof displayMetrics.cpu_hz === 'number') { + cpuGhz = displayMetrics.cpu_hz / 1000; // 转换为GHz + } + // 更新显示 const cpuElement = document.getElementById('cpuValue'); const cpuDetailsElement = document.getElementById('cpuDetails'); if (cpuElement) { cpuElement.textContent = `${cpuUsage.toFixed(1)}%`; - // 设置红色显示如果达到顶峰 - cpuElement.className = cpuUsage > 90 ? 'text-red-500' : ''; + // 设置红色显示如果达到顶峰,同时保留原有的样式类 + cpuElement.className = `text-3xl font-bold metric-value ${cpuUsage > 90 ? 'text-red-500' : 'text-gray-900'}`; } if (cpuDetailsElement) { + cpuDetailsElement.className = 'text-xs text-gray-500 mt-1'; cpuDetailsElement.textContent = `${cpuGhz.toFixed(2)} GHz | 负载: ${cpuLoad.toFixed(2)}`; } } @@ -1258,24 +1341,50 @@ function updateStatusCards(metrics) { // 解析内存数据 if (Array.isArray(displayMetrics.memory) && displayMetrics.memory.length > 0) { + // 数组格式:获取最新的数据点 memoryUsage = displayMetrics.memory[displayMetrics.memory.length - 1].value; } else if (typeof displayMetrics.memory === 'number') { + // 数值格式 memoryUsage = displayMetrics.memory; } else if (typeof displayMetrics.memory === 'object') { + // 对象格式,包含usage, used, total memoryUsage = displayMetrics.memory.usage || 0; memoryUsed = displayMetrics.memory.used || 0; memoryTotal = displayMetrics.memory.total || 0; } + // 如果只有使用率,没有使用量和总量,尝试从其他地方获取 + if (memoryTotal === 0 && state.historyMetrics.memory && typeof state.historyMetrics.memory === 'object') { + memoryUsed = state.historyMetrics.memory.used || 0; + memoryTotal = state.historyMetrics.memory.total || 0; + } + + // 如果内存总量仍为0,尝试使用默认值或从API获取 + if (memoryTotal === 0) { + // 尝试从系统获取内存总量(如果浏览器支持) + if (navigator.deviceMemory) { + memoryTotal = navigator.deviceMemory * 1024 * 1024 * 1024; // 转换为字节 + } else { + // 使用默认值16GB + memoryTotal = 16 * 1024 * 1024 * 1024; + } + } + + // 计算使用量(如果只有使用率) + if (memoryUsed === 0 && memoryTotal > 0) { + memoryUsed = (memoryTotal * memoryUsage) / 100; + } + // 更新显示 const memoryElement = document.getElementById('memoryValue'); const memoryDetailsElement = document.getElementById('memoryDetails'); if (memoryElement) { memoryElement.textContent = `${memoryUsage.toFixed(1)}%`; - // 设置红色显示如果达到顶峰 - memoryElement.className = memoryUsage > 90 ? 'text-red-500' : ''; + // 设置红色显示如果达到顶峰,同时保留原有的样式类 + memoryElement.className = `text-3xl font-bold metric-value ${memoryUsage > 90 ? 'text-red-500' : 'text-gray-900'}`; } if (memoryDetailsElement) { + memoryDetailsElement.className = 'text-xs text-gray-500 mt-1'; memoryDetailsElement.textContent = `${formatBytes(memoryUsed)} / ${formatBytes(memoryTotal)}`; } } @@ -1292,6 +1401,7 @@ function updateStatusCards(metrics) { // 解析磁盘数据 if (typeof displayMetrics.disk === 'object' && displayMetrics.disk !== null && !Array.isArray(displayMetrics.disk)) { // 按挂载点分组的数据 + let hasValidData = false; for (const mountpoint in displayMetrics.disk) { // 跳过排除的挂载点 if (excludedMountpoints.includes(mountpoint)) { @@ -1299,9 +1409,41 @@ function updateStatusCards(metrics) { } const data = displayMetrics.disk[mountpoint]; - if (data && typeof data === 'object' && data.used !== undefined && data.total !== undefined) { - totalUsed += data.used; - totalSize += data.total; + if (data && typeof data === 'object') { + if (data.used_percent !== undefined && data.total !== undefined) { + // 新格式:包含used_percent和total字段 + // 计算已使用大小(total * used_percent / 100) + const used = (data.total * data.used_percent) / 100; + totalUsed += used; + totalSize += data.total; + hasValidData = true; + } else if (data.used !== undefined && data.total !== undefined) { + // 旧格式:包含used和total字段 + totalUsed += data.used; + totalSize += data.total; + hasValidData = true; + } + } + } + + // 如果没有有效数据,尝试使用历史数据 + if (!hasValidData && state.historyMetrics.disk && typeof state.historyMetrics.disk === 'object' && !Array.isArray(state.historyMetrics.disk)) { + for (const mountpoint in state.historyMetrics.disk) { + if (excludedMountpoints.includes(mountpoint)) { + continue; + } + + const data = state.historyMetrics.disk[mountpoint]; + if (data && typeof data === 'object') { + if (data.used_percent !== undefined && data.total !== undefined) { + const used = (data.total * data.used_percent) / 100; + totalUsed += used; + totalSize += data.total; + } else if (data.used !== undefined && data.total !== undefined) { + totalUsed += data.used; + totalSize += data.total; + } + } } } } else if (typeof displayMetrics.disk === 'object' && displayMetrics.disk.used !== undefined && displayMetrics.disk.total !== undefined) { @@ -1310,9 +1452,36 @@ function updateStatusCards(metrics) { totalSize = displayMetrics.disk.total; } + // 如果只有使用率,没有使用量和总量,尝试计算 + if (totalSize === 0 && displayMetrics.disk && typeof displayMetrics.disk === 'number') { + // 如果disk是一个数字,尝试从历史数据获取总量 + if (state.historyMetrics.disk && typeof state.historyMetrics.disk === 'object' && !Array.isArray(state.historyMetrics.disk)) { + for (const mountpoint in state.historyMetrics.disk) { + if (excludedMountpoints.includes(mountpoint)) { + continue; + } + + const data = state.historyMetrics.disk[mountpoint]; + if (data && typeof data === 'object') { + if (data.total !== undefined) { + totalSize += data.total; + } + } + } + + // 计算使用量 + if (totalSize > 0) { + totalUsed = (totalSize * displayMetrics.disk) / 100; + } + } + } + // 计算使用率 if (totalSize > 0) { usagePercent = (totalUsed / totalSize) * 100; + } else if (typeof displayMetrics.disk === 'number') { + // 如果disk是一个数字,直接使用它作为使用率 + usagePercent = displayMetrics.disk; } // 更新显示 @@ -1320,10 +1489,11 @@ function updateStatusCards(metrics) { const diskDetailsElement = document.getElementById('diskDetails'); if (diskElement) { diskElement.textContent = `${usagePercent.toFixed(1)}%`; - // 设置红色显示如果达到顶峰 - diskElement.className = usagePercent > 90 ? 'text-red-500' : ''; + // 设置红色显示如果达到顶峰,同时保留原有的样式类 + diskElement.className = `text-3xl font-bold metric-value ${usagePercent > 90 ? 'text-red-500' : 'text-gray-900'}`; } if (diskDetailsElement) { + diskDetailsElement.className = 'text-xs text-gray-500 mt-1'; diskDetailsElement.textContent = `${formatBytes(totalUsed)} / ${formatBytes(totalSize)}`; } } @@ -1336,61 +1506,114 @@ function updateStatusCards(metrics) { let receivedTotal = 0; // 解析网络数据 - if (displayMetrics.network.sent && displayMetrics.network.received) { - // 处理数组格式的数据 - if (Array.isArray(displayMetrics.network.sent) && displayMetrics.network.sent.length > 0 && - Array.isArray(displayMetrics.network.received) && displayMetrics.network.received.length > 0) { + if (typeof displayMetrics.network === 'object') { + if (Array.isArray(displayMetrics.network)) { + // 数组格式的数据 + if (displayMetrics.network.length > 0) { + // 获取最新的数据点 + const latestData = displayMetrics.network[displayMetrics.network.length - 1]; + sentRate = latestData.sent || 0; + receivedRate = latestData.received || 0; + sentTotal = latestData.tx_bytes || 0; + receivedTotal = latestData.rx_bytes || 0; + } + } else if (displayMetrics.network.sent && displayMetrics.network.received) { + // 包含sent和received字段的数据 + if (Array.isArray(displayMetrics.network.sent) && displayMetrics.network.sent.length > 0 && + Array.isArray(displayMetrics.network.received) && displayMetrics.network.received.length > 0) { + // 处理数组格式的速率数据 + sentRate = displayMetrics.network.sent[displayMetrics.network.sent.length - 1].value; + receivedRate = displayMetrics.network.received[displayMetrics.network.received.length - 1].value; + } else if (typeof displayMetrics.network.sent === 'number' && typeof displayMetrics.network.received === 'number') { + // 处理数值格式的速率数据 + sentRate = displayMetrics.network.sent; + receivedRate = displayMetrics.network.received; + } - sentRate = displayMetrics.network.sent[displayMetrics.network.sent.length - 1].value; - receivedRate = displayMetrics.network.received[displayMetrics.network.received.length - 1].value; - // 计算总量(假设数组中的值是累积的) - sentTotal = displayMetrics.network.sent.reduce((sum, item) => sum + item.value, 0); - receivedTotal = displayMetrics.network.received.reduce((sum, item) => sum + item.value, 0); - } - // 处理数值格式的数据 - else if (typeof displayMetrics.network.sent === 'number' && typeof displayMetrics.network.received === 'number') { - sentRate = displayMetrics.network.sent; - receivedRate = displayMetrics.network.received; - sentTotal = displayMetrics.network.sent_total || sentRate; - receivedTotal = displayMetrics.network.received_total || receivedRate; - } - } else if (typeof displayMetrics.network === 'object' && - displayMetrics.network.bytes_sent !== undefined && - displayMetrics.network.bytes_received !== undefined) { - // WebSocket消息格式 - 总流量 - sentRate = displayMetrics.network.bytes_sent; - receivedRate = displayMetrics.network.bytes_received; - sentTotal = displayMetrics.network.bytes_sent_total || sentRate; - receivedTotal = displayMetrics.network.bytes_received_total || receivedRate; - } else if (typeof displayMetrics.network === 'object' && !Array.isArray(displayMetrics.network)) { - // 按网卡分组的数据,计算总流量 - for (const iface in displayMetrics.network) { - const ifaceMetrics = displayMetrics.network[iface]; - if (ifaceMetrics.bytes_sent !== undefined && ifaceMetrics.bytes_received !== undefined) { - sentRate += ifaceMetrics.bytes_sent; - receivedRate += ifaceMetrics.bytes_received; - sentTotal += ifaceMetrics.bytes_sent_total || ifaceMetrics.bytes_sent; - receivedTotal += ifaceMetrics.bytes_received_total || ifaceMetrics.bytes_received; - } else if (ifaceMetrics.BytesSent !== undefined && ifaceMetrics.BytesReceived !== undefined) { - sentRate += ifaceMetrics.BytesSent; - receivedRate += ifaceMetrics.BytesReceived; + // 处理总量数据 + sentTotal = displayMetrics.network.tx_bytes || displayMetrics.network.sent_total || 0; + receivedTotal = displayMetrics.network.rx_bytes || displayMetrics.network.received_total || 0; + } else if (displayMetrics.network.bytes_sent !== undefined && displayMetrics.network.bytes_received !== undefined) { + // WebSocket消息格式 - 总流量 + sentRate = displayMetrics.network.bytes_sent; + receivedRate = displayMetrics.network.bytes_received; + + // 优先使用tx_bytes和rx_bytes作为总量 + sentTotal = displayMetrics.network.tx_bytes || 0; + receivedTotal = displayMetrics.network.rx_bytes || 0; + } else { + // 按网卡分组的数据,计算总流量 + for (const iface in displayMetrics.network) { + const ifaceMetrics = displayMetrics.network[iface]; + if (typeof ifaceMetrics === 'object') { + // 计算速率 + if (ifaceMetrics.bytes_sent !== undefined) { + sentRate += ifaceMetrics.bytes_sent; + } else if (ifaceMetrics.sent !== undefined) { + sentRate += ifaceMetrics.sent; + } + + if (ifaceMetrics.bytes_received !== undefined) { + receivedRate += ifaceMetrics.bytes_received; + } else if (ifaceMetrics.received !== undefined) { + receivedRate += ifaceMetrics.received; + } + + // 计算总量(所有网卡的tx_bytes和rx_bytes之和) + if (ifaceMetrics.tx_bytes !== undefined) { + sentTotal += ifaceMetrics.tx_bytes; + } else if (ifaceMetrics.bytes_sent_total !== undefined) { + sentTotal += ifaceMetrics.bytes_sent_total; + } + + if (ifaceMetrics.rx_bytes !== undefined) { + receivedTotal += ifaceMetrics.rx_bytes; + } else if (ifaceMetrics.bytes_received_total !== undefined) { + receivedTotal += ifaceMetrics.bytes_received_total; + } + } } } } - // 计算比率 - let ratioText = '0.0'; - if (sentRate > 0) { - ratioText = (receivedRate / sentRate).toFixed(1); + // 如果总量为0,尝试使用速率作为总量(临时解决方案) + if (sentTotal === 0) { + sentTotal = sentRate; + } + if (receivedTotal === 0) { + receivedTotal = receivedRate; + } + + // 计算比率并格式化显示 + let ratioDisplay = "0.0"; + let ratioDirection = " / "; + + if (sentRate === 0 && receivedRate === 0) { + // 没有流量 + ratioDisplay = "0.0"; + } else if (sentRate === 0) { + // 只有接收流量 + ratioDisplay = "∞"; + ratioDirection = ""; + } else if (receivedRate === 0) { + // 只有发送流量 + ratioDisplay = "0.0"; + ratioDirection = ""; + } else { + // 正常计算比率 + const ratio = receivedRate / sentRate; + ratioDisplay = ratio.toFixed(1); } // 更新显示 const networkValueElement = document.getElementById('networkValue'); const networkDetailsElement = document.getElementById('networkDetails'); if (networkValueElement) { - networkValueElement.innerHTML = `${ratioText} / `; + networkValueElement.className = 'text-3xl font-bold text-gray-900 metric-value'; + networkValueElement.innerHTML = `${ratioDisplay} ${ratioDirection}`; } if (networkDetailsElement) { + networkDetailsElement.className = 'text-xs text-gray-500 mt-1'; networkDetailsElement.innerHTML = `接收: ${formatBytes(receivedRate, 2, true)} | ` + `发送: ${formatBytes(sentRate, 2, true)}
` + @@ -1405,6 +1628,9 @@ function updateHistoryMetrics(metrics) { if (metrics.cpu) { state.historyMetrics.cpu = metrics.cpu; } + if (metrics.cpu_hz) { + state.historyMetrics.cpu_hz = metrics.cpu_hz; + } if (metrics.memory) { state.historyMetrics.memory = metrics.memory; } diff --git a/backend/test/test_influxdb.go b/backend/test/test_influxdb.go new file mode 100644 index 0000000..9205d58 --- /dev/null +++ b/backend/test/test_influxdb.go @@ -0,0 +1,72 @@ +package main + +import ( + "context" + "fmt" + "time" + + influxdb2 "github.com/influxdata/influxdb-client-go" +) + +func main() { + // 使用配置文件中的InfluxDB配置 + url := "http://10.35.10.130:8066" + token := "aVI5qMGz6e8d4pfyhVZNYfS5we7C8Bb-5bi-V7LpL3K6CmQyudauigoxDFv1UFo2Hvda7swKEqTe8eP6xy4jBw==" + username := "admin" + password := "Wxf26051" + org := "AMAZEHOME" + bucket := "AMAZEHOME" + + fmt.Printf("Testing InfluxDB connection to %s\n", url) + fmt.Printf("Org: %s, Bucket: %s\n", org, bucket) + fmt.Printf("Username: %s, Password: %s\n", username, password) + fmt.Printf("Token: %s\n", token) + + // 测试1: 使用Token认证 + fmt.Println("\n=== Test 1: Using Token Authentication ===") + client1 := influxdb2.NewClient(url, token) + defer client1.Close() + + // 测试连接 + health, err := client1.Health(context.Background()) + if err != nil { + fmt.Printf("Health check failed: %v\n", err) + } else { + fmt.Printf("Health check result: %v\n", health) + } + + // 测试2: 使用Username/Password认证(通过URL嵌入) + fmt.Println("\n=== Test 2: Using Username/Password Authentication (Embedded in URL) ===") + authURL := fmt.Sprintf("http://%s:%s@10.35.10.130:8066", username, password) + client2 := influxdb2.NewClient(authURL, "") + defer client2.Close() + + // 测试连接 + health2, err2 := client2.Health(context.Background()) + if err2 != nil { + fmt.Printf("Health check failed: %v\n", err2) + } else { + fmt.Printf("Health check result: %v\n", health2) + } + + // 测试3: 尝试写入数据点 + fmt.Println("\n=== Test 3: Trying to Write Data Point ===") + // 使用client2进行测试 + writeAPI := client2.WriteAPIBlocking(org, bucket) + + point := influxdb2.NewPoint( + "test_metric", + map[string]string{"test_tag": "test_value"}, + map[string]interface{}{"value": 1.0}, + time.Now(), + ) + + err = writeAPI.WritePoint(context.Background(), point) + if err != nil { + fmt.Printf("Write failed: %v\n", err) + } else { + fmt.Println("Write successful!") + } + + fmt.Println("\n=== Test Completed ===") +}