Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/AdvantageKit.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Copy link

@srimanachanta srimanachanta Apr 18, 2023

Choose a reason for hiding this comment

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

ditto

Empty file.
221 changes: 221 additions & 0 deletions junction/core/src/org/littletonrobotics/junction/LoadMonitor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
package org.littletonrobotics.junction;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

// Based on work done by Teams 254 & 1736 in the casserole RioLoadMonitor

public class LoadMonitor {
/**
* Rate of update of the load variables in milliseconds. 1s should be enough?
*/
public static final int UPDATE_RATE_MS = 500;
static final String CPU_LOAD_VIRTUAL_FILE = "/proc/stat";
static final String MEM_LOAD_VIRTUAL_FILE = "/proc/meminfo";
private static final String OS = System.getProperty("os.name").toLowerCase();
public static LoadMonitor instance;
/**
* Overall (all-cpu) load percentage (non-idle time)
*/
public double totalCPULoadPct = 0;
/**
* Memory used percentage including cached memory
*/
public double totalMemUsedPct = 0;
/**
* JVM memory used percentage, may have discrete jumps when garbage collection occurs or jvm dedicated
* memory is resized.
*/
public double totalJVMMemUsedPct = 0;
double prevUserTime = 0;
double prevNicedTime = 0;
double prevSystemTime = 0;
double prevIdleTime = 0;

// Will prevent burning processor cycles if data is unreachable
boolean giveUp = false;
/**
* Constructor. Initializes measurement system and starts a two hertz
* background thread to gather load info
*/
public LoadMonitor() {
if(!OS.contains("nix") && !OS.contains("nux") && !OS.contains("aix")){
System.out.println("Advantage Kit LoadMonitor is only supported for the RoboRIO & other linux based " +
"operating systems. LoadMonitor will not be active.");
return;
}

//Reset give up flag
giveUp = false;

// Kick off monitor in new thread.
// Thanks to Team 254 for an example of how to do this!
Thread monitorThread = new Thread(() -> {
try {
while (!Thread.currentThread().isInterrupted()) {
periodicUpdate();
Thread.sleep(UPDATE_RATE_MS);
}
} catch (Exception e) {
e.printStackTrace();
}

});
//Set up thread properties and start it off
monitorThread.setName("Load Monitor Thread");
monitorThread.setPriority(Thread.MIN_PRIORITY);
monitorThread.start();
}

public static LoadMonitor getInstance() {
if (instance == null) {
instance = new LoadMonitor();
}
return instance;
}

/**
* Updates the loads based on info from the /proc virtual
* filesystem. Should be called in the background. will be called
* internally by the thread started in the constructor
*/
private void periodicUpdate() {
String CPUTotalLoadRawLine = "";
File file;

if (!giveUp) {
// CPU load parsing
// Get meaningful line from CPU load virtual file
file = new File(CPU_LOAD_VIRTUAL_FILE);

try {
BufferedReader br = new BufferedReader(new FileReader(file));
CPUTotalLoadRawLine = br.readLine();
while (CPUTotalLoadRawLine != null) {
if (CPUTotalLoadRawLine.startsWith("cpu ")) {
break;
}
CPUTotalLoadRawLine = br.readLine();
}
br.close();
} catch (IOException e) {
System.out.println("WARNING: cannot get raw CPU load data. Giving up future attempts to read.");
e.printStackTrace();
giveUp = true;
}

assert CPUTotalLoadRawLine != null;
String[] tokens = CPUTotalLoadRawLine.split(" ");

double curUserTime = 0;
double curNicedTime = 0;
double curSystemTime = 0;
double curIdleTime = 0;
try {
curUserTime = Double.parseDouble(tokens[2]); //Start at 2, because RIO parses an extra empty token at 1
curNicedTime = Double.parseDouble(tokens[3]);
curSystemTime = Double.parseDouble(tokens[4]);
curIdleTime = Double.parseDouble(tokens[5]);
} catch (Exception e) {
System.out.println("WARNING: cannot parse CPU load. Giving up future attempts to read.");
e.printStackTrace();
giveUp = true;
}

// Calculate change in time counters since last measurement
double deltaUserTime = curUserTime - prevUserTime;
double deltaNicedTime = curNicedTime - prevNicedTime;
double deltaSystemTime = curSystemTime - prevSystemTime;
double deltaIdleTime = curIdleTime - prevIdleTime;

prevUserTime = curUserTime;
prevNicedTime = curNicedTime;
prevSystemTime = curSystemTime;
prevIdleTime = curIdleTime;

// Add up totals
double totalInUseTime = (deltaUserTime + deltaNicedTime + deltaSystemTime);
double totalTime = totalInUseTime + deltaIdleTime;

// Calculate CPU load to nearest tenth of percent
totalCPULoadPct = ((double) Math.round(totalInUseTime / totalTime * 1000.0)) / 10.0;

// Memory parsing and calculation
String memTotalStr = "";
String memFreeStr = "";
String line;

// Get meaningful line from CPU load virtual file
file = new File(MEM_LOAD_VIRTUAL_FILE);
try {
BufferedReader br = new BufferedReader(new FileReader(file));
line = br.readLine();
while (line != null) {
if (line.startsWith("MemTotal: ")) {
memTotalStr = line;
} else if (line.startsWith("MemFree:")) {
memFreeStr = line;
break;
}
line = br.readLine();
}
br.close();
} catch (IOException e) {
System.out.println("WARNING: cannot get raw memory load data. Giving up future attempts to read.");
e.printStackTrace();
giveUp = true;
}

String[] memTotalTokens = memTotalStr.split("\\s+");
String[] memFreeTokens = memFreeStr.split("\\s+");

// Parse values from proper tokens
double curTotalMem = 0;
double curFreeMem = 0;
try {
curTotalMem = Double.parseDouble(memTotalTokens[1]);
curFreeMem = Double.parseDouble(memFreeTokens[1]);
} catch (Exception e) {
System.out.println("WARNING: cannot parse memory load. Giving up future attempts to read.");
e.printStackTrace();
giveUp = true;
}

totalMemUsedPct = ((double) Math.round((1.0 - curFreeMem / curTotalMem) * 1000.0)) / 10.0;
}
// Grab JVM memory (outside give-up loop)
double jvmTotalMem = Runtime.getRuntime().totalMemory();
double jvmFreeMem = Runtime.getRuntime().freeMemory();
totalJVMMemUsedPct = (jvmTotalMem - jvmFreeMem) / (jvmTotalMem) * 100.0;
}

/**
* Getter for load percentage on CPU. Aggregate of all cores on the system, including
* both system and user processes.
*
* @return percentage of non-idle time, or 0 if percentage unavailable
*/
public double getCPUUtilization() {
return totalCPULoadPct;
}

/**
* Getter for the load percentage on memory. Total memory utilization includes cached memory.
*
* @return percentage of available system RAM, or 0 if percentage unavailable.
*/
public double getSystemMemoryUtilization() {
return totalMemUsedPct;
}

/**
* Getter for the load percentage on memory in the Java Virtual Machine.
*
* @return percentage of available JVM RAM, or 0 if percentage unavailable.
*/
public double getJVMMemoryUtilization() {
return totalJVMMemUsedPct;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.littletonrobotics.junction.inputs;

import org.littletonrobotics.conduit.ConduitApi;
import org.littletonrobotics.junction.LoadMonitor;
import org.littletonrobotics.junction.LogTable;
import org.littletonrobotics.junction.Logger;

Expand Down Expand Up @@ -45,6 +46,9 @@ public static class SystemStatsInputs implements LoggableInputs {
public boolean systemActive;
public CANStatus canStatus = new CANStatus();
public long epochTime;
public double cpuUtilizationPercent;
public double memoryUtilizationPercent;
public double jvmMemoryUtilizationPercent;

@Override
public void toLog(LogTable table) {
Expand Down Expand Up @@ -75,6 +79,10 @@ public void toLog(LogTable table) {
table.put("CANBus/ReceiveErrorCount", canStatus.receiveErrorCount);
table.put("CANBus/TransmitErrorCount", canStatus.transmitErrorCount);
table.put("EpochTimeMicros", epochTime);

table.put("LoadMonitor/CPUUsagePercent", cpuUtilizationPercent);
table.put("LoadMonitor/SystemMemoryUsagePercent", memoryUtilizationPercent);
table.put("LoadMonitor/JVMMemoryUsagePercent", jvmMemoryUtilizationPercent);
}

@Override
Expand Down Expand Up @@ -107,6 +115,10 @@ public void fromLog(LogTable table) {
(int) table.getInteger("CANBus/ReceiveErrorCount", canStatus.receiveErrorCount),
(int) table.getInteger("CANBus/TransmitErrorCount", canStatus.transmitErrorCount));
epochTime = table.getInteger("EpochTimeMicros", epochTime);

cpuUtilizationPercent = table.getDouble("LoadMonitor/CPUUsagePercent", cpuUtilizationPercent);
memoryUtilizationPercent = table.getDouble("LoadMonitor/SystemMemoryUsagePercent", memoryUtilizationPercent);
jvmMemoryUtilizationPercent = table.getDouble("LoadMonitor/JVMMemoryUsagePercent", jvmMemoryUtilizationPercent);
}
}

Expand Down Expand Up @@ -142,6 +154,10 @@ public void periodic() {
(int) conduit.getReceiveErrorCount(),
(int) conduit.getTransmitErrorCount());
sysInputs.epochTime = conduit.getEpochTime();

sysInputs.cpuUtilizationPercent = LoadMonitor.getInstance().getCPUUtilization();
sysInputs.memoryUtilizationPercent = LoadMonitor.getInstance().getSystemMemoryUtilization();
sysInputs.jvmMemoryUtilizationPercent = LoadMonitor.getInstance().getJVMMemoryUtilization();
}

logger.processInputs("SystemStats", sysInputs);
Expand Down