From 54e666bf28a7b4515b9c0413a9b5420446133c35 Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Wed, 19 Feb 2025 12:21:18 -0600 Subject: [PATCH 01/20] feat: add metrics publisher for NT --- .../networktables/NetworkTablesManager.java | 8 ++++++++ .../common/hardware/HardwareManager.java | 4 ++++ .../hardware/metrics/MetricsManager.java | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+) 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 5765972f91..f3b52f4514 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 @@ -201,6 +201,12 @@ private void broadcastVersion() { kRootTable.getEntry("buildDate").setString(PhotonVersion.buildDate); } + private void broadcastMetrics() { + HardwareManager.getInstance() + .getMetrics() + .forEach((k, v) -> kRootTable.getSubTable("metrics").getEntry(k).setString(v)); + } + public void setConfig(NetworkConfig config) { if (config.runNTServer) { setServerMode(); @@ -256,6 +262,8 @@ private void ntTick() { logger.error( "[NetworkTablesManager] Could not connect to the robot! Will retry in the background..."); } + + broadcastMetrics(); } public long getTimeSinceLastPong() { 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 2c9a95615b..8ad180cf3a 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 @@ -229,4 +229,8 @@ public HardwareConfig getConfig() { public void publishMetrics() { metricsManager.publishMetrics(); } + + public HashMap getMetrics() { + return metricsManager.getMetrics(); + } } 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 cfddf8f829..f2e2d28238 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 @@ -129,6 +129,24 @@ public String getIpAddress() { return addr; } + public HashMap getMetrics() { + HashMap metrics = new HashMap(); + + 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()); + + return metrics; + } + public void publishMetrics() { logger.debug("Publishing Metrics..."); final var metrics = new HashMap(); From 1cb80b8bfed0f3bb1b9e4cc4fa337711e11b10b1 Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Wed, 19 Feb 2025 13:05:40 -0600 Subject: [PATCH 02/20] abstract hashmap --- .../hardware/metrics/MetricsManager.java | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) 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 f2e2d28238..f57db2e4e7 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 @@ -36,6 +36,7 @@ import org.photonvision.common.util.ShellExec; public class MetricsManager { + final Logger logger = new Logger(MetricsManager.class, LogGroup.General); CmdBase cmds; @@ -129,9 +130,9 @@ public String getIpAddress() { return addr; } - public HashMap getMetrics() { + public HashMap getMetricsObject() { HashMap metrics = new HashMap(); - + metrics.put("cpuTemp", this.getTemp()); metrics.put("cpuUtil", this.getUtilization()); metrics.put("cpuMem", this.getMemory()); @@ -147,21 +148,11 @@ public HashMap getMetrics() { return metrics; } + + public void publishMetrics() { logger.debug("Publishing Metrics..."); - final var metrics = new HashMap(); - - 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()); + HashMap metrics = getMetricsObject(); DataChangeService.getInstance().publishEvent(OutgoingUIEvent.wrappedOf("metrics", metrics)); } From a6513bbb6172e1c8b4b676a118228eb9f474e857 Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Wed, 19 Feb 2025 14:48:57 -0600 Subject: [PATCH 03/20] comments stuff --- .../common/dataflow/networktables/NetworkTablesManager.java | 6 +++--- .../common/hardware/metrics/MetricsManager.java | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) 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 f3b52f4514..07061525cd 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 @@ -248,9 +248,9 @@ private void setServerMode() { // So it seems like if Photon starts before the robot NT server does, and both aren't static IP, // it'll never connect. This hack works around it by restarting the client/server while the nt - // instance - // isn't connected, same as clicking the save button in the settings menu (or restarting the - // service) + // instance isn't connected, same as clicking the save button in the settings menu + // (or restarting the service) + // It's also used to update the metrics every tick private void ntTick() { if (!ntInstance.isConnected() && !ConfigManager.getInstance().getConfig().getNetworkConfig().runNTServer) { 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 f57db2e4e7..c671ccd3ba 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 @@ -132,7 +132,7 @@ public String getIpAddress() { public HashMap getMetricsObject() { HashMap metrics = new HashMap(); - + metrics.put("cpuTemp", this.getTemp()); metrics.put("cpuUtil", this.getUtilization()); metrics.put("cpuMem", this.getMemory()); @@ -148,8 +148,6 @@ public HashMap getMetricsObject() { return metrics; } - - public void publishMetrics() { logger.debug("Publishing Metrics..."); HashMap metrics = getMetricsObject(); From 6e0adcd92b8c9fa0f58fe0cb3245e3a64962101f Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Wed, 19 Feb 2025 14:56:50 -0600 Subject: [PATCH 04/20] fix the broke --- .../java/org/photonvision/common/hardware/HardwareManager.java | 2 +- .../photonvision/common/hardware/metrics/MetricsManager.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) 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 8ad180cf3a..0c0b36bbcc 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 @@ -231,6 +231,6 @@ public void publishMetrics() { } public HashMap getMetrics() { - return metricsManager.getMetrics(); + return metricsManager.getMetricsObject(); } } 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 c671ccd3ba..f96236ec49 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 @@ -36,7 +36,6 @@ import org.photonvision.common.util.ShellExec; public class MetricsManager { - final Logger logger = new Logger(MetricsManager.class, LogGroup.General); CmdBase cmds; From 58331b82df58190eba1990a2abd9f650e6bc54ee Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Wed, 19 Feb 2025 14:59:26 -0600 Subject: [PATCH 05/20] rename --- .../org/photonvision/common/hardware/HardwareManager.java | 2 +- .../photonvision/common/hardware/metrics/MetricsManager.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 0c0b36bbcc..8ad180cf3a 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 @@ -231,6 +231,6 @@ public void publishMetrics() { } public HashMap getMetrics() { - return metricsManager.getMetricsObject(); + return metricsManager.getMetrics(); } } 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 f96236ec49..fcd0a65e83 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 @@ -129,7 +129,7 @@ public String getIpAddress() { return addr; } - public HashMap getMetricsObject() { + public HashMap getMetrics() { HashMap metrics = new HashMap(); metrics.put("cpuTemp", this.getTemp()); @@ -149,7 +149,7 @@ public HashMap getMetricsObject() { public void publishMetrics() { logger.debug("Publishing Metrics..."); - HashMap metrics = getMetricsObject(); + HashMap metrics = getMetrics(); DataChangeService.getInstance().publishEvent(OutgoingUIEvent.wrappedOf("metrics", metrics)); } From bc3d905ee4c409243553ed29f884bc7240b67e7f Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 13 Apr 2025 10:35:47 -0700 Subject: [PATCH 06/20] Use record --- .../hardware/metrics/MetricsManager.java | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) 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 fcd0a65e83..aba5bee8ed 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 @@ -19,7 +19,6 @@ 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; @@ -129,27 +128,37 @@ public String getIpAddress() { return addr; } - public HashMap getMetrics() { - HashMap metrics = new HashMap(); - - 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()); - - return metrics; + public record SystemMetrics( + String cpuTemp, + String cpuUtil, + String cpuMem, + String cpuThr, + String cpuUptime, + String gpuMem, + String ramUtil, + String gpuMemUtil, + String diskUtilPct, + String npuUsage, + String ipAddress) {} + + public SystemMetrics getMetrics() { + return new SystemMetrics( + this.getTemp(), + this.getUtilization(), + this.getMemory(), + this.getThrottleReason(), + this.getUptime(), + this.getGPUMemorySplit(), + this.getUsedRam(), + this.getMallocedMemory(), + this.getUsedDiskPct(), + this.getNpuUsage(), + this.getIpAddress()); } public void publishMetrics() { logger.debug("Publishing Metrics..."); - HashMap metrics = getMetrics(); + var metrics = getMetrics(); DataChangeService.getInstance().publishEvent(OutgoingUIEvent.wrappedOf("metrics", metrics)); } From 8c1e60af91fb5ade625eb2aa363bef7823e04ca4 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 13 Apr 2025 10:37:34 -0700 Subject: [PATCH 07/20] Rename --- .../org/photonvision/common/hardware/HardwareManager.java | 3 ++- .../common/hardware/metrics/MetricsManager.java | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) 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 8ad180cf3a..81782eca83 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 @@ -30,6 +30,7 @@ import org.photonvision.common.hardware.GPIO.CustomGPIO; import org.photonvision.common.hardware.GPIO.pi.PigpioSocket; import org.photonvision.common.hardware.metrics.MetricsManager; +import org.photonvision.common.hardware.metrics.MetricsManager.DeviceMetrics; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; import org.photonvision.common.util.ShellExec; @@ -230,7 +231,7 @@ public void publishMetrics() { metricsManager.publishMetrics(); } - public HashMap getMetrics() { + public DeviceMetrics getMetrics() { return metricsManager.getMetrics(); } } 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 aba5bee8ed..397cd179db 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 @@ -128,7 +128,7 @@ public String getIpAddress() { return addr; } - public record SystemMetrics( + public static record DeviceMetrics( String cpuTemp, String cpuUtil, String cpuMem, @@ -141,8 +141,8 @@ public record SystemMetrics( String npuUsage, String ipAddress) {} - public SystemMetrics getMetrics() { - return new SystemMetrics( + public DeviceMetrics getMetrics() { + return new DeviceMetrics( this.getTemp(), this.getUtilization(), this.getMemory(), From ee9d7ed527b925da04e5f82b504e5e94e4f5442b Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 13 Apr 2025 10:50:27 -0700 Subject: [PATCH 08/20] Publish proto --- .../networktables/NetworkTablesManager.java | 13 +++- .../common/hardware/HardwareManager.java | 2 +- .../hardware/metrics/DeviceMetrics.java | 35 +++++++++ .../hardware/metrics/MetricsManager.java | 13 ---- .../metrics/proto/DeviceMetricsProto.java | 71 +++++++++++++++++++ photon-targeting/src/main/proto/photon.proto | 14 ++++ .../src/test/java/jni/FileLoggerTest.java | 2 +- 7 files changed, 132 insertions(+), 18 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-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 07061525cd..9af69b5a1f 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 @@ -18,11 +18,13 @@ package org.photonvision.common.dataflow.networktables; import edu.wpi.first.apriltag.AprilTagFieldLayout; +import edu.wpi.first.cscore.CameraServerJNI; import edu.wpi.first.networktables.LogMessage; import edu.wpi.first.networktables.NetworkTable; import edu.wpi.first.networktables.NetworkTableEvent; import edu.wpi.first.networktables.NetworkTableEvent.Kind; import edu.wpi.first.networktables.NetworkTableInstance; +import edu.wpi.first.networktables.ProtobufPublisher; import edu.wpi.first.networktables.StringSubscriber; import java.io.IOException; import java.util.EnumSet; @@ -34,6 +36,7 @@ import org.photonvision.common.dataflow.events.OutgoingUIEvent; import org.photonvision.common.dataflow.websocket.UIPhotonConfiguration; import org.photonvision.common.hardware.HardwareManager; +import org.photonvision.common.hardware.metrics.DeviceMetrics; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.LogLevel; import org.photonvision.common.logging.Logger; @@ -56,6 +59,12 @@ public class NetworkTablesManager { private StringSubscriber m_fieldLayoutSubscriber = kRootTable.getStringTopic(kFieldLayoutName).subscribe(""); + ProtobufPublisher metricPublisher = + kRootTable + .getSubTable(".metrics") + .getProtobufTopic(CameraServerJNI.getHostname(), DeviceMetrics.proto) + .publish(); + private final TimeSyncManager m_timeSync = new TimeSyncManager(kRootTable); private NetworkTablesManager() { @@ -202,9 +211,7 @@ private void broadcastVersion() { } private void broadcastMetrics() { - HardwareManager.getInstance() - .getMetrics() - .forEach((k, v) -> kRootTable.getSubTable("metrics").getEntry(k).setString(v)); + metricPublisher.set(HardwareManager.getInstance().getMetrics()); } public void setConfig(NetworkConfig config) { 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 81782eca83..0ec1ee0036 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 @@ -29,8 +29,8 @@ import org.photonvision.common.dataflow.networktables.NetworkTablesManager; import org.photonvision.common.hardware.GPIO.CustomGPIO; import org.photonvision.common.hardware.GPIO.pi.PigpioSocket; +import org.photonvision.common.hardware.metrics.DeviceMetrics; import org.photonvision.common.hardware.metrics.MetricsManager; -import org.photonvision.common.hardware.metrics.MetricsManager.DeviceMetrics; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; import org.photonvision.common.util.ShellExec; 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 0000000000..0af13440a8 --- /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( + String cpuTemp, + String cpuUtil, + String cpuMem, + String cpuThr, + String cpuUptime, + String gpuMem, + String ramUtil, + String gpuMemUtil, + String diskUtilPct, + String npuUsage, + String ipAddress) { + 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 397cd179db..1a4ee7975f 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 @@ -128,19 +128,6 @@ public String getIpAddress() { return addr; } - public static record DeviceMetrics( - String cpuTemp, - String cpuUtil, - String cpuMem, - String cpuThr, - String cpuUptime, - String gpuMem, - String ramUtil, - String gpuMemUtil, - String diskUtilPct, - String npuUsage, - String ipAddress) {} - public DeviceMetrics getMetrics() { return new DeviceMetrics( this.getTemp(), 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 0000000000..a416205915 --- /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.getCpuMem(), + msg.getCpuThr(), + msg.getCpuUptime(), + msg.getGpuMem(), + msg.getRamUtil(), + msg.getGpuMemUtil(), + msg.getDiskUtilPct(), + msg.getNpuUsage(), + msg.getIpAddress()); + } + + @Override + public void pack(ProtobufDeviceMetrics msg, DeviceMetrics value) { + msg.setCpuTemp(value.cpuTemp()); + msg.setCpuUtil(value.cpuUtil()); + msg.setCpuMem(value.cpuMem()); + msg.setCpuThr(value.cpuThr()); + msg.setCpuUptime(value.cpuUptime()); + msg.setGpuMem(value.gpuMem()); + msg.setRamUtil(value.ramUtil()); + msg.setGpuMemUtil(value.gpuMemUtil()); + msg.setDiskUtilPct(value.diskUtilPct()); + msg.setNpuUsage(value.npuUsage()); + msg.setIpAddress(value.ipAddress()); + } +} diff --git a/photon-targeting/src/main/proto/photon.proto b/photon-targeting/src/main/proto/photon.proto index 6650785bd6..27072fc426 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 { + string cpu_temp = 1; + string cpu_util = 2; + string cpu_mem = 3; + string cpu_thr = 4; + string cpu_uptime = 5; + string gpu_mem = 6; + string ram_util = 7; + string gpu_mem_util = 8; + string disk_util_pct = 9; + string npu_usage = 10; + string ip_address = 11; +} diff --git a/photon-targeting/src/test/java/jni/FileLoggerTest.java b/photon-targeting/src/test/java/jni/FileLoggerTest.java index 157ee9f1e2..f974825b38 100644 --- a/photon-targeting/src/test/java/jni/FileLoggerTest.java +++ b/photon-targeting/src/test/java/jni/FileLoggerTest.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package wpiutil_extras; +package jni; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; From d6381b9db45c566621e5020e9b607f5163b45658 Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Fri, 4 Jul 2025 19:27:21 -0500 Subject: [PATCH 09/20] update metrics publish location --- .../common/dataflow/networktables/NetworkTablesManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2e442dc6e2..ace01c39f5 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 @@ -78,7 +78,7 @@ public class NetworkTablesManager { ProtobufPublisher metricPublisher = kRootTable - .getSubTable(".metrics") + .getSubTable("/" + kCoprocTableName + "/metrics") .getProtobufTopic(CameraServerJNI.getHostname(), DeviceMetrics.proto) .publish(); From 0850049914a268541d60868a52380f90052ac643 Mon Sep 17 00:00:00 2001 From: samfreund Date: Wed, 9 Jul 2025 16:50:56 -0500 Subject: [PATCH 10/20] update metrics to publish numbers --- .../src/components/settings/MetricsCard.vue | 8 +- photon-client/src/types/SettingTypes.ts | 18 +-- .../hardware/metrics/DeviceMetrics.java | 18 +-- .../hardware/metrics/MetricsManager.java | 109 +++++++++++++----- .../hardware/metrics/cmds/LinuxCmds.java | 4 +- .../hardware/metrics/cmds/RK3588Cmds.java | 3 +- .../metrics/proto/DeviceMetricsProto.java | 4 +- photon-targeting/src/main/proto/photon.proto | 18 +-- 8 files changed, 120 insertions(+), 62 deletions(-) diff --git a/photon-client/src/components/settings/MetricsCard.vue b/photon-client/src/components/settings/MetricsCard.vue index 1fbd876418..d31bedc119 100644 --- a/photon-client/src/components/settings/MetricsCard.vue +++ b/photon-client/src/components/settings/MetricsCard.vue @@ -65,22 +65,22 @@ const platformMetrics = computed(() => { }, { header: "CPU Throttling", - value: useSettingsStore().metrics.cpuThr || "Unknown" + value: useSettingsStore().metrics.cpuThr?.toString() || "Unknown" }, { header: "CPU Uptime", - value: useSettingsStore().metrics.cpuUptime || "Unknown" + value: useSettingsStore().metrics.cpuUptime?.toString() || "Unknown" }, { header: "Disk Usage", - value: useSettingsStore().metrics.diskUtilPct || "Unknown" + value: useSettingsStore().metrics.diskUtilPct?.toString() || "Unknown" } ]; if (useSettingsStore().metrics.npuUsage) { stats.push({ header: "NPU Usage", - value: useSettingsStore().metrics.npuUsage || "Unknown" + value: useSettingsStore().metrics.npuUsage?.toString() || "Unknown" }); } diff --git a/photon-client/src/types/SettingTypes.ts b/photon-client/src/types/SettingTypes.ts index cec92713fb..5939ae75e6 100644 --- a/photon-client/src/types/SettingTypes.ts +++ b/photon-client/src/types/SettingTypes.ts @@ -25,16 +25,16 @@ export interface ObjectDetectionModelProperties { } export interface MetricData { - cpuTemp?: string; - cpuUtil?: string; - cpuMem?: string; - gpuMem?: string; - ramUtil?: string; - gpuMemUtil?: string; + cpuTemp?: number; + cpuUtil?: number; + cpuMem?: number; + gpuMem?: number; + ramUtil?: number; + gpuMemUtil?: number; cpuThr?: string; - cpuUptime?: string; - diskUtilPct?: string; - npuUsage?: string; + cpuUptime?: number; + diskUtilPct?: number; + npuUsage?: number[]; ipAddress?: string; } 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 index 0af13440a8..649dd05713 100644 --- 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 @@ -20,16 +20,16 @@ import org.photonvision.common.hardware.metrics.proto.DeviceMetricsProto; public record DeviceMetrics( - String cpuTemp, - String cpuUtil, - String cpuMem, + double cpuTemp, + double cpuUtil, + double cpuMem, String cpuThr, - String cpuUptime, - String gpuMem, - String ramUtil, - String gpuMemUtil, - String diskUtilPct, - String npuUsage, + double cpuUptime, + double gpuMem, + double ramUtil, + double gpuMemUtil, + double diskUtilPct, + double[] npuUsage, String ipAddress) { 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 1a4ee7975f..fb3fe8b1b1 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 @@ -66,58 +66,115 @@ public String safeExecute(String str) { } } - 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); + private double cpuMemSave = -2.0; + + public double getMemory() { + if (cpuMemSave == -2.0) { + try { + cpuMemSave = Double.parseDouble(safeExecute(cmds.cpuMemoryCommand)); + } catch (NumberFormatException e) { + cpuMemSave = -1.0; + } } return cpuMemSave; } - public String getTemp() { - return safeExecute(cmds.cpuTemperatureCommand); + public double getTemp() { + Double value; + try { + value = Double.parseDouble(safeExecute(cmds.cpuTemperatureCommand)); + } catch (NumberFormatException e) { + value = -1.0; + } + return value; } - public String getUtilization() { - return safeExecute(cmds.cpuUtilizationCommand); + public double getUtilization() { + Double value; + try { + value = Double.parseDouble(safeExecute(cmds.cpuUtilizationCommand)); + } catch (NumberFormatException e) { + value = -1.0; + } + return value; } - public String getUptime() { - return safeExecute(cmds.cpuUptimeCommand); + public double getUptime() { + Double value; + try { + value = Double.parseDouble(safeExecute(cmds.cpuUptimeCommand)); + } catch (NumberFormatException e) { + value = -1.0; + } + return value; } public String getThrottleReason() { return safeExecute(cmds.cpuThrottleReasonCmd); } - public String getNpuUsage() { - return safeExecute(cmds.npuUsageCommand); + boolean npuParseWarning = false; + + 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]); + } catch (NumberFormatException e) { + if (!npuParseWarning) { + logger.error("Failed to parse NPU usage value: " + usages[i], e); + npuParseWarning = true; + } + usageDoubles = null; // Default to null if parsing fails + break; + } + } + return usageDoubles; } - private String gpuMemSave = null; + private double gpuMemSave = -2.0; - public String getGPUMemorySplit() { - if (gpuMemSave == null) { - // only needs to run once - gpuMemSave = safeExecute(cmds.gpuMemoryCommand); + public double getGPUMemorySplit() { + if (gpuMemSave == -2.0) { + try { + gpuMemSave = Double.parseDouble(safeExecute(cmds.gpuMemoryCommand)); + } catch (NumberFormatException e) { + gpuMemSave = -1.0; + } } return gpuMemSave; } - public String getMallocedMemory() { - return safeExecute(cmds.gpuMemUsageCommand); + public double getMallocedMemory() { + Double value; + try { + value = Double.parseDouble(safeExecute(cmds.gpuMemUsageCommand)); + } catch (NumberFormatException e) { + value = -1.0; + } + return value; } - public String getUsedDiskPct() { - return safeExecute(cmds.diskUsageCommand); + public double getUsedDiskPct() { + Double value; + try { + value = Double.parseDouble(safeExecute(cmds.diskUsageCommand)); + } catch (NumberFormatException e) { + value = -1.0; + } + return value; } // TODO: Output in MBs for consistency - public String getUsedRam() { - return safeExecute(cmds.ramUsageCommand); + public double getUsedRam() { + Double value; + try { + value = Double.parseDouble(safeExecute(cmds.ramUsageCommand)); + } catch (NumberFormatException e) { + value = -1.0; + } + return value; } public String getIpAddress() { 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 56ad1b736c..6c8e244b84 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 @@ -29,12 +29,12 @@ public void initCmds(HardwareConfig config) { cpuUtilizationCommand = "top -bn1 | grep \"Cpu(s)\" | sed \"s/.*, *\\([0-9.]*\\)%* id.*/\\1/\" | awk '{print 100 - $1}'"; - cpuUptimeCommand = "uptime -p | cut -c 4-"; + cpuUptimeCommand = "cat /proc/uptime | cut -d ' ' -f1"; // RAM ramUsageCommand = "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/RK3588Cmds.java b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/RK3588Cmds.java index a215570bb2..b8be38ac6a 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 void initCmds(HardwareConfig config) { 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 index a416205915..32d0813713 100644 --- 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 @@ -50,7 +50,7 @@ public DeviceMetrics unpack(ProtobufDeviceMetrics msg) { msg.getRamUtil(), msg.getGpuMemUtil(), msg.getDiskUtilPct(), - msg.getNpuUsage(), + msg.getNpuUsage().toArray(), msg.getIpAddress()); } @@ -65,7 +65,7 @@ public void pack(ProtobufDeviceMetrics msg, DeviceMetrics value) { msg.setRamUtil(value.ramUtil()); msg.setGpuMemUtil(value.gpuMemUtil()); msg.setDiskUtilPct(value.diskUtilPct()); - msg.setNpuUsage(value.npuUsage()); + msg.addAllNpuUsage(value.npuUsage()); msg.setIpAddress(value.ipAddress()); } } diff --git a/photon-targeting/src/main/proto/photon.proto b/photon-targeting/src/main/proto/photon.proto index 27072fc426..fd8dfb39d3 100644 --- a/photon-targeting/src/main/proto/photon.proto +++ b/photon-targeting/src/main/proto/photon.proto @@ -70,15 +70,15 @@ message ProtobufPhotonPipelineResult { } message ProtobufDeviceMetrics { - string cpu_temp = 1; - string cpu_util = 2; - string cpu_mem = 3; + double cpu_temp = 1; + double cpu_util = 2; + double cpu_mem = 3; string cpu_thr = 4; - string cpu_uptime = 5; - string gpu_mem = 6; - string ram_util = 7; - string gpu_mem_util = 8; - string disk_util_pct = 9; - string npu_usage = 10; + double cpu_uptime = 5; + double gpu_mem = 6; + double ram_util = 7; + double gpu_mem_util = 8; + double disk_util_pct = 9; + repeated double npu_usage = 10; string ip_address = 11; } From f8d2589c4794d55e9a5c56ee26b97cea5e2c86ad Mon Sep 17 00:00:00 2001 From: samfreund Date: Thu, 10 Jul 2025 00:24:52 -0500 Subject: [PATCH 11/20] rework metrics frontend for numbers --- .../src/components/settings/MetricsCard.vue | 49 +++++++++++++------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/photon-client/src/components/settings/MetricsCard.vue b/photon-client/src/components/settings/MetricsCard.vue index d31bedc119..8b9fd89859 100644 --- a/photon-client/src/components/settings/MetricsCard.vue +++ b/photon-client/src/components/settings/MetricsCard.vue @@ -40,6 +40,8 @@ const generalMetrics = computed(() => { }); const platformMetrics = computed(() => { + const ramUtil = useSettingsStore().metrics.ramUtil; + const cpuMem = useSettingsStore().metrics.cpuMem; const stats = [ { header: "CPU Temp", @@ -52,16 +54,9 @@ const platformMetrics = computed(() => { { header: "CPU Memory Usage", value: - useSettingsStore().metrics.ramUtil === undefined || useSettingsStore().metrics.cpuMem === undefined - ? "Unknown" - : `${useSettingsStore().metrics.ramUtil || "Unknown"}MB of ${useSettingsStore().metrics.cpuMem}MB` - }, - { - header: "GPU Memory Usage", - value: - useSettingsStore().metrics.gpuMemUtil === undefined || useSettingsStore().metrics.gpuMem === undefined - ? "Unknown" - : `${useSettingsStore().metrics.gpuMemUtil}MB of ${useSettingsStore().metrics.gpuMem}MB` + (ramUtil&& cpuMem && ramUtil >= 0 && cpuMem >= 0) + + ? `${useSettingsStore().metrics.ramUtil}MB of ${useSettingsStore().metrics.cpuMem}MB` : "Unknown" }, { header: "CPU Throttling", @@ -69,18 +64,44 @@ const platformMetrics = computed(() => { }, { header: "CPU Uptime", - value: useSettingsStore().metrics.cpuUptime?.toString() || "Unknown" + value: (() => { + const seconds = useSettingsStore().metrics.cpuUptime; + 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 remainingSeconds = Math.floor(seconds % 60); + + let result = ''; + if (days > 0) result += `${days}d `; + if (hours > 0 || days > 0) result += `${hours}h `; + if (minutes > 0 || hours > 0 || days > 0) result += `${minutes}m `; + result += `${remainingSeconds}s`; + + return result; + })() }, { header: "Disk Usage", - value: useSettingsStore().metrics.diskUtilPct?.toString() || "Unknown" + value: useSettingsStore().metrics.diskUtilPct === undefined ? "Unknown" : `${useSettingsStore().metrics.diskUtilPct}%` } ]; - if (useSettingsStore().metrics.npuUsage) { + const npuUsage = useSettingsStore().metrics.npuUsage; + if (npuUsage) { stats.push({ header: "NPU Usage", - value: useSettingsStore().metrics.npuUsage?.toString() || "Unknown" + value: useSettingsStore().metrics.npuUsage?.map((usage, index) => `Core${index} ${usage}%`).join(', ') || "Unknown" + }); + } + + const gpuMem = useSettingsStore().metrics.gpuMem; + const gpuMemUtil = useSettingsStore().metrics.gpuMemUtil; + if (gpuMem && gpuMemUtil && gpuMem > 0 && gpuMemUtil > 0) { + stats.push({ + header: "GPU Memory Usage", + value: `${gpuMemUtil}MB of ${gpuMem}MB` }); } From dabd2755c572cb42089405dc7b4bf225f4480942 Mon Sep 17 00:00:00 2001 From: samfreund Date: Thu, 10 Jul 2025 17:21:50 -0500 Subject: [PATCH 12/20] refactor names for clarity --- .../src/components/settings/MetricsCard.vue | 22 ++-- .../stores/settings/GeneralSettingsStore.ts | 20 +-- photon-client/src/types/SettingTypes.ts | 8 +- .../hardware/metrics/DeviceMetrics.java | 8 +- .../hardware/metrics/MetricsManager.java | 122 +++++++++--------- .../common/hardware/metrics/cmds/CmdBase.java | 13 +- .../hardware/metrics/cmds/FileCmds.java | 11 +- .../hardware/metrics/cmds/LinuxCmds.java | 10 +- .../common/hardware/metrics/cmds/PiCmds.java | 4 +- .../metrics/proto/DeviceMetricsProto.java | 12 +- .../photonvision/hardware/HardwareTest.java | 10 +- photon-targeting/src/main/proto/photon.proto | 16 +-- 12 files changed, 134 insertions(+), 122 deletions(-) diff --git a/photon-client/src/components/settings/MetricsCard.vue b/photon-client/src/components/settings/MetricsCard.vue index 8b9fd89859..ccbdbb57b1 100644 --- a/photon-client/src/components/settings/MetricsCard.vue +++ b/photon-client/src/components/settings/MetricsCard.vue @@ -41,7 +41,7 @@ const generalMetrics = computed(() => { const platformMetrics = computed(() => { const ramUtil = useSettingsStore().metrics.ramUtil; - const cpuMem = useSettingsStore().metrics.cpuMem; + const ramMem = useSettingsStore().metrics.ramMem; const stats = [ { header: "CPU Temp", @@ -54,18 +54,18 @@ const platformMetrics = computed(() => { { header: "CPU Memory Usage", value: - (ramUtil&& cpuMem && ramUtil >= 0 && cpuMem >= 0) - - ? `${useSettingsStore().metrics.ramUtil}MB of ${useSettingsStore().metrics.cpuMem}MB` : "Unknown" + ramUtil && ramMem && ramUtil >= 0 && ramMem >= 0 + ? `${useSettingsStore().metrics.ramUtil}MB of ${useSettingsStore().metrics.ramMem}MB` + : "Unknown" }, { header: "CPU Throttling", value: useSettingsStore().metrics.cpuThr?.toString() || "Unknown" }, { - header: "CPU Uptime", + header: "Uptime", value: (() => { - const seconds = useSettingsStore().metrics.cpuUptime; + const seconds = useSettingsStore().metrics.uptime; if (seconds === undefined) return "Unknown"; const days = Math.floor(seconds / 86400); @@ -73,7 +73,7 @@ const platformMetrics = computed(() => { const minutes = Math.floor((seconds % 3600) / 60); const remainingSeconds = Math.floor(seconds % 60); - let result = ''; + let result = ""; if (days > 0) result += `${days}d `; if (hours > 0 || days > 0) result += `${hours}h `; if (minutes > 0 || hours > 0 || days > 0) result += `${minutes}m `; @@ -84,7 +84,8 @@ const platformMetrics = computed(() => { }, { header: "Disk Usage", - value: useSettingsStore().metrics.diskUtilPct === undefined ? "Unknown" : `${useSettingsStore().metrics.diskUtilPct}%` + value: + useSettingsStore().metrics.diskUtilPct === undefined ? "Unknown" : `${useSettingsStore().metrics.diskUtilPct}%` } ]; @@ -92,7 +93,10 @@ const platformMetrics = computed(() => { if (npuUsage) { stats.push({ header: "NPU Usage", - value: useSettingsStore().metrics.npuUsage?.map((usage, index) => `Core${index} ${usage}%`).join(', ') || "Unknown" + value: + useSettingsStore() + .metrics.npuUsage?.map((usage, index) => `Core${index} ${usage}%`) + .join(", ") || "Unknown" }); } diff --git a/photon-client/src/stores/settings/GeneralSettingsStore.ts b/photon-client/src/stores/settings/GeneralSettingsStore.ts index e5f3458236..28b0f7ad03 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, + cpuThr: undefined, + ramMem: undefined, ramUtil: undefined, + gpuMem: undefined, gpuMemUtil: undefined, - cpuThr: undefined, - cpuUptime: 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, + cpuThr: data.cpuThr || undefined, + ramMem: data.ramMem || undefined, ramUtil: data.ramUtil || undefined, + gpuMem: data.gpuMem || undefined, gpuMemUtil: data.gpuMemUtil || undefined, - cpuThr: data.cpuThr || undefined, - cpuUptime: data.cpuUptime || 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 5939ae75e6..c8c59c50d8 100644 --- a/photon-client/src/types/SettingTypes.ts +++ b/photon-client/src/types/SettingTypes.ts @@ -27,15 +27,15 @@ export interface ObjectDetectionModelProperties { export interface MetricData { cpuTemp?: number; cpuUtil?: number; - cpuMem?: number; - gpuMem?: number; + cpuThr?: string; + ramMem?: number; ramUtil?: number; + gpuMem?: number; gpuMemUtil?: number; - cpuThr?: string; - cpuUptime?: number; diskUtilPct?: number; npuUsage?: number[]; ipAddress?: string; + uptime?: number; } export enum NetworkConnectionType { 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 index 649dd05713..4c31a20fd9 100644 --- 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 @@ -22,14 +22,14 @@ public record DeviceMetrics( double cpuTemp, double cpuUtil, - double cpuMem, String cpuThr, - double cpuUptime, - double gpuMem, + double ramMem, double ramUtil, + double gpuMem, double gpuMemUtil, double diskUtilPct, double[] npuUsage, - String ipAddress) { + 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 fb3fe8b1b1..7ee476ab22 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 @@ -66,20 +66,7 @@ public String safeExecute(String str) { } } - private double cpuMemSave = -2.0; - - public double getMemory() { - if (cpuMemSave == -2.0) { - try { - cpuMemSave = Double.parseDouble(safeExecute(cmds.cpuMemoryCommand)); - } catch (NumberFormatException e) { - cpuMemSave = -1.0; - } - } - return cpuMemSave; - } - - public double getTemp() { + public double getCpuTemp() { Double value; try { value = Double.parseDouble(safeExecute(cmds.cpuTemperatureCommand)); @@ -89,7 +76,7 @@ public double getTemp() { return value; } - public double getUtilization() { + public double getCpuUtilization() { Double value; try { value = Double.parseDouble(safeExecute(cmds.cpuUtilizationCommand)); @@ -99,46 +86,44 @@ public double getUtilization() { return value; } - public double getUptime() { - Double value; - try { - value = Double.parseDouble(safeExecute(cmds.cpuUptimeCommand)); - } catch (NumberFormatException e) { - value = -1.0; - } - return value; - } - public String getThrottleReason() { return safeExecute(cmds.cpuThrottleReasonCmd); } - boolean npuParseWarning = false; + // This is only ran once as it retrieves total memory, this will not change over the course of + // time + private double ramMemSave = -2.0; - public double[] getNpuUsage() { - String[] usages = safeExecute(cmds.npuUsageCommand).split(","); - double[] usageDoubles = new double[usages.length]; - for (int i = 0; i < usages.length; i++) { + public double getRamMem() { + if (ramMemSave == -2.0) { try { - usageDoubles[i] = Double.parseDouble(usages[i]); + ramMemSave = Double.parseDouble(safeExecute(cmds.ramMemCommand)); } catch (NumberFormatException e) { - if (!npuParseWarning) { - logger.error("Failed to parse NPU usage value: " + usages[i], e); - npuParseWarning = true; - } - usageDoubles = null; // Default to null if parsing fails - break; + ramMemSave = -1.0; } } - return usageDoubles; + return ramMemSave; + } + + // TODO: Output in MBs for consistency + public double getRamUtil() { + Double value; + try { + value = Double.parseDouble(safeExecute(cmds.ramUtilCommand)); + } catch (NumberFormatException e) { + value = -1.0; + } + return value; } + // This is only ran once as it retrieves total memory, this will not change over the course of + // time private double gpuMemSave = -2.0; - public double getGPUMemorySplit() { + public double getGpuMem() { if (gpuMemSave == -2.0) { try { - gpuMemSave = Double.parseDouble(safeExecute(cmds.gpuMemoryCommand)); + gpuMemSave = Double.parseDouble(safeExecute(cmds.gpuMemCommand)); } catch (NumberFormatException e) { gpuMemSave = -1.0; } @@ -146,10 +131,10 @@ public double getGPUMemorySplit() { return gpuMemSave; } - public double getMallocedMemory() { + public double getGpuMemUtil() { Double value; try { - value = Double.parseDouble(safeExecute(cmds.gpuMemUsageCommand)); + value = Double.parseDouble(safeExecute(cmds.gpuMemUtilCommand)); } catch (NumberFormatException e) { value = -1.0; } @@ -166,15 +151,26 @@ public double getUsedDiskPct() { return value; } - // TODO: Output in MBs for consistency - public double getUsedRam() { - Double value; - try { - value = Double.parseDouble(safeExecute(cmds.ramUsageCommand)); - } catch (NumberFormatException e) { - value = -1.0; + // This is here so we don't spam logs if it fails + boolean npuParseWarning = false; + + 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 = null; // Default to null if parsing fails + break; + } } - return value; + return usageDoubles; } public String getIpAddress() { @@ -185,19 +181,29 @@ public String getIpAddress() { return addr; } + public double getUptime() { + Double value; + try { + value = Double.parseDouble(safeExecute(cmds.uptimeCommand)); + } catch (NumberFormatException e) { + value = -1.0; + } + return value; + } + public DeviceMetrics getMetrics() { return new DeviceMetrics( - this.getTemp(), - this.getUtilization(), - this.getMemory(), + this.getCpuTemp(), + this.getCpuUtilization(), this.getThrottleReason(), - this.getUptime(), - this.getGPUMemorySplit(), - this.getUsedRam(), - this.getMallocedMemory(), + this.getRamMem(), + this.getRamUtil(), + this.getGpuMem(), + this.getGpuMemUtil(), this.getUsedDiskPct(), this.getNpuUsage(), - this.getIpAddress()); + this.getIpAddress(), + this.getUptime()); } public void publishMetrics() { 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 1580b5d665..78ec89edc1 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 @@ 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 bda20e0afc..6c988feeb9 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 @@ 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 6c8e244b84..6db4dbce97 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,18 +21,18 @@ 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 = "cat /proc/uptime | cut -d ' ' -f1"; + // 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 | 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 88ed23f22c..57680726e9 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 void initCmds(HardwareConfig config) { + " 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/proto/DeviceMetricsProto.java b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/proto/DeviceMetricsProto.java index 32d0813713..4033e2dd43 100644 --- 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 @@ -43,24 +43,24 @@ public DeviceMetrics unpack(ProtobufDeviceMetrics msg) { return new DeviceMetrics( msg.getCpuTemp(), msg.getCpuUtil(), - msg.getCpuMem(), msg.getCpuThr(), - msg.getCpuUptime(), - msg.getGpuMem(), + msg.getRamMem(), msg.getRamUtil(), + msg.getGpuMem(), msg.getGpuMemUtil(), msg.getDiskUtilPct(), msg.getNpuUsage().toArray(), - msg.getIpAddress()); + msg.getIpAddress(), + msg.getUptime()); } @Override public void pack(ProtobufDeviceMetrics msg, DeviceMetrics value) { msg.setCpuTemp(value.cpuTemp()); msg.setCpuUtil(value.cpuUtil()); - msg.setCpuMem(value.cpuMem()); + msg.setRamMem(value.ramMem()); msg.setCpuThr(value.cpuThr()); - msg.setCpuUptime(value.cpuUptime()); + msg.setUptime(value.uptime()); msg.setGpuMem(value.gpuMem()); msg.setRamUtil(value.ramUtil()); msg.setGpuMemUtil(value.gpuMemUtil()); 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 9d371d25f8..8e53b4e9ae 100644 --- a/photon-core/src/test/java/org/photonvision/hardware/HardwareTest.java +++ b/photon-core/src/test/java/org/photonvision/hardware/HardwareTest.java @@ -37,15 +37,15 @@ public void testHardware() { 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 fd8dfb39d3..0ff3991f9d 100644 --- a/photon-targeting/src/main/proto/photon.proto +++ b/photon-targeting/src/main/proto/photon.proto @@ -72,13 +72,13 @@ message ProtobufPhotonPipelineResult { message ProtobufDeviceMetrics { double cpu_temp = 1; double cpu_util = 2; - double cpu_mem = 3; - string cpu_thr = 4; - double cpu_uptime = 5; + string cpu_thr = 3; + double ram_mem = 4; + double ram_util = 5; double gpu_mem = 6; - double ram_util = 7; - double gpu_mem_util = 8; - double disk_util_pct = 9; - repeated double npu_usage = 10; - string ip_address = 11; + double gpu_mem_util = 7; + double disk_util_pct = 8; + repeated double npu_usage = 9; + string ip_address = 10; + double uptime = 11; } From 74d89ca15f224a02d676eaaf20cb0a3c158f1603 Mon Sep 17 00:00:00 2001 From: samfreund Date: Fri, 11 Jul 2025 15:59:46 -0500 Subject: [PATCH 13/20] refactor frontend uptime display to use Intl API --- .../src/components/settings/MetricsCard.vue | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/photon-client/src/components/settings/MetricsCard.vue b/photon-client/src/components/settings/MetricsCard.vue index ccbdbb57b1..985740acdb 100644 --- a/photon-client/src/components/settings/MetricsCard.vue +++ b/photon-client/src/components/settings/MetricsCard.vue @@ -71,15 +71,15 @@ const platformMetrics = computed(() => { const days = Math.floor(seconds / 86400); const hours = Math.floor((seconds % 86400) / 3600); const minutes = Math.floor((seconds % 3600) / 60); - const remainingSeconds = Math.floor(seconds % 60); + const secs = seconds % 60; - let result = ""; - if (days > 0) result += `${days}d `; - if (hours > 0 || days > 0) result += `${hours}h `; - if (minutes > 0 || hours > 0 || days > 0) result += `${minutes}m `; - result += `${remainingSeconds}s`; - - return result; + // @ts-expect-error This uses Intl.DurationFormat which is newly implemented and not available in TS. + return new Intl.DurationFormat("en", { style: "narrow" }).format({ + days: days, + hours: hours, + minutes: minutes, + seconds: secs + }); })() }, { From 364d7f9374f5214a0157e7eda2f0ce251b6b47a7 Mon Sep 17 00:00:00 2001 From: samfreund Date: Fri, 11 Jul 2025 17:21:46 -0500 Subject: [PATCH 14/20] annotate previous code as unused --- .../common/dataflow/networktables/NetworkTablesManager.java | 2 ++ 1 file changed, 2 insertions(+) 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 ace01c39f5..f225b14410 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 @@ -62,6 +62,8 @@ public class NetworkTablesManager { 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 + "/"}); From cda9f0c14641f0eca82d04f2dc408027e3cf5703 Mon Sep 17 00:00:00 2001 From: samfreund Date: Fri, 11 Jul 2025 17:37:22 -0500 Subject: [PATCH 15/20] javadoc --- .../hardware/metrics/MetricsManager.java | 59 +++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) 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 7ee476ab22..7f1a73c80e 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 @@ -66,6 +66,11 @@ public String safeExecute(String str) { } } + /** + * 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() { Double value; try { @@ -76,6 +81,11 @@ public double getCpuTemp() { return value; } + /** + * 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() { Double value; try { @@ -86,14 +96,22 @@ public double getCpuUtilization() { return value; } + /** + * 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); } - // This is only ran once as it retrieves total memory, this will not change over the course of - // time 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 { @@ -106,6 +124,11 @@ public double getRamMem() { } // TODO: Output in MBs for consistency + /** + * 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() { Double value; try { @@ -116,10 +139,13 @@ public double getRamUtil() { return value; } - // This is only ran once as it retrieves total memory, this will not change over the course of - // time 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 { @@ -131,6 +157,11 @@ public double getGpuMem() { return gpuMemSave; } + /** + * 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() { Double value; try { @@ -141,6 +172,11 @@ public double getGpuMemUtil() { return value; } + /** + * 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() { Double value; try { @@ -154,6 +190,11 @@ public double getUsedDiskPct() { // 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]; @@ -173,6 +214,11 @@ public double[] getNpuUsage() { 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 + "\""); @@ -181,6 +227,11 @@ public String getIpAddress() { 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() { Double value; try { From 70a90124f38ebaf263e8d36fd52d28fd1b1ca6db Mon Sep 17 00:00:00 2001 From: samfreund Date: Sat, 12 Jul 2025 00:08:20 -0500 Subject: [PATCH 16/20] refactor frontend with copy of useSettingsStore --- .../src/components/settings/MetricsCard.vue | 46 ++++++++----------- .../hardware/metrics/MetricsManager.java | 2 +- 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/photon-client/src/components/settings/MetricsCard.vue b/photon-client/src/components/settings/MetricsCard.vue index 985740acdb..8e6a1cf883 100644 --- a/photon-client/src/components/settings/MetricsCard.vue +++ b/photon-client/src/components/settings/MetricsCard.vue @@ -40,72 +40,62 @@ const generalMetrics = computed(() => { }); const platformMetrics = computed(() => { - const ramUtil = useSettingsStore().metrics.ramUtil; - const ramMem = useSettingsStore().metrics.ramMem; + 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: - ramUtil && ramMem && ramUtil >= 0 && ramMem >= 0 - ? `${useSettingsStore().metrics.ramUtil}MB of ${useSettingsStore().metrics.ramMem}MB` + metrics.ramUtil && metrics.ramMem && metrics.ramUtil >= 0 && metrics.ramMem >= 0 + ? `${metrics.ramUtil}MB of ${metrics.ramMem}MB` : "Unknown" }, { header: "CPU Throttling", - value: useSettingsStore().metrics.cpuThr?.toString() || "Unknown" + value: metrics.cpuThr?.toString() || "Unknown" }, { header: "Uptime", value: (() => { - const seconds = useSettingsStore().metrics.uptime; + 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 = seconds % 60; + const secs = Math.floor(seconds % 60); - // @ts-expect-error This uses Intl.DurationFormat which is newly implemented and not available in TS. - return new Intl.DurationFormat("en", { style: "narrow" }).format({ - days: days, - hours: hours, - minutes: minutes, - seconds: secs - }); + var result = ""; + if (days > 0) result += `${days}d `; + if (hours > 0) result += `${hours}h `; + if (minutes > 0) result += `${minutes}m `; + return (result += `${secs}s`); })() }, { header: "Disk Usage", - value: - useSettingsStore().metrics.diskUtilPct === undefined ? "Unknown" : `${useSettingsStore().metrics.diskUtilPct}%` + value: metrics.diskUtilPct === undefined ? "Unknown" : `${metrics.diskUtilPct}%` } ]; - const npuUsage = useSettingsStore().metrics.npuUsage; - if (npuUsage) { + if (metrics.npuUsage && metrics.npuUsage.length > 0) { stats.push({ header: "NPU Usage", - value: - useSettingsStore() - .metrics.npuUsage?.map((usage, index) => `Core${index} ${usage}%`) - .join(", ") || "Unknown" + value: metrics.npuUsage?.map((usage, index) => `Core${index} ${usage}%`).join(", ") || "Unknown" }); } - const gpuMem = useSettingsStore().metrics.gpuMem; - const gpuMemUtil = useSettingsStore().metrics.gpuMemUtil; - if (gpuMem && gpuMemUtil && gpuMem > 0 && gpuMemUtil > 0) { + if (metrics.gpuMem && metrics.gpuMemUtil && metrics.gpuMem > 0 && metrics.gpuMemUtil > 0) { stats.push({ header: "GPU Memory Usage", - value: `${gpuMemUtil}MB of ${gpuMem}MB` + value: `${metrics.gpuMemUtil}MB of ${metrics.gpuMem}MB` }); } 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 7f1a73c80e..a5786e1b39 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 @@ -207,7 +207,7 @@ public double[] getNpuUsage() { logger.error("Failed to parse NPU usage value: " + usages[i], e); npuParseWarning = true; } - usageDoubles = null; // Default to null if parsing fails + usageDoubles = new double[0]; // Default to empty array if parsing fails break; } } From 2e959d92add255af8012f6cd1c786ac47ef35068 Mon Sep 17 00:00:00 2001 From: samfreund Date: Sat, 12 Jul 2025 00:21:24 -0500 Subject: [PATCH 17/20] periodic updates from hardware manager --- .../networktables/NetworkTablesManager.java | 17 +--- .../common/hardware/HardwareManager.java | 12 +-- .../hardware/metrics/MetricsManager.java | 78 +++++++++---------- 3 files changed, 41 insertions(+), 66 deletions(-) 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 f225b14410..2a44f6738b 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 @@ -18,14 +18,12 @@ package org.photonvision.common.dataflow.networktables; import edu.wpi.first.apriltag.AprilTagFieldLayout; -import edu.wpi.first.cscore.CameraServerJNI; import edu.wpi.first.networktables.LogMessage; import edu.wpi.first.networktables.MultiSubscriber; import edu.wpi.first.networktables.NetworkTable; import edu.wpi.first.networktables.NetworkTableEvent; import edu.wpi.first.networktables.NetworkTableEvent.Kind; import edu.wpi.first.networktables.NetworkTableInstance; -import edu.wpi.first.networktables.ProtobufPublisher; import edu.wpi.first.networktables.StringSubscriber; import edu.wpi.first.wpilibj.Alert; import edu.wpi.first.wpilibj.Alert.AlertType; @@ -42,7 +40,6 @@ import org.photonvision.common.dataflow.events.OutgoingUIEvent; import org.photonvision.common.dataflow.websocket.UIPhotonConfiguration; import org.photonvision.common.hardware.HardwareManager; -import org.photonvision.common.hardware.metrics.DeviceMetrics; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.LogLevel; import org.photonvision.common.logging.Logger; @@ -58,7 +55,7 @@ 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); @@ -78,12 +75,6 @@ public class NetworkTablesManager { private StringSubscriber m_fieldLayoutSubscriber = kRootTable.getStringTopic(kFieldLayoutName).subscribe(""); - ProtobufPublisher metricPublisher = - kRootTable - .getSubTable("/" + kCoprocTableName + "/metrics") - .getProtobufTopic(CameraServerJNI.getHostname(), DeviceMetrics.proto) - .publish(); - private final TimeSyncManager m_timeSync = new TimeSyncManager(kRootTable); NTDriverStation ntDriverStation; @@ -238,10 +229,6 @@ private void broadcastVersion() { kRootTable.getEntry("buildDate").setString(PhotonVersion.buildDate); } - private void broadcastMetrics() { - metricPublisher.set(HardwareManager.getInstance().getMetrics()); - } - /** * Publishes the hostname and camera names to a table using the MAC address as a key. Then checks * for conflicts of hostname or camera names across other coprocessors that are also publishing to @@ -391,8 +378,6 @@ private void ntTick() { logger.error( "[NetworkTablesManager] Could not connect to the robot! Will retry in the background..."); } - - broadcastMetrics(); } public long getTimeSinceLastPong() { 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 95977bb1be..8e8035169c 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 @@ -29,7 +29,6 @@ import org.photonvision.common.dataflow.networktables.NetworkTablesManager; import org.photonvision.common.hardware.GPIO.CustomGPIO; import org.photonvision.common.hardware.GPIO.pi.PigpioSocket; -import org.photonvision.common.hardware.metrics.DeviceMetrics; import org.photonvision.common.hardware.metrics.MetricsManager; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; @@ -77,6 +76,9 @@ private HardwareManager(HardwareConfig hardwareConfig, HardwareSettings hardware this.metricsManager = new MetricsManager(); this.metricsManager.setConfig(hardwareConfig); + TimedTaskManager.getInstance() + .addTask("Metrics Publisher", this.metricsManager::publishMetrics, 5000); + ledModeRequest = NetworkTablesManager.getInstance() .kRootTable @@ -223,15 +225,7 @@ private void statusLEDUpdate() { blinkCounter++; } - public HardwareConfig getConfig() { - return hardwareConfig; - } - public void publishMetrics() { metricsManager.publishMetrics(); } - - public DeviceMetrics getMetrics() { - return metricsManager.getMetrics(); - } } 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 a5786e1b39..0a8a73f5aa 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,12 +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 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; @@ -39,6 +42,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) { @@ -72,13 +82,11 @@ public String safeExecute(String str) { * @return The CPU temperature in Celsius, or -1.0 if the command fails or parsing fails. */ public double getCpuTemp() { - Double value; try { - value = Double.parseDouble(safeExecute(cmds.cpuTemperatureCommand)); + return Double.parseDouble(safeExecute(cmds.cpuTemperatureCommand)); } catch (NumberFormatException e) { - value = -1.0; + return -1.0; } - return value; } /** @@ -87,13 +95,11 @@ public double getCpuTemp() { * @return The CPU utilization as a percentage, or -1.0 if the command fails or parsing fails. */ public double getCpuUtilization() { - Double value; try { - value = Double.parseDouble(safeExecute(cmds.cpuUtilizationCommand)); + return Double.parseDouble(safeExecute(cmds.cpuUtilizationCommand)); } catch (NumberFormatException e) { - value = -1.0; + return -1.0; } - return value; } /** @@ -123,20 +129,17 @@ public double getRamMem() { return ramMemSave; } - // TODO: Output in MBs for consistency /** * 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() { - Double value; try { - value = Double.parseDouble(safeExecute(cmds.ramUtilCommand)); + return Double.parseDouble(safeExecute(cmds.ramUtilCommand)); } catch (NumberFormatException e) { - value = -1.0; + return -1.0; } - return value; } private double gpuMemSave = -2.0; @@ -163,13 +166,11 @@ public double getGpuMem() { * @return The GPU memory utilization in MBs, or -1.0 if the command fails or parsing fails. */ public double getGpuMemUtil() { - Double value; try { - value = Double.parseDouble(safeExecute(cmds.gpuMemUtilCommand)); + return Double.parseDouble(safeExecute(cmds.gpuMemUtilCommand)); } catch (NumberFormatException e) { - value = -1.0; + return -1.0; } - return value; } /** @@ -178,13 +179,11 @@ public double getGpuMemUtil() { * @return The percentage of disk space used, or -1.0 if the command fails or parsing fails. */ public double getUsedDiskPct() { - Double value; try { - value = Double.parseDouble(safeExecute(cmds.diskUsageCommand)); + return Double.parseDouble(safeExecute(cmds.diskUsageCommand)); } catch (NumberFormatException e) { - value = -1.0; + return -1.0; } - return value; } // This is here so we don't spam logs if it fails @@ -233,33 +232,30 @@ public String getIpAddress() { * @return The uptime in seconds, or -1.0 if the command fails or parsing fails. */ public double getUptime() { - Double value; try { - value = Double.parseDouble(safeExecute(cmds.uptimeCommand)); + return Double.parseDouble(safeExecute(cmds.uptimeCommand)); } catch (NumberFormatException e) { - value = -1.0; + return -1.0; } - return value; - } - - public DeviceMetrics getMetrics() { - return new DeviceMetrics( - this.getCpuTemp(), - this.getCpuUtilization(), - this.getThrottleReason(), - this.getRamMem(), - this.getRamUtil(), - this.getGpuMem(), - this.getGpuMemUtil(), - this.getUsedDiskPct(), - this.getNpuUsage(), - this.getIpAddress(), - this.getUptime()); } public void publishMetrics() { logger.debug("Publishing Metrics..."); - var metrics = getMetrics(); + 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()); + + metricPublisher.set(metrics); DataChangeService.getInstance().publishEvent(OutgoingUIEvent.wrappedOf("metrics", metrics)); } From fa910247ab16b3a1381eaa1324d8a2e7fdf89c92 Mon Sep 17 00:00:00 2001 From: Gold856 <117957790+Gold856@users.noreply.github.com> Date: Sat, 12 Jul 2025 01:14:37 -0400 Subject: [PATCH 18/20] Fix DurationFormatter --- .../src/components/settings/MetricsCard.vue | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/photon-client/src/components/settings/MetricsCard.vue b/photon-client/src/components/settings/MetricsCard.vue index 8e6a1cf883..48e56287df 100644 --- a/photon-client/src/components/settings/MetricsCard.vue +++ b/photon-client/src/components/settings/MetricsCard.vue @@ -39,6 +39,8 @@ 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 = [ @@ -72,11 +74,12 @@ const platformMetrics = computed(() => { const minutes = Math.floor((seconds % 3600) / 60); const secs = Math.floor(seconds % 60); - var result = ""; - if (days > 0) result += `${days}d `; - if (hours > 0) result += `${hours}h `; - if (minutes > 0) result += `${minutes}m `; - return (result += `${secs}s`); + return durationFormatter.format({ + days: days, + hours: hours, + minutes: minutes, + seconds: secs + }); })() }, { From 0ac0f393d1955cb93fc0690288f9aa751cccfcff Mon Sep 17 00:00:00 2001 From: samfreund Date: Sat, 12 Jul 2025 01:25:44 -0500 Subject: [PATCH 19/20] make throttling conditional --- photon-client/src/components/settings/MetricsCard.vue | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/photon-client/src/components/settings/MetricsCard.vue b/photon-client/src/components/settings/MetricsCard.vue index 48e56287df..adbc4468f8 100644 --- a/photon-client/src/components/settings/MetricsCard.vue +++ b/photon-client/src/components/settings/MetricsCard.vue @@ -59,10 +59,6 @@ const platformMetrics = computed(() => { ? `${metrics.ramUtil}MB of ${metrics.ramMem}MB` : "Unknown" }, - { - header: "CPU Throttling", - value: metrics.cpuThr?.toString() || "Unknown" - }, { header: "Uptime", value: (() => { @@ -102,6 +98,13 @@ const platformMetrics = computed(() => { }); } + if (metrics.cpuThr) { + stats.push({ + header: "CPU Throttling", + value: metrics.cpuThr.toString() + }); + } + return stats; }); From 9b79cf0e099522b135e152a346f7d31d4bc2956a Mon Sep 17 00:00:00 2001 From: Gold856 <117957790+Gold856@users.noreply.github.com> Date: Sat, 12 Jul 2025 03:44:08 -0400 Subject: [PATCH 20/20] Load libraries in HardwareTest --- .../java/org/photonvision/hardware/HardwareTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) 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 8e53b4e9ae..f5fa86cbe5 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 @@ 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;