From 7f6edcd567443755a0cc2cb91a2352975a7b05ce Mon Sep 17 00:00:00 2001 From: Sam Freund Date: Sat, 12 Jul 2025 04:40:58 -0500 Subject: [PATCH] feat: add metrics publisher for NT (#1791) Publishes metrics to NT using a protobuf under `photonvision/coprocessors/metrics` using the device host name as the key. Refactors metrics to use numbers where possible, instead of strings. Removes GPU mem display from metrics card when it can't be determined. Updates UI metrics periodically. Resolves #1988 Closes #830 --------- Co-authored-by: Matt Co-authored-by: Gold856 <117957790+Gold856@users.noreply.github.com> --- .../src/components/settings/MetricsCard.vue | 63 ++++-- .../stores/settings/GeneralSettingsStore.ts | 24 +-- photon-client/src/types/SettingTypes.ts | 18 +- .../networktables/NetworkTablesManager.java | 4 +- .../common/hardware/HardwareManager.java | 7 +- .../hardware/metrics/DeviceMetrics.java | 35 +++ .../hardware/metrics/MetricsManager.java | 202 ++++++++++++++---- .../common/hardware/metrics/cmds/CmdBase.java | 13 +- .../hardware/metrics/cmds/FileCmds.java | 11 +- .../hardware/metrics/cmds/LinuxCmds.java | 12 +- .../common/hardware/metrics/cmds/PiCmds.java | 4 +- .../hardware/metrics/cmds/RK3588Cmds.java | 3 +- .../metrics/proto/DeviceMetricsProto.java | 71 ++++++ .../photonvision/hardware/HardwareTest.java | 19 +- photon-targeting/src/main/proto/photon.proto | 14 ++ 15 files changed, 383 insertions(+), 117 deletions(-) create mode 100644 photon-core/src/main/java/org/photonvision/common/hardware/metrics/DeviceMetrics.java create mode 100644 photon-core/src/main/java/org/photonvision/common/hardware/metrics/proto/DeviceMetricsProto.java diff --git a/photon-client/src/components/settings/MetricsCard.vue b/photon-client/src/components/settings/MetricsCard.vue index 9bc5956ad..585f8ce33 100644 --- a/photon-client/src/components/settings/MetricsCard.vue +++ b/photon-client/src/components/settings/MetricsCard.vue @@ -39,48 +39,69 @@ const generalMetrics = computed(() => { return stats; }); +// @ts-expect-error This uses Intl.DurationFormat which is newly implemented and not available in TS. +const durationFormatter = new Intl.DurationFormat("en", { style: "narrow" }); const platformMetrics = computed(() => { + const metrics = useSettingsStore().metrics; const stats = [ { header: "CPU Temp", - value: useSettingsStore().metrics.cpuTemp === undefined ? "Unknown" : `${useSettingsStore().metrics.cpuTemp}°C` + value: metrics.cpuTemp === undefined || metrics.cpuTemp == -1 ? "Unknown" : `${metrics.cpuTemp}°C` }, { header: "CPU Usage", - value: useSettingsStore().metrics.cpuUtil === undefined ? "Unknown" : `${useSettingsStore().metrics.cpuUtil}%` + value: metrics.cpuUtil === undefined ? "Unknown" : `${metrics.cpuUtil}%` }, { header: "CPU Memory Usage", value: - useSettingsStore().metrics.ramUtil === undefined || useSettingsStore().metrics.cpuMem === undefined - ? "Unknown" - : `${useSettingsStore().metrics.ramUtil || "Unknown"}MB of ${useSettingsStore().metrics.cpuMem}MB` + metrics.ramUtil && metrics.ramMem && metrics.ramUtil >= 0 && metrics.ramMem >= 0 + ? `${metrics.ramUtil}MB of ${metrics.ramMem}MB` + : "Unknown" }, { - header: "GPU Memory Usage", - value: - useSettingsStore().metrics.gpuMemUtil === undefined || useSettingsStore().metrics.gpuMem === undefined - ? "Unknown" - : `${useSettingsStore().metrics.gpuMemUtil}MB of ${useSettingsStore().metrics.gpuMem}MB` - }, - { - header: "CPU Throttling", - value: useSettingsStore().metrics.cpuThr || "Unknown" - }, - { - header: "CPU Uptime", - value: useSettingsStore().metrics.cpuUptime || "Unknown" + header: "Uptime", + value: (() => { + const seconds = metrics.uptime; + if (seconds === undefined) return "Unknown"; + + const days = Math.floor(seconds / 86400); + const hours = Math.floor((seconds % 86400) / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const secs = Math.floor(seconds % 60); + + return durationFormatter.format({ + days: days, + hours: hours, + minutes: minutes, + seconds: secs + }); + })() }, { header: "Disk Usage", - value: useSettingsStore().metrics.diskUtilPct || "Unknown" + value: metrics.diskUtilPct === undefined ? "Unknown" : `${metrics.diskUtilPct}%` } ]; - if (useSettingsStore().metrics.npuUsage) { + if (metrics.npuUsage && metrics.npuUsage.length > 0) { stats.push({ header: "NPU Usage", - value: useSettingsStore().metrics.npuUsage || "Unknown" + value: metrics.npuUsage?.map((usage, index) => `Core${index} ${usage}%`).join(", ") || "Unknown" + }); + } + + if (metrics.gpuMem && metrics.gpuMemUtil && metrics.gpuMem > 0 && metrics.gpuMemUtil > 0) { + stats.push({ + header: "GPU Memory Usage", + value: `${metrics.gpuMemUtil}MB of ${metrics.gpuMem}MB` + }); + } + + if (metrics.cpuThr) { + stats.push({ + header: "CPU Throttling", + value: metrics.cpuThr.toString() }); } diff --git a/photon-client/src/stores/settings/GeneralSettingsStore.ts b/photon-client/src/stores/settings/GeneralSettingsStore.ts index e5f345823..28b0f7ad0 100644 --- a/photon-client/src/stores/settings/GeneralSettingsStore.ts +++ b/photon-client/src/stores/settings/GeneralSettingsStore.ts @@ -56,15 +56,15 @@ export const useSettingsStore = defineStore("settings", { metrics: { cpuTemp: undefined, cpuUtil: undefined, - cpuMem: undefined, - gpuMem: undefined, - ramUtil: undefined, - gpuMemUtil: undefined, cpuThr: undefined, - cpuUptime: undefined, + ramMem: undefined, + ramUtil: undefined, + gpuMem: undefined, + gpuMemUtil: undefined, diskUtilPct: undefined, npuUsage: undefined, - ipAddress: undefined + ipAddress: undefined, + uptime: undefined }, currentFieldLayout: { field: { @@ -90,15 +90,15 @@ export const useSettingsStore = defineStore("settings", { this.metrics = { cpuTemp: data.cpuTemp || undefined, cpuUtil: data.cpuUtil || undefined, - cpuMem: data.cpuMem || undefined, - gpuMem: data.gpuMem || undefined, - ramUtil: data.ramUtil || undefined, - gpuMemUtil: data.gpuMemUtil || undefined, cpuThr: data.cpuThr || undefined, - cpuUptime: data.cpuUptime || undefined, + ramMem: data.ramMem || undefined, + ramUtil: data.ramUtil || undefined, + gpuMem: data.gpuMem || undefined, + gpuMemUtil: data.gpuMemUtil || undefined, diskUtilPct: data.diskUtilPct || undefined, npuUsage: data.npuUsage || undefined, - ipAddress: data.ipAddress || undefined + ipAddress: data.ipAddress || undefined, + uptime: data.uptime || undefined }; }, updateGeneralSettingsFromWebsocket(data: WebsocketSettingsUpdate) { diff --git a/photon-client/src/types/SettingTypes.ts b/photon-client/src/types/SettingTypes.ts index 6f9dfe352..98030bbdd 100644 --- a/photon-client/src/types/SettingTypes.ts +++ b/photon-client/src/types/SettingTypes.ts @@ -25,17 +25,17 @@ export interface ObjectDetectionModelProperties { } export interface MetricData { - cpuTemp?: string; - cpuUtil?: string; - cpuMem?: string; - gpuMem?: string; - ramUtil?: string; - gpuMemUtil?: string; + cpuTemp?: number; + cpuUtil?: number; cpuThr?: string; - cpuUptime?: string; - diskUtilPct?: string; - npuUsage?: string; + ramMem?: number; + ramUtil?: number; + gpuMem?: number; + gpuMemUtil?: number; + diskUtilPct?: number; + npuUsage?: number[]; ipAddress?: string; + uptime?: number; } export enum NetworkConnectionType { diff --git a/photon-core/src/main/java/org/photonvision/common/dataflow/networktables/NetworkTablesManager.java b/photon-core/src/main/java/org/photonvision/common/dataflow/networktables/NetworkTablesManager.java index 5b112ddf1..2a44f6738 100644 --- a/photon-core/src/main/java/org/photonvision/common/dataflow/networktables/NetworkTablesManager.java +++ b/photon-core/src/main/java/org/photonvision/common/dataflow/networktables/NetworkTablesManager.java @@ -55,10 +55,12 @@ public class NetworkTablesManager { private final NetworkTableInstance ntInstance = NetworkTableInstance.getDefault(); private final String kRootTableName = "/photonvision"; - private final String kCoprocTableName = "coprocessors"; + public final String kCoprocTableName = "coprocessors"; private final String kFieldLayoutName = "apriltag_field_layout"; public final NetworkTable kRootTable = ntInstance.getTable(kRootTableName); + // This is used to subscribe to all coprocessor tables, so we can detect conflicts + @SuppressWarnings("unused") private final MultiSubscriber sub = new MultiSubscriber(ntInstance, new String[] {kRootTableName + "/" + kCoprocTableName + "/"}); diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/HardwareManager.java b/photon-core/src/main/java/org/photonvision/common/hardware/HardwareManager.java index e1a217031..8e8035169 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/HardwareManager.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/HardwareManager.java @@ -76,6 +76,9 @@ public class HardwareManager { this.metricsManager = new MetricsManager(); this.metricsManager.setConfig(hardwareConfig); + TimedTaskManager.getInstance() + .addTask("Metrics Publisher", this.metricsManager::publishMetrics, 5000); + ledModeRequest = NetworkTablesManager.getInstance() .kRootTable @@ -222,10 +225,6 @@ public class HardwareManager { blinkCounter++; } - public HardwareConfig getConfig() { - return hardwareConfig; - } - public void publishMetrics() { metricsManager.publishMetrics(); } diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/DeviceMetrics.java b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/DeviceMetrics.java new file mode 100644 index 000000000..4c31a20fd --- /dev/null +++ b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/DeviceMetrics.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.photonvision.common.hardware.metrics; + +import org.photonvision.common.hardware.metrics.proto.DeviceMetricsProto; + +public record DeviceMetrics( + double cpuTemp, + double cpuUtil, + String cpuThr, + double ramMem, + double ramUtil, + double gpuMem, + double gpuMemUtil, + double diskUtilPct, + double[] npuUsage, + String ipAddress, + double uptime) { + public static final DeviceMetricsProto proto = new DeviceMetricsProto(); +} diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/MetricsManager.java b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/MetricsManager.java index 053f14a70..9a41f4c44 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/MetricsManager.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/MetricsManager.java @@ -17,13 +17,15 @@ package org.photonvision.common.hardware.metrics; +import edu.wpi.first.cscore.CameraServerJNI; +import edu.wpi.first.networktables.ProtobufPublisher; import java.io.PrintWriter; import java.io.StringWriter; -import java.util.HashMap; import org.photonvision.common.configuration.ConfigManager; import org.photonvision.common.configuration.HardwareConfig; import org.photonvision.common.dataflow.DataChangeService; import org.photonvision.common.dataflow.events.OutgoingUIEvent; +import org.photonvision.common.dataflow.networktables.NetworkTablesManager; import org.photonvision.common.hardware.Platform; import org.photonvision.common.hardware.metrics.cmds.CmdBase; import org.photonvision.common.hardware.metrics.cmds.FileCmds; @@ -41,6 +43,13 @@ public class MetricsManager { CmdBase cmds; + ProtobufPublisher metricPublisher = + NetworkTablesManager.getInstance() + .kRootTable + .getSubTable("/" + NetworkTablesManager.getInstance().kCoprocTableName + "/metrics") + .getProtobufTopic(CameraServerJNI.getHostname(), DeviceMetrics.proto) + .publish(); + private final ShellExec runCommand = new ShellExec(true, true); public void setConfig(HardwareConfig config) { @@ -70,60 +79,148 @@ public class MetricsManager { } } - private String cpuMemSave = null; - - public String getMemory() { - if (cmds.cpuMemoryCommand.isEmpty()) return ""; - if (cpuMemSave == null) { - // save the value and only run it once - cpuMemSave = execute(cmds.cpuMemoryCommand); + /** + * Get the CPU temperature in Celsius. + * + * @return The CPU temperature in Celsius, or -1.0 if the command fails or parsing fails. + */ + public double getCpuTemp() { + try { + return Double.parseDouble(safeExecute(cmds.cpuTemperatureCommand)); + } catch (NumberFormatException e) { + return -1.0; } - return cpuMemSave; } - public String getTemp() { - return safeExecute(cmds.cpuTemperatureCommand); - } - - public String getUtilization() { - return safeExecute(cmds.cpuUtilizationCommand); - } - - public String getUptime() { - return safeExecute(cmds.cpuUptimeCommand); + /** + * Get the CPU utilization as a percentage. + * + * @return The CPU utilization as a percentage, or -1.0 if the command fails or parsing fails. + */ + public double getCpuUtilization() { + try { + return Double.parseDouble(safeExecute(cmds.cpuUtilizationCommand)); + } catch (NumberFormatException e) { + return -1.0; + } } + /** + * Get the reason for CPU throttling, if applicable. + * + * @return A string describing the CPU throttle reason, or an empty string if the command fails. + */ public String getThrottleReason() { return safeExecute(cmds.cpuThrottleReasonCmd); } - public String getNpuUsage() { - return safeExecute(cmds.npuUsageCommand); + private double ramMemSave = -2.0; + + /** + * Get the total RAM memory in MB. This only runs once, as it won't change over time. + * + * @return The total RAM memory in MB, or -1.0 if the command fails or parsing fails. + */ + public double getRamMem() { + if (ramMemSave == -2.0) { + try { + ramMemSave = Double.parseDouble(safeExecute(cmds.ramMemCommand)); + } catch (NumberFormatException e) { + ramMemSave = -1.0; + } + } + return ramMemSave; } - private String gpuMemSave = null; + /** + * Get the RAM utilization in MBs. + * + * @return The RAM utilization in MBs, or -1.0 if the command fails or parsing fails. + */ + public double getRamUtil() { + try { + return Double.parseDouble(safeExecute(cmds.ramUtilCommand)); + } catch (NumberFormatException e) { + return -1.0; + } + } - public String getGPUMemorySplit() { - if (gpuMemSave == null) { - // only needs to run once - gpuMemSave = safeExecute(cmds.gpuMemoryCommand); + private double gpuMemSave = -2.0; + + /** + * Get the total GPU memory in MB. This only runs once, as it won't change over time. + * + * @return The total GPU memory in MB, or -1.0 if the command fails or parsing fails. + */ + public double getGpuMem() { + if (gpuMemSave == -2.0) { + try { + gpuMemSave = Double.parseDouble(safeExecute(cmds.gpuMemCommand)); + } catch (NumberFormatException e) { + gpuMemSave = -1.0; + } } return gpuMemSave; } - public String getMallocedMemory() { - return safeExecute(cmds.gpuMemUsageCommand); + /** + * Get the GPU memory utilization as MBs. + * + * @return The GPU memory utilization in MBs, or -1.0 if the command fails or parsing fails. + */ + public double getGpuMemUtil() { + try { + return Double.parseDouble(safeExecute(cmds.gpuMemUtilCommand)); + } catch (NumberFormatException e) { + return -1.0; + } } - public String getUsedDiskPct() { - return safeExecute(cmds.diskUsageCommand); + /** + * Get the percentage of disk space used. + * + * @return The percentage of disk space used, or -1.0 if the command fails or parsing fails. + */ + public double getUsedDiskPct() { + try { + return Double.parseDouble(safeExecute(cmds.diskUsageCommand)); + } catch (NumberFormatException e) { + return -1.0; + } } - // TODO: Output in MBs for consistency - public String getUsedRam() { - return safeExecute(cmds.ramUsageCommand); + // This is here so we don't spam logs if it fails + boolean npuParseWarning = false; + + /** + * Get the NPU usage as an array of doubles. + * + * @return An array of doubles representing NPU usage, or null if parsing fails. + */ + public double[] getNpuUsage() { + String[] usages = safeExecute(cmds.npuUsageCommand).split(","); + double[] usageDoubles = new double[usages.length]; + for (int i = 0; i < usages.length; i++) { + try { + usageDoubles[i] = Double.parseDouble(usages[i]); + npuParseWarning = false; // Reset warning if parsing succeeds + } catch (NumberFormatException e) { + if (!npuParseWarning) { + logger.error("Failed to parse NPU usage value: " + usages[i], e); + npuParseWarning = true; + } + usageDoubles = new double[0]; // Default to empty array if parsing fails + break; + } + } + return usageDoubles; } + /** + * Get the IP address of the device. + * + * @return The IP address as a string, or an empty string if the command fails. + */ public String getIpAddress() { String dev = ConfigManager.getInstance().getConfig().getNetworkConfig().networkManagerIface; logger.debug("Requesting IP addresses for \"" + dev + "\""); @@ -132,21 +229,36 @@ public class MetricsManager { return addr; } + /** + * Get the uptime of the device in seconds. + * + * @return The uptime in seconds, or -1.0 if the command fails or parsing fails. + */ + public double getUptime() { + try { + return Double.parseDouble(safeExecute(cmds.uptimeCommand)); + } catch (NumberFormatException e) { + return -1.0; + } + } + public void publishMetrics() { logger.debug("Publishing Metrics..."); - final var metrics = new HashMap(); + var metrics = + new DeviceMetrics( + this.getCpuTemp(), + this.getCpuUtilization(), + this.getThrottleReason(), + this.getRamMem(), + this.getRamUtil(), + this.getGpuMem(), + this.getGpuMemUtil(), + this.getUsedDiskPct(), + this.getNpuUsage(), + this.getIpAddress(), + this.getUptime()); - metrics.put("cpuTemp", this.getTemp()); - metrics.put("cpuUtil", this.getUtilization()); - metrics.put("cpuMem", this.getMemory()); - metrics.put("cpuThr", this.getThrottleReason()); - metrics.put("cpuUptime", this.getUptime()); - metrics.put("gpuMem", this.getGPUMemorySplit()); - metrics.put("ramUtil", this.getUsedRam()); - metrics.put("gpuMemUtil", this.getMallocedMemory()); - metrics.put("diskUtilPct", this.getUsedDiskPct()); - metrics.put("npuUsage", this.getNpuUsage()); - metrics.put("ipAddress", this.getIpAddress()); + metricPublisher.set(metrics); DataChangeService.getInstance().publishEvent(OutgoingUIEvent.wrappedOf("metrics", metrics)); } diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/CmdBase.java b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/CmdBase.java index 1580b5d66..78ec89edc 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/CmdBase.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/CmdBase.java @@ -21,20 +21,21 @@ import org.photonvision.common.configuration.HardwareConfig; public class CmdBase { // CPU - public String cpuMemoryCommand = ""; public String cpuTemperatureCommand = ""; public String cpuUtilizationCommand = ""; public String cpuThrottleReasonCmd = ""; - public String cpuUptimeCommand = ""; + // RAM + public String ramMemCommand = ""; + public String ramUtilCommand = ""; // GPU - public String gpuMemoryCommand = ""; - public String gpuMemUsageCommand = ""; + public String gpuMemCommand = ""; + public String gpuMemUtilCommand = ""; // NPU public String npuUsageCommand = ""; - // RAM - public String ramUsageCommand = ""; // Disk public String diskUsageCommand = ""; + // Uptime + public String uptimeCommand = ""; public void initCmds(HardwareConfig config) { // default - do nothing diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/FileCmds.java b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/FileCmds.java index bda20e0af..6c988feeb 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/FileCmds.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/FileCmds.java @@ -22,17 +22,18 @@ import org.photonvision.common.configuration.HardwareConfig; public class FileCmds extends CmdBase { @Override public void initCmds(HardwareConfig config) { - cpuMemoryCommand = config.cpuMemoryCommand(); cpuTemperatureCommand = config.cpuTempCommand(); cpuUtilizationCommand = config.cpuUtilCommand(); cpuThrottleReasonCmd = config.cpuThrottleReasonCmd(); - cpuUptimeCommand = config.cpuUptimeCommand(); - gpuMemoryCommand = config.gpuMemoryCommand(); - gpuMemUsageCommand = config.gpuMemUsageCommand(); + ramMemCommand = config.cpuMemoryCommand(); + ramUtilCommand = config.ramUtilCommand(); + + gpuMemCommand = config.gpuMemoryCommand(); + gpuMemUtilCommand = config.gpuMemUsageCommand(); diskUsageCommand = config.diskUsageCommand(); - ramUsageCommand = config.ramUtilCommand(); + uptimeCommand = config.cpuUptimeCommand(); } } diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/LinuxCmds.java b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/LinuxCmds.java index 56ad1b736..6db4dbce9 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/LinuxCmds.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/LinuxCmds.java @@ -21,20 +21,20 @@ import org.photonvision.common.configuration.HardwareConfig; public class LinuxCmds extends CmdBase { public void initCmds(HardwareConfig config) { - // CPU - cpuMemoryCommand = "free -m | awk 'FNR == 2 {print $2}'"; - // TODO: boards have lots of thermal devices. Hard to pick the CPU + // CPU cpuUtilizationCommand = "top -bn1 | grep \"Cpu(s)\" | sed \"s/.*, *\\([0-9.]*\\)%* id.*/\\1/\" | awk '{print 100 - $1}'"; - cpuUptimeCommand = "uptime -p | cut -c 4-"; + // Uptime + uptimeCommand = "cat /proc/uptime | cut -d ' ' -f1"; // RAM - ramUsageCommand = "free -m | awk 'FNR == 2 {print $3}'"; + ramMemCommand = "free -m | awk 'FNR == 2 {print $2}'"; + ramUtilCommand = "free -m | awk 'FNR == 2 {print $3}'"; // Disk - diskUsageCommand = "df ./ --output=pcent | tail -n +2"; + diskUsageCommand = "df ./ --output=pcent | tail -n +2 | tr -d '%'"; } } diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/PiCmds.java b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/PiCmds.java index 88ed23f22..57680726e 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/PiCmds.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/PiCmds.java @@ -34,7 +34,7 @@ public class PiCmds extends LinuxCmds { + " else echo \"None\"; fi"; // GPU - gpuMemoryCommand = "vcgencmd get_mem gpu | grep -Eo '[0-9]+'"; - gpuMemUsageCommand = "vcgencmd get_mem malloc | grep -Eo '[0-9]+'"; + gpuMemCommand = "vcgencmd get_mem gpu | grep -Eo '[0-9]+'"; + gpuMemUtilCommand = "vcgencmd get_mem malloc | grep -Eo '[0-9]+'"; } } diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/RK3588Cmds.java b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/RK3588Cmds.java index a215570bb..b8be38ac6 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/RK3588Cmds.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/RK3588Cmds.java @@ -45,6 +45,7 @@ public class RK3588Cmds extends LinuxCmds { cpuTemperatureCommand = "cat /sys/class/thermal/thermal_zone1/temp | awk '{printf \"%.1f\", $1/1000}'"; - npuUsageCommand = "cat /sys/kernel/debug/rknpu/load | sed 's/NPU load://; s/^ *//; s/ *$//'"; + npuUsageCommand = + "cat /sys/kernel/debug/rknpu/load | grep -o '[0-9]\\+%' | sed 's/%//g' | paste -sd ','"; } } diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/proto/DeviceMetricsProto.java b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/proto/DeviceMetricsProto.java new file mode 100644 index 000000000..4033e2dd4 --- /dev/null +++ b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/proto/DeviceMetricsProto.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.photonvision.common.hardware.metrics.proto; + +import edu.wpi.first.util.protobuf.Protobuf; +import org.photonvision.common.hardware.metrics.DeviceMetrics; +import org.photonvision.proto.Photon.ProtobufDeviceMetrics; +import us.hebi.quickbuf.Descriptors.Descriptor; + +public class DeviceMetricsProto implements Protobuf { + @Override + public Class getTypeClass() { + return DeviceMetrics.class; + } + + @Override + public Descriptor getDescriptor() { + return ProtobufDeviceMetrics.getDescriptor(); + } + + @Override + public ProtobufDeviceMetrics createMessage() { + return ProtobufDeviceMetrics.newInstance(); + } + + @Override + public DeviceMetrics unpack(ProtobufDeviceMetrics msg) { + return new DeviceMetrics( + msg.getCpuTemp(), + msg.getCpuUtil(), + msg.getCpuThr(), + msg.getRamMem(), + msg.getRamUtil(), + msg.getGpuMem(), + msg.getGpuMemUtil(), + msg.getDiskUtilPct(), + msg.getNpuUsage().toArray(), + msg.getIpAddress(), + msg.getUptime()); + } + + @Override + public void pack(ProtobufDeviceMetrics msg, DeviceMetrics value) { + msg.setCpuTemp(value.cpuTemp()); + msg.setCpuUtil(value.cpuUtil()); + msg.setRamMem(value.ramMem()); + msg.setCpuThr(value.cpuThr()); + msg.setUptime(value.uptime()); + msg.setGpuMem(value.gpuMem()); + msg.setRamUtil(value.ramUtil()); + msg.setGpuMemUtil(value.gpuMemUtil()); + msg.setDiskUtilPct(value.diskUtilPct()); + msg.addAllNpuUsage(value.npuUsage()); + msg.setIpAddress(value.ipAddress()); + } +} diff --git a/photon-core/src/test/java/org/photonvision/hardware/HardwareTest.java b/photon-core/src/test/java/org/photonvision/hardware/HardwareTest.java index 9d371d25f..f5fa86cbe 100644 --- a/photon-core/src/test/java/org/photonvision/hardware/HardwareTest.java +++ b/photon-core/src/test/java/org/photonvision/hardware/HardwareTest.java @@ -20,16 +20,25 @@ package org.photonvision.hardware; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.IOException; import org.junit.jupiter.api.Test; import org.photonvision.common.hardware.GPIO.CustomGPIO; import org.photonvision.common.hardware.GPIO.GPIOBase; import org.photonvision.common.hardware.GPIO.pi.PigpioPin; import org.photonvision.common.hardware.Platform; import org.photonvision.common.hardware.metrics.MetricsManager; +import org.photonvision.common.util.TestUtils; +import org.photonvision.jni.PhotonTargetingJniLoader; public class HardwareTest { @Test public void testHardware() { + try { + TestUtils.loadLibraries(); + PhotonTargetingJniLoader.load(); + } catch (UnsatisfiedLinkError | IOException e) { + e.printStackTrace(); + } MetricsManager mm = new MetricsManager(); if (!Platform.isRaspberryPi()) return; @@ -37,15 +46,15 @@ public class HardwareTest { System.out.println("Testing on platform: " + Platform.getPlatformName()); System.out.println("Printing CPU Info:"); - System.out.println("Memory: " + mm.getMemory() + "MB"); - System.out.println("Temperature: " + mm.getTemp() + "C"); - System.out.println("Utilization: : " + mm.getUtilization() + "%"); + System.out.println("Memory: " + mm.getRamMem() + "MB"); + System.out.println("Temperature: " + mm.getCpuTemp() + "C"); + System.out.println("Utilization: : " + mm.getCpuUtilization() + "%"); System.out.println("Printing GPU Info:"); - System.out.println("Memory: " + mm.getGPUMemorySplit() + "MB"); + System.out.println("Memory: " + mm.getGpuMem() + "MB"); System.out.println("Printing RAM Info: "); - System.out.println("Used RAM: : " + mm.getUsedRam() + "MB"); + System.out.println("Used RAM: : " + mm.getRamUtil() + "MB"); } @Test diff --git a/photon-targeting/src/main/proto/photon.proto b/photon-targeting/src/main/proto/photon.proto index 6650785bd..0ff3991f9 100644 --- a/photon-targeting/src/main/proto/photon.proto +++ b/photon-targeting/src/main/proto/photon.proto @@ -68,3 +68,17 @@ message ProtobufPhotonPipelineResult { int64 nt_publish_timestamp_micros = 6; int64 time_since_last_pong_micros = 7; } + +message ProtobufDeviceMetrics { + double cpu_temp = 1; + double cpu_util = 2; + string cpu_thr = 3; + double ram_mem = 4; + double ram_util = 5; + double gpu_mem = 6; + double gpu_mem_util = 7; + double disk_util_pct = 8; + repeated double npu_usage = 9; + string ip_address = 10; + double uptime = 11; +}