Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
598827b
initial oshi integration
crschardt Dec 21, 2025
1a2d198
use Files for getting available disk space
crschardt Dec 21, 2025
5530413
log mebibytes
crschardt Dec 21, 2025
14a38bb
try diozero for cpu temperature
crschardt Dec 22, 2025
259662a
monitor cpu
crschardt Dec 22, 2025
a71c224
basic network traffic tracking
crschardt Dec 22, 2025
b1e27a1
monitor almost everything with oshi
crschardt Dec 23, 2025
55cf8e0
add subclass for RaspberryPi-specific items
crschardt Dec 23, 2025
26e94f1
add subclass for RK3588 NPU load
crschardt Dec 23, 2025
24f8256
replace MetricsManager with SystemMonitor
crschardt Dec 23, 2025
88e0ca0
fix number (lack of) formatting
crschardt Dec 23, 2025
0c2778a
improved profiling
crschardt Dec 23, 2025
400fd0f
enable logging of DeviceMetrics for debugging
crschardt Dec 23, 2025
d937abd
show network traffic in UI
crschardt Dec 23, 2025
7e64729
publish metrics should only be called from the monitor
crschardt Dec 23, 2025
f7544ae
test adding jLibreHardwareMonitor for Windows computers
crschardt Dec 23, 2025
ef14940
it didn't help
crschardt Dec 23, 2025
1e7a480
acknowledge OSHI
crschardt Dec 23, 2025
5ce9f83
add disk space metrics, refactor network traffic, add comments, and c…
crschardt Dec 29, 2025
f0b240e
Refactor and document thermal zones for temperature monitoring
crschardt Dec 29, 2025
bfc4901
Add subclass for RubikPi3
crschardt Dec 29, 2025
cd75121
wpiformat doesn't like the blank line
crschardt Dec 29, 2025
6b4ca07
missed renaming sent and recv in NetworkTables
crschardt Dec 29, 2025
93bb733
remove metric update button and replace with last updated text
crschardt Jan 1, 2026
c474cea
Remove MetricsManager and clean up
crschardt Jan 1, 2026
14c96ea
allow changing the system monitor update interval
crschardt Jan 1, 2026
e79bcc7
remove metrics manager cmds from the database
crschardt Jan 1, 2026
ae3f79e
typo
crschardt Jan 1, 2026
87f80d0
Merge branch 'main' into feat/oshi-metrics-manager
crschardt Jan 1, 2026
d236201
minor fixes
crschardt Jan 1, 2026
a1f8a8c
update tests
crschardt Jan 1, 2026
855bf79
Remove test for MetricsManager
crschardt Jan 1, 2026
1af6f1c
monospace metrics
samfreund Jan 1, 2026
6ca13bd
addressed comments
crschardt Jan 1, 2026
b1baaf3
Merge branch 'feat/oshi-metrics-manager' of https://github.com/crscha…
crschardt Jan 1, 2026
33ef9a3
monospace update time also
crschardt Jan 1, 2026
b3106f4
addressing comments
crschardt Jan 2, 2026
fdf9a7d
spotless
crschardt Jan 2, 2026
85b3507
frontend cleanup
crschardt Jan 2, 2026
7ca8976
Merge remote-tracking branch 'upstream/main' into feat/oshi-metrics-m…
crschardt Jan 2, 2026
bfee10b
a few comment fixes
crschardt Jan 2, 2026
4374d8b
Merge remote-tracking branch 'upstream/main' into feat/oshi-metrics-m…
crschardt Jan 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ PhotonVision was forked from [Chameleon Vision](https://github.com/Chameleon-Vis

* [FasterXML](https://github.com/FasterXML) - Specifically [jackson](https://github.com/FasterXML/jackson)

* [OSHI](https://github.com/oshi/oshi)

## License

PhotonVision is licensed under the [GNU General Public License](https://www.gnu.org/licenses/gpl-3.0.html).
Expand Down
64 changes: 24 additions & 40 deletions photon-client/src/components/settings/MetricsCard.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script setup lang="ts">
import { useSettingsStore } from "@/stores/settings/GeneralSettingsStore";
import { computed, onBeforeMount, ref } from "vue";
import { useStateStore } from "@/stores/StateStore";
import { computed } from "vue";

interface MetricItem {
header: string;
Expand Down Expand Up @@ -30,17 +29,17 @@ const platformMetrics = computed<MetricItem[]>(() => {
const stats = [
{
header: "CPU Temp",
value: metrics.cpuTemp === undefined || metrics.cpuTemp == -1 ? "Unknown" : `${metrics.cpuTemp}°C`
value: metrics.cpuTemp === undefined || metrics.cpuTemp == -1 ? "Unknown" : `${metrics.cpuTemp.toFixed(1)}°C`
},
{
header: "CPU Usage",
value: metrics.cpuUtil === undefined ? "Unknown" : `${metrics.cpuUtil}%`
value: metrics.cpuUtil === undefined ? "Unknown" : `${metrics.cpuUtil.toFixed(1)}%`
},
{
header: "CPU Memory Usage",
value:
metrics.ramUtil && metrics.ramMem && metrics.ramUtil >= 0 && metrics.ramMem >= 0
? `${metrics.ramUtil}MB of ${metrics.ramMem}MB`
? `${metrics.ramUtil} of ${metrics.ramMem} MiB`
: "Unknown"
},
{
Expand All @@ -64,7 +63,14 @@ const platformMetrics = computed<MetricItem[]>(() => {
},
{
header: "Disk Usage",
value: metrics.diskUtilPct === undefined ? "Unknown" : `${metrics.diskUtilPct}%`
value: metrics.diskUtilPct === undefined ? "Unknown" : `${metrics.diskUtilPct.toFixed(1)}%`
},
{
header: "Network Traffic",
value:
metrics.sentBitRate === undefined || metrics.recvBitRate === undefined
? "Missing"
: `↑${(metrics.sentBitRate / 1e6).toFixed(3).padStart(7, "\u00A0")} Mbps | ↓${(metrics.recvBitRate / 1e6).toFixed(3).padStart(7, "\u00A0")} Mbps`
}
];

Expand All @@ -78,7 +84,7 @@ const platformMetrics = computed<MetricItem[]>(() => {
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`
value: `${metrics.gpuMemUtil} of ${metrics.gpuMem} MiB`
});
}

Expand All @@ -92,46 +98,18 @@ const platformMetrics = computed<MetricItem[]>(() => {
return stats;
});

const metricsLastFetched = ref("Never");
const fetchMetrics = () => {
useSettingsStore()
.requestMetricsUpdate()
.catch((error) => {
if (error.request) {
useStateStore().showSnackbarMessage({
color: "error",
message: "Unable to fetch metrics! The backend didn't respond."
});
} else {
useStateStore().showSnackbarMessage({
color: "error",
message: "An error occurred while trying to fetch metrics."
});
}
})
.finally(() => {
const pad = (num: number): string => {
return String(num).padStart(2, "0");
};

const date = new Date();
metricsLastFetched.value = `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
});
};

onBeforeMount(() => {
fetchMetrics();
const formattedDate = new Intl.DateTimeFormat(undefined, {
timeStyle: "medium"
});
</script>

<template>
<v-card class="mb-3 rounded-12" color="surface">
<v-card-title style="display: flex; justify-content: space-between">
<span>Metrics</span>
<v-btn variant="text" @click="fetchMetrics">
<v-icon start class="open-icon" size="large">mdi-reload</v-icon>
Last Fetched: {{ metricsLastFetched }}
</v-btn>
<span class="metrics-update-time">
Last Update: <span>{{ formattedDate.format(useSettingsStore().lastMetricsUpdate) }}</span>
</span>
</v-card-title>
<v-card-text class="pt-0 pb-3">
<v-card-subtitle class="pa-0" style="font-size: 16px">General</v-card-subtitle>
Expand Down Expand Up @@ -215,6 +193,12 @@ onBeforeMount(() => {
.metrics-table {
width: 100%;
text-align: center;
font-family: monospace !important;
}

.metrics-update-time {
font-family: monospace !important;
font-size: 16px;
}

$stats-table-border: rgba(255, 255, 255, 0.5);
Expand Down
18 changes: 12 additions & 6 deletions photon-client/src/stores/settings/GeneralSettingsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface GeneralSettingsStore {
network: NetworkSettings;
lighting: LightingSettings;
metrics: MetricData;
lastMetricsUpdate: Date;
currentFieldLayout;
}

Expand Down Expand Up @@ -62,17 +63,21 @@ export const useSettingsStore = defineStore("settings", {
gpuMem: undefined,
gpuMemUtil: undefined,
diskUtilPct: undefined,
diskUsableSpace: undefined,
npuUsage: undefined,
ipAddress: undefined,
uptime: undefined
uptime: undefined,
sentBitRate: undefined,
recvBitRate: undefined
},
currentFieldLayout: {
field: {
length: 16.4592,
width: 8.2296
},
tags: []
}
},
lastMetricsUpdate: new Date()
}),
getters: {
gpuAccelerationEnabled(): boolean {
Expand All @@ -83,10 +88,8 @@ export const useSettingsStore = defineStore("settings", {
}
},
actions: {
requestMetricsUpdate() {
return axios.post("/utils/publishMetrics");
},
updateMetricsFromWebsocket(data: Required<MetricData>) {
this.lastMetricsUpdate = new Date();
this.metrics = {
cpuTemp: data.cpuTemp || undefined,
cpuUtil: data.cpuUtil || undefined,
Expand All @@ -96,9 +99,12 @@ export const useSettingsStore = defineStore("settings", {
gpuMem: data.gpuMem || undefined,
gpuMemUtil: data.gpuMemUtil || undefined,
diskUtilPct: data.diskUtilPct || undefined,
diskUsableSpace: data.diskUsableSpace || undefined,
npuUsage: data.npuUsage || undefined,
ipAddress: data.ipAddress || undefined,
uptime: data.uptime || undefined
uptime: data.uptime || undefined,
sentBitRate: data.sentBitRate || undefined,
recvBitRate: data.recvBitRate || undefined
};
},
updateGeneralSettingsFromWebsocket(data: WebsocketSettingsUpdate) {
Expand Down
3 changes: 3 additions & 0 deletions photon-client/src/types/SettingTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ export interface MetricData {
gpuMem?: number;
gpuMemUtil?: number;
diskUtilPct?: number;
diskUsableSpace?: number;
npuUsage?: number[];
ipAddress?: string;
uptime?: number;
sentBitRate?: number;
recvBitRate?: number;
}

export enum NetworkConnectionType {
Expand Down
1 change: 1 addition & 0 deletions photon-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ dependencies {
implementation 'org.zeroturnaround:zt-zip:1.14'
implementation "org.xerial:sqlite-jdbc:3.41.0.0"
implementation 'com.diozero:diozero-core:1.4.1'
implementation 'com.github.oshi:oshi-core:6.9.1'

// The JNI libraries use wpilibNatives, the java libraries use implementation
if (jniPlatform == "linuxarm64") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,6 @@ public class HardwareConfig {
public final String setPWMFrequencyCommand;
public final String releaseGPIOCommand;

// Metrics
public final String cpuTempCommand;
public final String cpuMemoryCommand;
public final String cpuUtilCommand;
public final String cpuThrottleReasonCmd;
public final String cpuUptimeCommand;
public final String gpuMemoryCommand;
public final String ramUtilCommand;
public final String gpuMemUsageCommand;
public final String diskUsageCommand;

// Device stuff
public final String restartHardwareCommand;
public final double vendorFOV; // -1 for unmanaged
Expand All @@ -71,15 +60,6 @@ public HardwareConfig(
String setPWMCommand,
String setPWMFrequencyCommand,
String releaseGPIOCommand,
String cpuTempCommand,
String cpuMemoryCommand,
String cpuUtilCommand,
String cpuThrottleReasonCmd,
String cpuUptimeCommand,
String gpuMemoryCommand,
String ramUtilCommand,
String gpuMemUsageCommand,
String diskUsageCommand,
String restartHardwareCommand,
double vendorFOV) {
this.deviceName = deviceName;
Expand All @@ -96,15 +76,6 @@ public HardwareConfig(
this.setPWMCommand = setPWMCommand;
this.setPWMFrequencyCommand = setPWMFrequencyCommand;
this.releaseGPIOCommand = releaseGPIOCommand;
this.cpuTempCommand = cpuTempCommand;
this.cpuMemoryCommand = cpuMemoryCommand;
this.cpuUtilCommand = cpuUtilCommand;
this.cpuThrottleReasonCmd = cpuThrottleReasonCmd;
this.cpuUptimeCommand = cpuUptimeCommand;
this.gpuMemoryCommand = gpuMemoryCommand;
this.ramUtilCommand = ramUtilCommand;
this.gpuMemUsageCommand = gpuMemUsageCommand;
this.diskUsageCommand = diskUsageCommand;
this.restartHardwareCommand = restartHardwareCommand;
this.vendorFOV = vendorFOV;
}
Expand All @@ -124,15 +95,6 @@ public HardwareConfig() {
setPWMCommand = "";
setPWMFrequencyCommand = "";
releaseGPIOCommand = "";
cpuTempCommand = "";
cpuMemoryCommand = "";
cpuUtilCommand = "";
cpuThrottleReasonCmd = "";
cpuUptimeCommand = "";
gpuMemoryCommand = "";
ramUtilCommand = "";
gpuMemUsageCommand = "";
diskUsageCommand = "";
restartHardwareCommand = "";
vendorFOV = -1;
}
Expand All @@ -144,21 +106,6 @@ public final boolean hasPresetFOV() {
return vendorFOV > 0;
}

/**
* @return True if any info command has been configured to be non-empty, false otherwise
*/
public final boolean hasCommandsConfigured() {
return cpuTempCommand != ""
|| cpuMemoryCommand != ""
|| cpuUtilCommand != ""
|| cpuThrottleReasonCmd != ""
|| cpuUptimeCommand != ""
|| gpuMemoryCommand != ""
|| ramUtilCommand != ""
|| gpuMemUsageCommand != ""
|| diskUsageCommand != "";
}

/**
* @return True if any gpio command has been configured to be non-empty, false otherwise
*/
Expand Down Expand Up @@ -200,24 +147,6 @@ public String toString() {
+ setPWMFrequencyCommand
+ ", releaseGPIOCommand="
+ releaseGPIOCommand
+ ", cpuTempCommand="
+ cpuTempCommand
+ ", cpuMemoryCommand="
+ cpuMemoryCommand
+ ", cpuUtilCommand="
+ cpuUtilCommand
+ ", cpuThrottleReasonCmd="
+ cpuThrottleReasonCmd
+ ", cpuUptimeCommand="
+ cpuUptimeCommand
+ ", gpuMemoryCommand="
+ gpuMemoryCommand
+ ", ramUtilCommand="
+ ramUtilCommand
+ ", gpuMemUsageCommand="
+ gpuMemUsageCommand
+ ", diskUsageCommand="
+ diskUsageCommand
+ ", restartHardwareCommand="
+ restartHardwareCommand
+ ", vendorFOV="
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,9 @@
import org.photonvision.common.dataflow.networktables.NetworkTablesManager;
import org.photonvision.common.hardware.gpio.CustomAdapter;
import org.photonvision.common.hardware.gpio.CustomDeviceFactory;
import org.photonvision.common.hardware.metrics.MetricsManager;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import org.photonvision.common.util.ShellExec;
import org.photonvision.common.util.TimedTaskManager;

public class HardwareManager {
private static HardwareManager instance;
Expand All @@ -50,8 +48,6 @@ public class HardwareManager {
private final HardwareConfig hardwareConfig;
private final HardwareSettings hardwareSettings;

private final MetricsManager metricsManager;

@SuppressWarnings({"FieldCanBeLocal", "unused"})
private final StatusLED statusLED;

Expand All @@ -77,12 +73,6 @@ private HardwareManager(HardwareConfig hardwareConfig, HardwareSettings hardware
this.hardwareConfig = hardwareConfig;
this.hardwareSettings = hardwareSettings;

this.metricsManager = new MetricsManager();
this.metricsManager.setConfig(hardwareConfig);

TimedTaskManager.getInstance()
.addTask("Metrics Publisher", this.metricsManager::publishMetrics, 5000);

ledModeRequest =
NetworkTablesManager.getInstance()
.kRootTable
Expand Down Expand Up @@ -259,8 +249,4 @@ private void updateStatus() {
}
statusLED.setStatus(status);
}

public void publishMetrics() {
metricsManager.publishMetrics();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ public record DeviceMetrics(
double gpuMem,
double gpuMemUtil,
double diskUtilPct,
double diskUsableSpace,
double[] npuUsage,
String ipAddress,
double uptime) {
double uptime,
double sentBitRate,
double recvBitRate) {
public static final DeviceMetricsProto proto = new DeviceMetricsProto();
}
Loading
Loading