Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 16 additions & 2 deletions photon-client/src/components/settings/DeviceCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,20 @@ const generalMetrics = computed<MetricItem[]>(() => {
return stats;
});

function mapToString(map: Map<string, number>): string {
let result = "";
let comma = false;
for (const [key, value] of map.entries()) {
if (comma) {
result += ", ";
} else {
comma = true;
}
result += `${key}: ${value}`;
}
return result;
}

// @ts-expect-error This uses Intl.DurationFormat which is newly implemented and not available in TS.
const durationFormatter = new Intl.DurationFormat("en", { style: "narrow" });
const platformMetrics = computed<MetricItem[]>(() => {
Expand All @@ -228,10 +242,10 @@ const platformMetrics = computed<MetricItem[]>(() => {
}
];

if (metrics.npuUsage && metrics.npuUsage.length > 0) {
if (metrics.npuUsage && metrics.npuUsage.size > 0) {
stats.push({
header: "NPU Usage",
value: metrics.npuUsage?.map((usage, index) => `Core${index} ${usage}%`).join(", ") || "Unknown"
value: mapToString(metrics?.npuUsage) || "Unknown"
});
}

Expand Down
7 changes: 6 additions & 1 deletion photon-client/src/stores/settings/GeneralSettingsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ export const useSettingsStore = defineStore("settings", {
},
actions: {
updateMetricsFromWebsocket(data: Required<MetricData>) {
const npuUsage =
data.npuUsage instanceof Map
? new Map(data.npuUsage)
: new Map(Object.entries(data.npuUsage as Record<string, number>));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to deal with both cases here?


this.metrics = {
cpuTemp: data.cpuTemp || undefined,
cpuUtil: data.cpuUtil || undefined,
Expand All @@ -145,7 +150,7 @@ export const useSettingsStore = defineStore("settings", {
gpuMemUtil: data.gpuMemUtil || undefined,
diskUtilPct: data.diskUtilPct || undefined,
diskUsableSpace: data.diskUsableSpace || undefined,
npuUsage: data.npuUsage || undefined,
npuUsage,
ipAddress: data.ipAddress || undefined,
uptime: data.uptime || undefined,
sentBitRate: data.sentBitRate || undefined,
Expand Down
2 changes: 1 addition & 1 deletion photon-client/src/types/SettingTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export interface MetricData {
gpuMemUtil?: number;
diskUtilPct?: number;
diskUsableSpace?: number;
npuUsage?: number[];
npuUsage?: Map<string, number>;
ipAddress?: string;
uptime?: number;
sentBitRate?: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package org.photonvision.common.hardware.metrics;

import io.avaje.jsonb.Json;
import java.util.Map;
import org.photonvision.common.hardware.metrics.proto.DeviceMetricsProto;

@Json
Expand All @@ -31,7 +32,7 @@ public record DeviceMetrics(
double gpuMemUtil,
double diskUtilPct,
double diskUsableSpace,
double[] npuUsage,
Map<String, Double> npuUsage,
String ipAddress,
double uptime,
double sentBitRate,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.photonvision.common.configuration.ConfigManager;
Expand Down Expand Up @@ -83,7 +85,7 @@ private record NetworkTraffic(double sentBitRate, double recvBitRate) {}

// Set this to true to enable logging the contents of the DeviceMetrics class that is sent to NT
// and the UI.
public boolean writeMetricsToLog = false;
public boolean writeMetricsToLog = true;

private final String taskName = "SystemMonitorPublisher";
private final double minimumDeltaTime = 0.250; // seconds
Expand Down Expand Up @@ -223,7 +225,9 @@ private void logMetrics(DeviceMetrics metrics) {
sb.append(String.format("System Uptime: %.0f, ", metrics.uptime()));
sb.append(String.format("CPU Usage: %.2f%%, ", metrics.cpuUtil()));
sb.append(String.format("CPU Temperature: %.2f °C, ", metrics.cpuTemp()));
sb.append(String.format("NPU Usage: %s, ", Arrays.toString(metrics.npuUsage())));
if (!metrics.npuUsage().isEmpty()) {
sb.append(String.format("NPU Usage: %s, ", metrics.npuUsage().toString()));
}
sb.append(String.format("Used Disk: %.2f%%, ", metrics.diskUtilPct()));
sb.append(String.format("Usable Disk Space: %.0f MiB, ", metrics.diskUsableSpace() / mebi));
sb.append(String.format("Memory: %.0f / %.0f MiB, ", metrics.ramUtil(), metrics.ramMem()));
Expand Down Expand Up @@ -430,10 +434,10 @@ public synchronized double getCpuUsage() {
* Returns the npu usage, if available. Platforms with NPUs will need to override this method to
* return a useful value.
*
* @return the NPU usage or an empty array if not available.
* @return the NPU usage or an empty map if not available.
*/
public double[] getNpuUsage() {
return new double[0];
public Map<String, Double> getNpuUsage() {
return new HashMap<>();
}

/**
Expand Down Expand Up @@ -518,7 +522,7 @@ private void testSM() {
total += timeIt(sb, () -> String.format("System Uptime: %d", getUptime()));
total += timeIt(sb, () -> String.format("CPU Usage: %.2f%%", getCpuUsage()));
total += timeIt(sb, () -> String.format("CPU Temperature: %.2f °C", getCpuTemperature()));
total += timeIt(sb, () -> String.format("NPU Usage: %s", Arrays.toString(getNpuUsage())));
total += timeIt(sb, () -> String.format("NPU Usage: %s", getNpuUsage().toString()));
total += timeIt(sb, () -> String.format("Used Disk: %.2f%%", getUsedDiskPct()));
total +=
timeIt(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,36 @@

package org.photonvision.common.hardware.metrics;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class SystemMonitorQCS6490 extends SystemMonitor {
@Override
protected String getThermalZoneTypes() {
return "cpu0-thermal";
}

@Override
public Map<String, Double> getNpuUsage() {
try {
var contents = Files.readString(Path.of("/tmp/qcnpuperf_stats"));
Map<String, Double> map = new HashMap<>();
Arrays.stream(contents.split("\n"))
.filter(line -> !line.trim().isEmpty())
.forEach(
line -> {
String[] parts = line.split("=");
if (parts.length == 2) {
map.put(parts[0].trim(), Double.parseDouble(parts[1].trim()));
}
});
return map;
} catch (IOException e) {
return new HashMap<>();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -51,15 +53,19 @@ protected String getThermalZoneTypes() {
}

@Override
public double[] getNpuUsage() {
public Map<String, Double> getNpuUsage() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does a more strongly typed data structure make more sense than a Map here?

try {
var contents = Files.readString(Path.of("/sys/kernel/debug/rknpu/load"));
Matcher matcher = pattern.matcher(contents);
double[] results =
matcher.results().map(mr -> mr.group(1)).mapToDouble(Double::parseDouble).toArray();
return results;
Map<String, Double> map = new HashMap<>();
for (int i = 0; i < results.length; i++) {
map.put("core " + i, results[i]);
}
return map;
} catch (IOException e) {
return new double[0];
return new HashMap<>();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

package org.photonvision.common.hardware.metrics.proto;

import java.util.HashMap;
import java.util.Map;
import org.photonvision.common.hardware.metrics.DeviceMetrics;
import org.photonvision.proto.Photon.ProtobufDeviceMetrics;
import org.wpilib.util.protobuf.Protobuf;
Expand Down Expand Up @@ -50,7 +52,7 @@ public DeviceMetrics unpack(ProtobufDeviceMetrics msg) {
msg.getGpuMemUtil(),
msg.getDiskUtilPct(),
msg.getDiskUsableSpace(),
msg.getNpuUsage().toArray(),
convertNpuUsage(msg.getNpuUsage()),
msg.getIpAddress(),
msg.getUptime(),
msg.getSentBitRate(),
Expand All @@ -69,9 +71,23 @@ public void pack(ProtobufDeviceMetrics msg, DeviceMetrics value) {
msg.setGpuMemUtil(value.gpuMemUtil());
msg.setDiskUtilPct(value.diskUtilPct());
msg.setDiskUsableSpace(value.diskUsableSpace());
msg.addAllNpuUsage(value.npuUsage());
for (var entry : value.npuUsage().entrySet()) {
msg.addNpuUsage(
ProtobufDeviceMetrics.NpuUsageEntry.newInstance()
.setKey(entry.getKey())
.setValue(entry.getValue()));
}
msg.setIpAddress(value.ipAddress());
msg.setSentBitRate(value.sentBitRate());
msg.setRecvBitRate(value.recvBitRate());
}

private static Map<String, Double> convertNpuUsage(
us.hebi.quickbuf.RepeatedMessage<? extends ProtobufDeviceMetrics.NpuUsageEntry> entries) {
var map = new HashMap<String, Double>();
for (var entry : entries) {
map.put(entry.getKey(), entry.getValue());
}
return map;
}
}
2 changes: 1 addition & 1 deletion photon-targeting/src/main/proto/photon.proto
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ message ProtobufDeviceMetrics {
double gpu_mem = 6;
double gpu_mem_util = 7;
double disk_util_pct = 8;
repeated double npu_usage = 9;
map<string, double> npu_usage = 9;
string ip_address = 10;
double uptime = 11;
double sent_bit_rate = 12;
Expand Down
Loading