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