mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-19 00:41:41 +00:00
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 <matthew.morley.ca@gmail.com> Co-authored-by: Gold856 <117957790+Gold856@users.noreply.github.com>
This commit is contained in:
@@ -39,48 +39,69 @@ const generalMetrics = computed<MetricItem[]>(() => {
|
||||
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<MetricItem[]>(() => {
|
||||
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()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 + "/"});
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -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<DeviceMetrics> 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<String, String>();
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 '%'";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]+'";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ','";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<DeviceMetrics, ProtobufDeviceMetrics> {
|
||||
@Override
|
||||
public Class<DeviceMetrics> 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());
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user