diff --git a/lib/src/main/java/com/team2813/lib2813/preferences/PersistedConfiguration.java b/lib/src/main/java/com/team2813/lib2813/preferences/PersistedConfiguration.java
index 7df3ecca..5c4ab105 100644
--- a/lib/src/main/java/com/team2813/lib2813/preferences/PersistedConfiguration.java
+++ b/lib/src/main/java/com/team2813/lib2813/preferences/PersistedConfiguration.java
@@ -20,6 +20,7 @@
import edu.wpi.first.networktables.NetworkTable;
import edu.wpi.first.networktables.NetworkTableEntry;
import edu.wpi.first.networktables.NetworkTableInstance;
+import edu.wpi.first.networktables.NetworkTableType;
import edu.wpi.first.wpilibj.DataLogManager;
import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.Preferences;
@@ -31,8 +32,8 @@
/**
* Initializes the fields of a Record Class from values stored in {@link Preferences}.
*
- *
The Preference values can be updated in the Elastic. Updated values will be stored in the
- * flash storage for the robot.
+ *
The Preference values can be updated in Elastic and other dashboards. Updated values will be
+ * stored in the flash storage for the robot.
*
*
Example use:
*
@@ -41,7 +42,7 @@
*
* public record DriveConfiguration(
* boolean addVisionMeasurements, long robotWeight,
- * DoubleSupplier powerMultiplier) {
+ * double maxSpeed, String name) {
*
* public static DriveConfiguration fromPreferences() {
* return PersistedConfiguration.fromPreferences("Drive", DriveConfiguration.class);
@@ -56,7 +57,8 @@
*
If no value is stored in Preferences for a key, the default value returned (and initialized in
@@ -67,9 +69,30 @@
*
In the above example, {@code fromPreferences()} would return a record instance that contained
+ * suppliers that, when queried, would return the current value in the "Preferences" NetworkTables
+ * table.
+ *
*
The caller could specify different default values by passing an instance of the record class:
*
*
{@code "Drive/maxAngularVelocity"} (default value: {@code 3.14})
*
*
+ * Note that {@code PersistedConfiguration} will use the default record constructor to create
+ * record instances, so any parameter validation should be done in a custom constructor; see Custom Constructor in Java
+ * Records for details.
+ *
*
For record classes with many component values of the same type, it is strongly recommended
* that a builder is provided to construct the record, to avoid callers passing the parameters in
* the wrong order. To make generation of a builder easier, consider using {@code @AutoBuilder}
* from Google Auto or {@code @Builder}
- * from Project Lombok. Note that {@code PersistedConfiguration} will use the default record
- * constructor to create record instances, so any parameter validation should be done in a custom
- * constructor; see Custom
- * Constructor in Java Records for details.
+ * from Project Lombok.
*
* @since 2.0.0
*/
@@ -164,7 +189,7 @@ public static T fromPreferences(String preferenceName, T conf
*
*
* The default values for the preferences will be Java defaults (for example, zero for
- * integers).
+ * integers, an empty string for strings, etc.).
*
* @param preferenceName Preference subtable to use to get the values.
* @param recordClass Type of the record instance to populate from preferences.
@@ -421,10 +446,10 @@ private static int intFactory(
if (defaultValue == null) {
defaultValue = 0;
}
- Preferences.initInt(key, defaultValue);
+ initIntegerPreference(key, defaultValue);
return defaultValue;
}
- return Preferences.getInt(key, 0);
+ return getIntegerPreference(key);
}
/** Gets a IntSupplier value from Preferences for the given component. */
@@ -432,9 +457,9 @@ private static IntSupplier intSupplierFactory(
RecordComponent component, String key, IntSupplier defaultValueSupplier, boolean initialize) {
if (initialize) {
int defaultValue = defaultValueSupplier != null ? defaultValueSupplier.getAsInt() : 0;
- Preferences.initInt(key, defaultValue);
+ initIntegerPreference(key, defaultValue);
}
- return () -> Preferences.getInt(key, 0);
+ return () -> getIntegerPreference(key);
}
/** Gets a long value from Preferences for the given component. */
@@ -550,6 +575,42 @@ private static void deleteLegacyKeys() {
}
}
+ /**
+ * Puts the given int into the preferences table if it doesn't already exist.
+ *
+ *
Unlike with {@link Preferences#initInt(String, int)}, the value is stored as an integer, not
+ * a double.
+ *
+ * @param key The key
+ * @param value The value
+ */
+ private static void initIntegerPreference(String key, int value) {
+ NetworkTable table = Preferences.getNetworkTable();
+ NetworkTableEntry entry = table.getEntry(key);
+ if (NetworkTableType.kDouble.equals(entry.getType())) {
+ // If we get here that should mean there is a value for this key, so the default value should
+ // be ignored. We update the default value just in case, if nothing else than to be
+ // consistent with the Preferences "init" methods.
+ entry.setDefaultDouble(value);
+ } else {
+ entry.setDefaultInteger(value);
+ }
+ entry.setPersistent();
+ }
+
+ /**
+ * Returns the value at the given key, as an int. If this table does not have a value for that
+ * position, then a value of zero will be returned.
+ *
+ * @param key the key
+ * @return either the value in the table, or zero
+ */
+ private static int getIntegerPreference(String key) {
+ NetworkTable table = Preferences.getNetworkTable();
+ NetworkTableEntry entry = table.getEntry(key);
+ return (int) entry.getInteger(0);
+ }
+
/**
* Emits a warning, usually via {@link DataLogManager}.
*
diff --git a/lib/src/test/java/com/team2813/lib2813/preferences/PersistedConfigurationTest.java b/lib/src/test/java/com/team2813/lib2813/preferences/PersistedConfigurationTest.java
index f54c32a8..b1ddebf2 100644
--- a/lib/src/test/java/com/team2813/lib2813/preferences/PersistedConfigurationTest.java
+++ b/lib/src/test/java/com/team2813/lib2813/preferences/PersistedConfigurationTest.java
@@ -79,6 +79,35 @@ protected enum ValuesKind {
UPDATED_VALUES
}
+ private NetworkTableEntry getTableEntry(String key, NetworkTableType expectedType) {
+ NetworkTableEntry entry = isolatedPreferences.getPreferencesTable().getEntry(key);
+ assertThat(entry.getType()).isEqualTo(expectedType);
+ return entry;
+ }
+
+ protected final boolean getBooleanValue(String key, boolean defaultValue) {
+ return getTableEntry(key, NetworkTableType.kBoolean).getBoolean(defaultValue);
+ }
+
+ protected final long getIntegerValue(String key) {
+ return getTableEntry(key, NetworkTableType.kInteger).getInteger(-1);
+ }
+
+ protected final double getDoubleValue(String key) {
+ return getTableEntry(key, NetworkTableType.kDouble).getDouble(-1);
+ }
+
+ protected final String getStringValue(String key) {
+ return getTableEntry(key, NetworkTableType.kString).getString("defaultValue");
+ }
+
+ protected final void setIntegerValue(String key, int value) {
+ NetworkTable table = isolatedPreferences.getPreferencesTable();
+ NetworkTableEntry entry = table.getEntry(key);
+ entry.setInteger(value);
+ entry.setPersistent();
+ }
+
protected final void assertHasNoChangesSince(Map previousValues) {
var preferenceValues = preferenceValues();
assertWithMessage("Unexpected no changes to preference values")
@@ -359,7 +388,7 @@ protected void assertPreferencesHaveConfiguredDefaults() {
assertThat(preferenceKeys())
.containsExactly(BOOLEAN_VALUE_KEY, BOOLEAN_SUPPLIER_KEY, SUPPLIER_BOOLEAN_KEY);
for (String key : ALL_KEYS) {
- assertThat(Preferences.getBoolean(key, !defaultValue)).isEqualTo(defaultValue);
+ assertThat(getBooleanValue(key, !defaultValue)).isEqualTo(defaultValue);
}
}
@@ -375,7 +404,7 @@ protected void assertPreferencesHaveJavaDefaults() {
assertThat(preferenceKeys())
.containsExactly(BOOLEAN_VALUE_KEY, BOOLEAN_SUPPLIER_KEY, SUPPLIER_BOOLEAN_KEY);
for (String key : ALL_KEYS) {
- assertThat(Preferences.getBoolean(key, true)).isFalse();
+ assertThat(getBooleanValue(key, true)).isFalse();
}
}
@@ -410,15 +439,23 @@ protected void assertSuppliersHaveUpdatedValues(RecordWithBooleans record) {
}
}
+ @RunWith(Parameterized.class)
public static class IntPreferencesTest
extends PreferencesRegistryTestCase {
static final String PREFERENCE_NAME = "Integers";
static final String INT_VALUE_KEY = "Integers/intValue";
static final String INT_SUPPLIER_KEY = "Integers/intSupplier";
static final String SUPPLIER_INT_KEY = "Integers/supplierInt";
+ final boolean storeAsDoubles;
+
+ @Parameters(name = "storeAsDoubles={0}")
+ public static Object[] data() {
+ return new Object[] {true, false};
+ }
- public IntPreferencesTest() {
+ public IntPreferencesTest(boolean storeAsDoubles) {
super(PREFERENCE_NAME, RecordWithInts.class);
+ this.storeAsDoubles = storeAsDoubles;
}
/** Test record for testing classes that contain int fields. */
@@ -447,9 +484,9 @@ protected void assertHasConfiguredDefaults(RecordWithInts record) {
protected void assertPreferencesHaveConfiguredDefaults() {
assertThat(preferenceKeys())
.containsExactly(INT_VALUE_KEY, INT_SUPPLIER_KEY, SUPPLIER_INT_KEY);
- assertThat(Preferences.getInt(INT_VALUE_KEY, -1)).isEqualTo(1);
- assertThat(Preferences.getInt(INT_SUPPLIER_KEY, -1)).isEqualTo(2);
- assertThat(Preferences.getInt(SUPPLIER_INT_KEY, -1)).isEqualTo(3);
+ assertThat(getIntegerValue(INT_VALUE_KEY)).isEqualTo(1);
+ assertThat(getIntegerValue(INT_SUPPLIER_KEY)).isEqualTo(2);
+ assertThat(getIntegerValue(SUPPLIER_INT_KEY)).isEqualTo(3);
}
@Override
@@ -463,23 +500,38 @@ protected void assertHasJavaDefaults(RecordWithInts record) {
protected void assertPreferencesHaveJavaDefaults() {
assertThat(preferenceKeys())
.containsExactly(INT_VALUE_KEY, INT_SUPPLIER_KEY, SUPPLIER_INT_KEY);
- assertThat(Preferences.getInt(INT_VALUE_KEY, -1)).isEqualTo(0);
- assertThat(Preferences.getInt(INT_SUPPLIER_KEY, -1)).isEqualTo(0);
- assertThat(Preferences.getInt(SUPPLIER_INT_KEY, -1)).isEqualTo(0);
+ assertThat(getIntegerValue(INT_VALUE_KEY)).isEqualTo(0);
+ assertThat(getIntegerValue(INT_SUPPLIER_KEY)).isEqualTo(0);
+ assertThat(getIntegerValue(SUPPLIER_INT_KEY)).isEqualTo(0);
}
@Override
protected void updatePreferenceValues(ValuesKind kind) {
- switch (kind) {
- case INITIAL_VALUES -> {
- Preferences.setInt(INT_VALUE_KEY, 101);
- Preferences.setInt(INT_SUPPLIER_KEY, 102);
- Preferences.setInt(SUPPLIER_INT_KEY, 103);
+ if (storeAsDoubles) {
+ switch (kind) {
+ case INITIAL_VALUES -> {
+ Preferences.setDouble(INT_VALUE_KEY, 101);
+ Preferences.setDouble(INT_SUPPLIER_KEY, 102);
+ Preferences.setDouble(SUPPLIER_INT_KEY, 103);
+ }
+ case UPDATED_VALUES -> {
+ Preferences.setDouble(INT_VALUE_KEY, 201);
+ Preferences.setDouble(INT_SUPPLIER_KEY, 202);
+ Preferences.setDouble(SUPPLIER_INT_KEY, 203);
+ }
}
- case UPDATED_VALUES -> {
- Preferences.setInt(INT_VALUE_KEY, 201);
- Preferences.setInt(INT_SUPPLIER_KEY, 202);
- Preferences.setInt(SUPPLIER_INT_KEY, 203);
+ } else {
+ switch (kind) {
+ case INITIAL_VALUES -> {
+ setIntegerValue(INT_VALUE_KEY, 101);
+ setIntegerValue(INT_SUPPLIER_KEY, 102);
+ setIntegerValue(SUPPLIER_INT_KEY, 103);
+ }
+ case UPDATED_VALUES -> {
+ setIntegerValue(INT_VALUE_KEY, 201);
+ setIntegerValue(INT_SUPPLIER_KEY, 202);
+ setIntegerValue(SUPPLIER_INT_KEY, 203);
+ }
}
}
}
@@ -544,9 +596,9 @@ protected void assertHasConfiguredDefaults(RecordWithLongs record) {
protected void assertPreferencesHaveConfiguredDefaults() {
assertThat(preferenceKeys())
.containsExactly(LONG_VALUE_KEY, LONG_SUPPLIER_KEY, SUPPLIER_LONG_KEY);
- assertThat(Preferences.getLong(LONG_VALUE_KEY, -1)).isEqualTo(1);
- assertThat(Preferences.getLong(LONG_SUPPLIER_KEY, -1)).isEqualTo(2);
- assertThat(Preferences.getLong(SUPPLIER_LONG_KEY, -1)).isEqualTo(3);
+ assertThat(getIntegerValue(LONG_VALUE_KEY)).isEqualTo(1);
+ assertThat(getIntegerValue(LONG_SUPPLIER_KEY)).isEqualTo(2);
+ assertThat(getIntegerValue(SUPPLIER_LONG_KEY)).isEqualTo(3);
}
@Override
@@ -560,9 +612,9 @@ protected void assertHasJavaDefaults(RecordWithLongs record) {
protected void assertPreferencesHaveJavaDefaults() {
assertThat(preferenceKeys())
.containsExactly(LONG_VALUE_KEY, LONG_SUPPLIER_KEY, SUPPLIER_LONG_KEY);
- assertThat(Preferences.getLong(LONG_VALUE_KEY, -1)).isEqualTo(0);
- assertThat(Preferences.getLong(LONG_SUPPLIER_KEY, -1)).isEqualTo(0);
- assertThat(Preferences.getLong(SUPPLIER_LONG_KEY, -1)).isEqualTo(0);
+ assertThat(getIntegerValue(LONG_VALUE_KEY)).isEqualTo(0);
+ assertThat(getIntegerValue(LONG_SUPPLIER_KEY)).isEqualTo(0);
+ assertThat(getIntegerValue(SUPPLIER_LONG_KEY)).isEqualTo(0);
}
@Override
@@ -642,9 +694,9 @@ protected void assertHasConfiguredDefaults(RecordWithDoubles record) {
protected void assertPreferencesHaveConfiguredDefaults() {
assertThat(preferenceKeys())
.containsExactly(DOUBLE_VALUE_KEY, DOUBLE_SUPPLIER_KEY, SUPPLIER_DOUBLE_KEY);
- assertThat(Preferences.getDouble(DOUBLE_VALUE_KEY, -1)).isWithin(EPSILON).of(3.14159);
- assertThat(Preferences.getDouble(DOUBLE_SUPPLIER_KEY, -1)).isWithin(EPSILON).of(2.71828);
- assertThat(Preferences.getDouble(SUPPLIER_DOUBLE_KEY, -1)).isWithin(EPSILON).of(6.28318);
+ assertThat(getDoubleValue(DOUBLE_VALUE_KEY)).isWithin(EPSILON).of(3.14159);
+ assertThat(getDoubleValue(DOUBLE_SUPPLIER_KEY)).isWithin(EPSILON).of(2.71828);
+ assertThat(getDoubleValue(SUPPLIER_DOUBLE_KEY)).isWithin(EPSILON).of(6.28318);
}
@Override
@@ -658,9 +710,9 @@ protected void assertHasJavaDefaults(RecordWithDoubles record) {
protected void assertPreferencesHaveJavaDefaults() {
assertThat(preferenceKeys())
.containsExactly(DOUBLE_VALUE_KEY, DOUBLE_SUPPLIER_KEY, SUPPLIER_DOUBLE_KEY);
- assertThat(Preferences.getDouble(DOUBLE_VALUE_KEY, -1)).isWithin(EPSILON).of(0);
- assertThat(Preferences.getDouble(DOUBLE_SUPPLIER_KEY, -1)).isWithin(EPSILON).of(0);
- assertThat(Preferences.getDouble(SUPPLIER_DOUBLE_KEY, -1)).isWithin(EPSILON).of(0);
+ assertThat(getDoubleValue(DOUBLE_VALUE_KEY)).isWithin(EPSILON).of(0);
+ assertThat(getDoubleValue(DOUBLE_SUPPLIER_KEY)).isWithin(EPSILON).of(0);
+ assertThat(getDoubleValue(SUPPLIER_DOUBLE_KEY)).isWithin(EPSILON).of(0);
}
@Override
@@ -733,8 +785,8 @@ protected void assertHasConfiguredDefaults(RecordWithStrings record) {
@Override
protected void assertPreferencesHaveConfiguredDefaults() {
assertThat(preferenceKeys()).containsExactly(STRING_VALUE_KEY, SUPPLIER_STRING_KEY);
- assertThat(Preferences.getString(STRING_VALUE_KEY, "")).isEqualTo("chicken");
- assertThat(Preferences.getString(SUPPLIER_STRING_KEY, "")).isEqualTo("bus");
+ assertThat(getStringValue(STRING_VALUE_KEY)).isEqualTo("chicken");
+ assertThat(getStringValue(SUPPLIER_STRING_KEY)).isEqualTo("bus");
}
@Override
@@ -746,8 +798,8 @@ protected void assertHasJavaDefaults(RecordWithStrings record) {
@Override
protected void assertPreferencesHaveJavaDefaults() {
assertThat(preferenceKeys()).containsExactly(STRING_VALUE_KEY, SUPPLIER_STRING_KEY);
- assertThat(Preferences.getString(STRING_VALUE_KEY, "default")).isEmpty();
- assertThat(Preferences.getString(SUPPLIER_STRING_KEY, "default")).isEmpty();
+ assertThat(getStringValue(STRING_VALUE_KEY)).isEmpty();
+ assertThat(getStringValue(SUPPLIER_STRING_KEY)).isEmpty();
}
@Override
@@ -814,8 +866,8 @@ protected void assertHasConfiguredDefaults(RecordWithRecords record) {
@Override
protected void assertPreferencesHaveConfiguredDefaults() {
assertThat(preferenceKeys()).containsExactly(stringValueKey, longValueKey);
- assertThat(Preferences.getString(stringValueKey, "")).isEqualTo("The Answer");
- assertThat(Preferences.getLong(longValueKey, -1)).isEqualTo(42);
+ assertThat(getStringValue(stringValueKey)).isEqualTo("The Answer");
+ assertThat(getIntegerValue(longValueKey)).isEqualTo(42);
}
@Override
@@ -827,8 +879,8 @@ protected void assertHasJavaDefaults(RecordWithRecords record) {
@Override
protected void assertPreferencesHaveJavaDefaults() {
assertThat(preferenceKeys()).containsExactly(stringValueKey, longValueKey);
- assertThat(Preferences.getString(stringValueKey, "default")).isEmpty();
- assertThat(Preferences.getLong(longValueKey, -1)).isEqualTo(0);
+ assertThat(getStringValue(stringValueKey)).isEmpty();
+ assertThat(getIntegerValue(longValueKey)).isEqualTo(0);
}
@Override