001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package org.wpilib.util; 006 007import static org.wpilib.util.ErrorMessages.requireNonNullParam; 008 009import java.util.Collection; 010import java.util.EnumSet; 011import org.wpilib.hardware.hal.HAL; 012import org.wpilib.networktables.MultiSubscriber; 013import org.wpilib.networktables.NetworkTable; 014import org.wpilib.networktables.NetworkTableEntry; 015import org.wpilib.networktables.NetworkTableEvent; 016import org.wpilib.networktables.NetworkTableInstance; 017import org.wpilib.networktables.NetworkTableListener; 018import org.wpilib.networktables.StringPublisher; 019import org.wpilib.networktables.StringTopic; 020import org.wpilib.networktables.Topic; 021 022/** 023 * The preferences class provides a relatively simple way to save important values to the roboRIO to 024 * access the next time the roboRIO is booted. 025 * 026 * <p>This class loads and saves from a file inside the roboRIO. The user can not access the file 027 * directly, but may modify values at specific fields which will then be automatically saved to the 028 * file by the NetworkTables server. 029 * 030 * <p>This class is thread safe. 031 * 032 * <p>This will also interact with {@link NetworkTable} by creating a table called "Preferences" 033 * with all the key-value pairs. 034 */ 035public final class Preferences { 036 /** The Preferences table name. */ 037 private static final String kTableName = "Preferences"; 038 039 private static final String kSmartDashboardType = "RobotPreferences"; 040 041 /** The network table. */ 042 private static NetworkTable m_table; 043 044 private static StringPublisher m_typePublisher; 045 private static MultiSubscriber m_tableSubscriber; 046 private static NetworkTableListener m_listener; 047 048 /** Creates a preference class. */ 049 private Preferences() {} 050 051 static { 052 setNetworkTableInstance(NetworkTableInstance.getDefault()); 053 HAL.reportUsage("Preferences", ""); 054 } 055 056 /** 057 * Set the NetworkTable instance used for entries. For testing purposes; use with caution. 058 * 059 * @param inst NetworkTable instance 060 */ 061 public static synchronized void setNetworkTableInstance(NetworkTableInstance inst) { 062 m_table = inst.getTable(kTableName); 063 if (m_typePublisher != null) { 064 m_typePublisher.close(); 065 } 066 m_typePublisher = 067 m_table 068 .getStringTopic(".type") 069 .publishEx( 070 StringTopic.kTypeString, "{\"SmartDashboard\":\"" + kSmartDashboardType + "\"}"); 071 m_typePublisher.set(kSmartDashboardType); 072 073 // Subscribe to all Preferences; this ensures we get the latest values 074 // ahead of a getter call. 075 if (m_tableSubscriber != null) { 076 m_tableSubscriber.close(); 077 } 078 m_tableSubscriber = new MultiSubscriber(inst, new String[] {m_table.getPath() + "/"}); 079 080 // Listener to set all Preferences values to persistent 081 // (for backwards compatibility with old dashboards). 082 if (m_listener != null) { 083 m_listener.close(); 084 } 085 086 Topic typePublisherTopic = m_typePublisher.getTopic(); 087 m_listener = 088 NetworkTableListener.createListener( 089 m_tableSubscriber, 090 EnumSet.of(NetworkTableEvent.Kind.kImmediate, NetworkTableEvent.Kind.kPublish), 091 event -> { 092 if (event.topicInfo != null) { 093 Topic topic = event.topicInfo.getTopic(); 094 if (!topic.equals(typePublisherTopic)) { 095 topic.setPersistent(true); 096 } 097 } 098 }); 099 } 100 101 /** 102 * Gets the network table used for preferences entries. 103 * 104 * @return the network table used for preferences entries 105 */ 106 public static NetworkTable getNetworkTable() { 107 return m_table; 108 } 109 110 /** 111 * Gets the preferences keys. 112 * 113 * @return a collection of the keys 114 */ 115 public static Collection<String> getKeys() { 116 return m_table.getKeys(); 117 } 118 119 /** 120 * Puts the given string into the preferences table. 121 * 122 * @param key the key 123 * @param value the value 124 * @throws NullPointerException if value is null 125 */ 126 public static void setString(String key, String value) { 127 requireNonNullParam(value, "value", "setString"); 128 129 NetworkTableEntry entry = m_table.getEntry(key); 130 entry.setString(value); 131 entry.setPersistent(); 132 } 133 134 /** 135 * Puts the given string into the preferences table if it doesn't already exist. 136 * 137 * @param key The key 138 * @param value The value 139 */ 140 public static void initString(String key, String value) { 141 NetworkTableEntry entry = m_table.getEntry(key); 142 entry.setDefaultString(value); 143 entry.setPersistent(); 144 } 145 146 /** 147 * Puts the given int into the preferences table. 148 * 149 * @param key the key 150 * @param value the value 151 */ 152 public static void setInt(String key, int value) { 153 NetworkTableEntry entry = m_table.getEntry(key); 154 entry.setDouble(value); 155 entry.setPersistent(); 156 } 157 158 /** 159 * Puts the given int into the preferences table if it doesn't already exist. 160 * 161 * @param key The key 162 * @param value The value 163 */ 164 public static void initInt(String key, int value) { 165 NetworkTableEntry entry = m_table.getEntry(key); 166 entry.setDefaultDouble(value); 167 entry.setPersistent(); 168 } 169 170 /** 171 * Puts the given double into the preferences table. 172 * 173 * @param key the key 174 * @param value the value 175 */ 176 public static void setDouble(String key, double value) { 177 NetworkTableEntry entry = m_table.getEntry(key); 178 entry.setDouble(value); 179 entry.setPersistent(); 180 } 181 182 /** 183 * Puts the given double into the preferences table if it doesn't already exist. 184 * 185 * @param key The key 186 * @param value The value 187 */ 188 public static void initDouble(String key, double value) { 189 NetworkTableEntry entry = m_table.getEntry(key); 190 entry.setDefaultDouble(value); 191 entry.setPersistent(); 192 } 193 194 /** 195 * Puts the given float into the preferences table. 196 * 197 * @param key the key 198 * @param value the value 199 */ 200 public static void setFloat(String key, float value) { 201 NetworkTableEntry entry = m_table.getEntry(key); 202 entry.setDouble(value); 203 entry.setPersistent(); 204 } 205 206 /** 207 * Puts the given float into the preferences table if it doesn't already exist. 208 * 209 * @param key The key 210 * @param value The value 211 */ 212 public static void initFloat(String key, float value) { 213 NetworkTableEntry entry = m_table.getEntry(key); 214 entry.setDefaultDouble(value); 215 entry.setPersistent(); 216 } 217 218 /** 219 * Puts the given boolean into the preferences table. 220 * 221 * @param key the key 222 * @param value the value 223 */ 224 public static void setBoolean(String key, boolean value) { 225 NetworkTableEntry entry = m_table.getEntry(key); 226 entry.setBoolean(value); 227 entry.setPersistent(); 228 } 229 230 /** 231 * Puts the given boolean into the preferences table if it doesn't already exist. 232 * 233 * @param key The key 234 * @param value The value 235 */ 236 public static void initBoolean(String key, boolean value) { 237 NetworkTableEntry entry = m_table.getEntry(key); 238 entry.setDefaultBoolean(value); 239 entry.setPersistent(); 240 } 241 242 /** 243 * Puts the given long into the preferences table. 244 * 245 * @param key the key 246 * @param value the value 247 */ 248 public static void setLong(String key, long value) { 249 NetworkTableEntry entry = m_table.getEntry(key); 250 entry.setInteger(value); 251 entry.setPersistent(); 252 } 253 254 /** 255 * Puts the given long into the preferences table if it doesn't already exist. 256 * 257 * @param key The key 258 * @param value The value 259 */ 260 public static void initLong(String key, long value) { 261 NetworkTableEntry entry = m_table.getEntry(key); 262 entry.setDefaultInteger(value); 263 entry.setPersistent(); 264 } 265 266 /** 267 * Returns whether there is a key with the given name. 268 * 269 * @param key the key 270 * @return if there is a value at the given key 271 */ 272 public static boolean containsKey(String key) { 273 return m_table.containsKey(key); 274 } 275 276 /** 277 * Remove a preference. 278 * 279 * @param key the key 280 */ 281 public static void remove(String key) { 282 NetworkTableEntry entry = m_table.getEntry(key); 283 entry.clearPersistent(); 284 entry.unpublish(); 285 } 286 287 /** Remove all preferences. */ 288 public static void removeAll() { 289 for (String key : m_table.getKeys()) { 290 if (!".type".equals(key)) { 291 remove(key); 292 } 293 } 294 } 295 296 /** 297 * Returns the string at the given key. If this table does not have a value for that position, 298 * then the given backup value will be returned. 299 * 300 * @param key the key 301 * @param backup the value to return if none exists in the table 302 * @return either the value in the table, or the backup 303 */ 304 public static String getString(String key, String backup) { 305 return m_table.getEntry(key).getString(backup); 306 } 307 308 /** 309 * Returns the int at the given key. If this table does not have a value for that position, then 310 * the given backup value will be returned. 311 * 312 * @param key the key 313 * @param backup the value to return if none exists in the table 314 * @return either the value in the table, or the backup 315 */ 316 public static int getInt(String key, int backup) { 317 return (int) m_table.getEntry(key).getDouble(backup); 318 } 319 320 /** 321 * Returns the double at the given key. If this table does not have a value for that position, 322 * then the given backup value will be returned. 323 * 324 * @param key the key 325 * @param backup the value to return if none exists in the table 326 * @return either the value in the table, or the backup 327 */ 328 public static double getDouble(String key, double backup) { 329 return m_table.getEntry(key).getDouble(backup); 330 } 331 332 /** 333 * Returns the boolean at the given key. If this table does not have a value for that position, 334 * then the given backup value will be returned. 335 * 336 * @param key the key 337 * @param backup the value to return if none exists in the table 338 * @return either the value in the table, or the backup 339 */ 340 public static boolean getBoolean(String key, boolean backup) { 341 return m_table.getEntry(key).getBoolean(backup); 342 } 343 344 /** 345 * Returns the float at the given key. If this table does not have a value for that position, then 346 * the given backup value will be returned. 347 * 348 * @param key the key 349 * @param backup the value to return if none exists in the table 350 * @return either the value in the table, or the backup 351 */ 352 public static float getFloat(String key, float backup) { 353 return (float) m_table.getEntry(key).getDouble(backup); 354 } 355 356 /** 357 * Returns the long at the given key. If this table does not have a value for that position, then 358 * the given backup value will be returned. 359 * 360 * @param key the key 361 * @param backup the value to return if none exists in the table 362 * @return either the value in the table, or the backup 363 */ 364 public static long getLong(String key, long backup) { 365 return m_table.getEntry(key).getInteger(backup); 366 } 367}