diff --git a/docs/DATA-FLOW.md b/docs/DATA-FLOW.md index aeb67d55..c5c4e83e 100644 --- a/docs/DATA-FLOW.md +++ b/docs/DATA-FLOW.md @@ -39,6 +39,10 @@ AdvantageKit also supports logging the state of a `Mechanism2d` object as an out WPILib includes a [units library](https://docs.wpilib.org/en/latest/docs/software/basic-programming/java-units.html) that can be used to simplify unit conversions. `Measure` objects can be logged and replayed by AdvantageKit. These values will be stored in the log as doubles using the [base unit](https://github.com/wpilibsuite/allwpilib/blob/main/wpiunits/src/main/java/edu/wpi/first/units/BaseUnits.java) for the measurement type (e.g. distances will always be logged in meters). +### Enums + +[Enum](https://www.w3schools.com/java/java_enums.asp) values can be logged and replayed by AdvantageKit. These values will be stored in the log as string values (using the [`name()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Enum.html#name--) method). + ## Deterministic Timestamps ### The Problem diff --git a/docs/RECORDING-INPUTS.md b/docs/RECORDING-INPUTS.md index eeb4f189..fd545005 100644 --- a/docs/RECORDING-INPUTS.md +++ b/docs/RECORDING-INPUTS.md @@ -52,7 +52,7 @@ public RobotContainer() { ## `AutoLog` Annotation & Data Types -By adding the `@AutoLog` annotation to your inputs class, AdvantageKit will automatically generate implementations of `toLog` and `fromLog` for your inputs. All simple data types (including single values and arrays) are supported. [Structured data types](DATA-FLOW.md#structured-data-types) are also supported, so geometry objects like `Rotation2d` and `Pose3d` can be directly used as inputs. +By adding the `@AutoLog` annotation to your inputs class, AdvantageKit will automatically generate implementations of `toLog` and `fromLog` for your inputs. All simple data types (including single values and arrays) are supported. [Structured data types](DATA-FLOW.md#structured-data-types) and enum values are also supported, so geometry objects like `Rotation2d` and `Pose3d` can be directly used as inputs. For example: @@ -61,6 +61,7 @@ For example: public class MyInputs { public double myNumber = 0.0; public Pose2d myPose = new Pose2d(); + public MyEnum myEnum = MyEnum.VALUE; } ``` @@ -71,11 +72,13 @@ class MyInputsAutoLogged extends MyInputs implements LoggableInputs { public void toLog(LogTable table) { table.put("MyNumber", myField); table.put("MyPose", myPose); + table.put("MyEnum", myEnum); } public void fromLog(LogTable table) { myNumber = table.get("MyNumber", myNumber); myPose = table.get("MyPose", myPose); + myEnum = table.get("MyEnum", myEnum); } } ``` diff --git a/docs/RECORDING-OUTPUTS.md b/docs/RECORDING-OUTPUTS.md index dededf6e..89fa92ce 100644 --- a/docs/RECORDING-OUTPUTS.md +++ b/docs/RECORDING-OUTPUTS.md @@ -12,8 +12,8 @@ The logging framework supports recording this output data on the real robot and ```java Logger.recordOutput("Flywheel/Setpoint", setpointSpeed); -Logger.recordOutput("FeederState", "IDLE"); -Logger.recordOutput("Drive/CalculatedLeftVolts", leftVolts); +Logger.recordOutput("Drive/Pose", odometryPose); +Logger.recordOutput("FeederState", FeederState.RUNNING); ``` > Note: This data is automatically saved to the `RealOutputs` or `ReplayOutputs` table, and it can be divided further into subtables using slashes (as seen above). diff --git a/junction/core/src/org/littletonrobotics/junction/AutoLogOutputManager.java b/junction/core/src/org/littletonrobotics/junction/AutoLogOutputManager.java index 6c1f17fb..25ec70e0 100644 --- a/junction/core/src/org/littletonrobotics/junction/AutoLogOutputManager.java +++ b/junction/core/src/org/littletonrobotics/junction/AutoLogOutputManager.java @@ -388,6 +388,14 @@ private static void registerField(String key, Class type, Supplier supplie if (value != null) Logger.recordOutput(key, (String) value); }); + } else if (type.isEnum()) { + callbacks.add( + () -> { + Object value = supplier.get(); + if (value != null) + // Cannot cast to enum subclass, log the name directly + Logger.recordOutput(key, ((Enum) value).name()); + }); } else if (type.equals(Measure.class)) { callbacks.add( () -> { diff --git a/junction/core/src/org/littletonrobotics/junction/LogTable.java b/junction/core/src/org/littletonrobotics/junction/LogTable.java index 096d3e1e..19868ae0 100644 --- a/junction/core/src/org/littletonrobotics/junction/LogTable.java +++ b/junction/core/src/org/littletonrobotics/junction/LogTable.java @@ -231,6 +231,14 @@ public void put(String key, String value) { put(key, new LogValue(value, null)); } + /** + * Writes a new enum value to the table. Skipped if the key already exists as + * a different type. + */ + public > void put(String key, E value) { + put(key, new LogValue(value.name(), null)); + } + /** * Writes a new Measure value to the table. Skipped if the key already exists as * a different type. @@ -532,6 +540,16 @@ public String get(String key, String defaultValue) { } } + /** Reads an enum value from the table. */ + public > E get(String key, E defaultValue) { + if (data.containsKey(prefix + key)) { + String name = get(key).getString(defaultValue.name()); + return (E) Enum.valueOf(defaultValue.getClass(), name); + } else { + return defaultValue; + } + } + /** Reads a Measure value from the table. */ public > Measure get(String key, Measure defaultValue) { if (data.containsKey(prefix + key)) { diff --git a/junction/core/src/org/littletonrobotics/junction/Logger.java b/junction/core/src/org/littletonrobotics/junction/Logger.java index 51d5c683..4a00b4c2 100644 --- a/junction/core/src/org/littletonrobotics/junction/Logger.java +++ b/junction/core/src/org/littletonrobotics/junction/Logger.java @@ -503,6 +503,20 @@ public static void recordOutput(String key, String value) { } } + /** + * Records a single output field for easy access when viewing the log. On the + * simulator, use this method to record extra data based on the original inputs. + * + * @param key The name of the field to record. It will be stored under + * "/RealOutputs" or "/ReplayOutputs" + * @param value The value of the field. + */ + public static > void recordOutput(String key, E value) { + if (running) { + outputTable.put(key, value); + } + } + /** * Records a single output field for easy access when viewing the log. On the * simulator, use this method to record extra data based on the original inputs.