Skip to content

Commit

Permalink
Merge pull request #432 from Pi4J/feature/throttled-state-support
Browse files Browse the repository at this point in the history
Add Support for Throttled State Parsing and Retrieval
  • Loading branch information
FDelporte authored Dec 30, 2024
2 parents 1aeec7a + 910569d commit 5c731ab
Show file tree
Hide file tree
Showing 6 changed files with 332 additions and 16 deletions.
77 changes: 68 additions & 9 deletions pi4j-core/src/main/java/com/pi4j/boardinfo/model/BoardReading.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,24 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

/**
* Represents the readings from a Raspberry Pi board including information
* about its code, version, temperature, uptime, voltage, and memory usage.
* Provides utility methods to parse and convert these readings.
* Represents the readings and status information from a Raspberry Pi board.
* This includes the board's unique code, version, temperature, uptime, voltage, memory usage, and throttled state.
* Provides utility methods to parse and convert these readings into more useful formats (e.g., Celsius, Fahrenheit, integer values for throttling state).
* <p>
* The throttled state reflects whether the board is under certain limitations like low voltage or throttling due to high temperature or CPU usage.
* This class is intended to capture the board's operational data to help monitor its health and performance.
* <p>
* Fields:
* - {@link #boardCode}: The unique code identifying the board model.
* - {@link #boardVersionCode}: The version code for the specific model of the board.
* - {@link #temperature}: The current temperature of the board (as a string).
* - {@link #uptimeInfo}: Information about how long the board has been running.
* - {@link #volt}: The current voltage reading (as a string).
* - {@link #memory}: Information about the memory usage of the board.
* - {@link #throttledState}: The throttling state of the board, indicating if the board is under-voltage, throttled, or experiencing frequency capping (as a string).
*/
public class BoardReading {

Expand All @@ -43,25 +57,29 @@ public class BoardReading {
private final String uptimeInfo;
private final String volt;
private final String memory;
private final String throttledState;

/**
* Constructor to initialize a {@link BoardReading} object.
*
* @param boardCode the unique code for the board.
* @param boardCode the unique code for the board.
* @param boardVersionCode the version code of the board.
* @param temperature the temperature reading of the board (in string format).
* @param uptimeInfo the uptime information for the board.
* @param volt the voltage reading of the board (in string format).
* @param memory the memory usage information for the board.
* @param temperature the temperature reading of the board (in string format).
* @param uptimeInfo the uptime information for the board.
* @param volt the voltage reading of the board (in string format).
* @param memory the memory usage information for the board.
* @param throttledState the throttled state of the board, indicating under-voltage, throttling,
* or frequency capping conditions (in string format).
*/
public BoardReading(String boardCode, String boardVersionCode, String temperature, String uptimeInfo,
String volt, String memory) {
String volt, String memory, String throttledState) {
this.boardCode = boardCode;
this.boardVersionCode = boardVersionCode;
this.temperature = temperature;
this.uptimeInfo = uptimeInfo;
this.volt = volt;
this.memory = memory;
this.throttledState = throttledState;
}

/**
Expand Down Expand Up @@ -138,6 +156,47 @@ public double getTemperatureInCelsius() {
return 0;
}

/**
* Converts the throttled state to an integer value.
* The expected input format is "throttled=0x<value>".
*
* @return the throttled state as an integer, or 0 if the conversion fails.
*/
public int getThrottledStateAsInt() {
try {
if (throttledState.startsWith("throttled=0x")) {
return Integer.parseInt(throttledState.substring(12), 16);
} else {
logger.warn("Unexpected throttled state format: {}", throttledState);
}
} catch (Exception e) {
logger.error("Can't convert throttled state value: {}. {}", throttledState, e.getMessage());
}
return 0;
}

/**
* Gets the list of active throttled states as decoded from the raw throttled state integer.
* This method calls {@link ThrottledState#decode(int)} to convert the raw throttled state value
* into a list of active {@link ThrottledState} enum values.
*
* @return a list of {@link ThrottledState} enums representing the active throttled states.
*/
public List<ThrottledState> getThrottledStates() {
return ThrottledState.decode(getThrottledStateAsInt());
}

/**
* Gets a human-readable description of the active throttled states.
* This method calls {@link ThrottledState#getActiveStatesDescription(int)} to convert the raw throttled
* state value into a string describing the active throttled states.
*
* @return a string containing the description of the active throttled states.
*/
public String getThrottledStatesDescription() {
return ThrottledState.getActiveStatesDescription(getThrottledStateAsInt());
}

/**
* Converts the temperature reading to Fahrenheit.
* This method uses the Celsius temperature and applies the conversion formula:
Expand Down
111 changes: 111 additions & 0 deletions pi4j-core/src/main/java/com/pi4j/boardinfo/model/ThrottledState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.pi4j.boardinfo.model;

/*-
* #%L
* **********************************************************************
* ORGANIZATION : Pi4J
* PROJECT : Pi4J :: LIBRARY :: Java Library (CORE)
* FILENAME : ThrottledState.java
*
* This file is part of the Pi4J project. More information about
* this project can be found here: https://pi4j.com/
* **********************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

import java.util.ArrayList;
import java.util.List;

/**
* Enum representing the different throttling and under-voltage states of the Raspberry Pi board.
* Each enum value represents a specific condition that might be detected on the board, such as
* undervoltage, frequency capping, throttling, or temperature limits.
*/
public enum ThrottledState {
UNDERVOLTAGE_DETECTED(0x1, "Undervoltage detected"),
ARM_FREQUENCY_CAPPED(0x2, "ARM frequency capped"),
CURRENTLY_THROTTLED(0x4, "Currently throttled"),
SOFT_TEMPERATURE_LIMIT_ACTIVE(0x8, "Soft temperature limit active"),
UNDERVOLTAGE_HAS_OCCURED(0x10000, "Undervoltage has occurred"),
ARM_FREQUENCY_CAPPING_HAS_OCCURED(0x20000, "ARM frequency capping has occurred"),
THROTTLING_HAS_OCCURED(0x40000, "Throttling has occurred"),
SOFT_TEMPERATURE_LIMIT_HAS_OCCURED(0x80000, "Soft temperature limit has occurred");

private final int value;
private final String description;

/**
* Constructor for the ThrottledState enum.
*
* @param value The integer value representing the state.
* @param description A human-readable description of the state.
*/
ThrottledState(int value, String description) {
this.value = value;
this.description = description;
}

/**
* Returns the integer value representing this throttled state.
*
* @return the integer value associated with the state.
*/
public int getValue() {
return value;
}

/**
* Returns a human-readable description of this throttled state.
*
* @return the description of the throttled state.
*/
public String getDescription() {
return description;
}

/**
* Decodes a raw throttled state (as an integer) and returns a list of active {@link ThrottledState} enums.
*
* @param rawState the raw throttled state as an integer (e.g., 0x50005).
* @return a list of active throttled states.
*/
public static List<ThrottledState> decode(int rawState) {
List<ThrottledState> activeStates = new ArrayList<>();
for (ThrottledState state : ThrottledState.values()) {
if ((rawState & state.getValue()) != 0) {
activeStates.add(state);
}
}
return activeStates;
}

/**
* Returns a human-readable description of the active throttled states based on the raw throttled state value.
*
* @param rawState the raw throttled state as an integer (e.g., 0x50005).
* @return a description of the active throttled states.
*/
public static String getActiveStatesDescription(int rawState) {
List<ThrottledState> activeStates = decode(rawState);
StringBuilder description = new StringBuilder();
for (ThrottledState state : activeStates) {
if (description.length() > 0) {
description.append(", ");
}
description.append(state.getDescription());
}
return description.length() > 0 ? description.toString() : "No active throttled states";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

import static com.pi4j.boardinfo.util.Command.CORE_VOLTAGE_COMMAND;
import static com.pi4j.boardinfo.util.Command.TEMPERATURE_COMMAND;
import static com.pi4j.boardinfo.util.Command.THROTTLED_STATE_COMMAND;
import static com.pi4j.boardinfo.util.Command.UPTIME_COMMAND;
import static com.pi4j.boardinfo.util.SystemProperties.ARCHITECTURE_DATA_MODEL;
import static com.pi4j.boardinfo.util.SystemProperties.JAVA_RUNTIME_VERSION;
Expand Down Expand Up @@ -242,7 +243,8 @@ public static BoardReading getBoardReading() {
getTemperature(),
getUptime(),
getCoreVoltage(),
getMemTotal()
getMemTotal(),
getThrottledState()
);
}

Expand Down Expand Up @@ -281,5 +283,18 @@ private static String getUptime() {
private static String getTemperature() {
return execute(TEMPERATURE_COMMAND).getOutputMessage();
}

/**
* Retrieves the throttled state of the system.
* <p>
* This command uses the `vcgencmd get_throttled` utility to check for under-voltage,
* throttling, or frequency capping conditions on the board.
* </p>
*
* @return a string representing the throttled state of the system
*/
public static String getThrottledState() {
return execute(THROTTLED_STATE_COMMAND).getOutputMessage();
}
}

15 changes: 15 additions & 0 deletions pi4j-core/src/main/java/com/pi4j/boardinfo/util/Command.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,19 @@ public interface Command {
* </p>
*/
String TEMPERATURE_COMMAND = "vcgencmd measure_temp";

/**
* Command to retrieve throttled state information.
* <p>
* This command uses the `vcgencmd` tool to query the throttled state of the system. The output provides
* details about under-voltage, throttling, and frequency capping conditions. It is useful for diagnosing
* power and thermal management issues.
* </p>
* <p>
* For more details on the bit interpretation of the output, see the
* <a href="https://www.raspberrypi.com/documentation/computers/os.html#get_throttled">
* Raspberry Pi documentation</a>.
* </p>
*/
String THROTTLED_STATE_COMMAND = "vcgencmd get_throttled";
}
25 changes: 19 additions & 6 deletions pi4j-core/src/test/java/com/pi4j/boardinfo/model/ModelTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import org.junit.jupiter.api.Test;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;

Expand All @@ -23,20 +25,31 @@ void testStringOutputFromJavaInfo() {
void testBoardReadingParsing() {
var boardReading = new BoardReading(
"Raspberry Pi 4 Model B Rev 1.1",
"c03111",
"c03111",
"temp=42.8'C",
"08:06:15 up 85 days, 9:43, 0 users, load average: 0.00, 0.00, 0.00",
"volt=0.8563V",
"MemTotal: 3885396 kB"
);
"volt=0.8563V",
"MemTotal: 3885396 kB",
"throttled=0x3"
);

assertAll(
() -> assertEquals(42.8, boardReading.getTemperatureInCelsius(), "Temperature in Celsius"),
() -> assertEquals(109.03999999999999, boardReading.getTemperatureInFahrenheit(), "Temperature in Fahrenheit"),
() -> assertEquals(0.8563, boardReading.getVoltValue(), "Volt")
() -> assertEquals(109.04, boardReading.getTemperatureInFahrenheit(), 0.01, "Temperature in Fahrenheit"),
() -> assertEquals(0.8563, boardReading.getVoltValue(), "Volt"),
() -> assertEquals(3, boardReading.getThrottledStateAsInt(), "Throttled state as integer"), // Hex 0x3 to int
() -> assertEquals(List.of(ThrottledState.UNDERVOLTAGE_DETECTED, ThrottledState.ARM_FREQUENCY_CAPPED),
boardReading.getThrottledStates(),
"Throttled states as list of active ThrottledState enums"),
() -> assertEquals(
"Undervoltage detected, ARM frequency capped",
boardReading.getThrottledStatesDescription(),
"Throttled states description for active states"
)
);
}


@Test
void testMemoryParsing() {
var memory = new JvmMemory(Runtime.getRuntime());
Expand Down
Loading

0 comments on commit 5c731ab

Please sign in to comment.