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.smartdashboard; 006 007import edu.wpi.first.hal.HAL; 008import edu.wpi.first.networktables.NetworkTable; 009import edu.wpi.first.networktables.NetworkTableEntry; 010import edu.wpi.first.networktables.NetworkTableInstance; 011import edu.wpi.first.util.sendable.Sendable; 012import edu.wpi.first.util.sendable.SendableRegistry; 013import java.util.HashMap; 014import java.util.Map; 015import java.util.Set; 016 017/** 018 * The {@link SmartDashboard} class is the bridge between robot programs and the SmartDashboard on 019 * the laptop. 020 * 021 * <p>When a value is put into the SmartDashboard here, it pops up on the SmartDashboard on the 022 * laptop. Users can put values into and get values from the SmartDashboard. 023 */ 024public final class SmartDashboard { 025 /** The {@link NetworkTable} used by {@link SmartDashboard}. */ 026 private static NetworkTable table; 027 028 /** 029 * A table linking tables in the SmartDashboard to the {@link Sendable} objects they came from. 030 */ 031 @SuppressWarnings("PMD.UseConcurrentHashMap") 032 private static final Map<String, Sendable> tablesToData = new HashMap<>(); 033 034 /** The executor for listener tasks; calls listener tasks synchronously from main thread. */ 035 private static final ListenerExecutor listenerExecutor = new ListenerExecutor(); 036 037 private static boolean m_reported = false; // NOPMD redundant field initializer 038 039 static { 040 setNetworkTableInstance(NetworkTableInstance.getDefault()); 041 } 042 043 private SmartDashboard() { 044 throw new UnsupportedOperationException("This is a utility class!"); 045 } 046 047 /** 048 * Set the NetworkTable instance used for entries. For testing purposes; use with caution. 049 * 050 * @param inst NetworkTable instance 051 */ 052 public static synchronized void setNetworkTableInstance(NetworkTableInstance inst) { 053 SmartDashboard.table = inst.getTable("SmartDashboard"); 054 tablesToData.clear(); 055 } 056 057 /** 058 * Maps the specified key to the specified value in this table. The key can not be null. The value 059 * can be retrieved by calling the get method with a key that is equal to the original key. 060 * 061 * @param key the key 062 * @param data the value 063 * @throws IllegalArgumentException If key is null 064 */ 065 @SuppressWarnings("PMD.CompareObjectsWithEquals") 066 public static synchronized void putData(String key, Sendable data) { 067 if (!m_reported) { 068 HAL.reportUsage("SmartDashboard", ""); 069 m_reported = true; 070 } 071 Sendable sddata = tablesToData.get(key); 072 if (sddata == null || sddata != data) { 073 tablesToData.put(key, data); 074 NetworkTable dataTable = table.getSubTable(key); 075 SendableBuilderImpl builder = new SendableBuilderImpl(); 076 builder.setTable(dataTable); 077 SendableRegistry.publish(data, builder); 078 builder.startListeners(); 079 dataTable.getEntry(".name").setString(key); 080 } 081 } 082 083 /** 084 * Maps the specified key (where the key is the name of the {@link Sendable}) to the specified 085 * value in this table. The value can be retrieved by calling the get method with a key that is 086 * equal to the original key. 087 * 088 * @param value the value 089 * @throws IllegalArgumentException If key is null 090 */ 091 public static void putData(Sendable value) { 092 String name = SendableRegistry.getName(value); 093 if (!name.isEmpty()) { 094 putData(name, value); 095 } 096 } 097 098 /** 099 * Returns the value at the specified key. 100 * 101 * @param key the key 102 * @return the value 103 * @throws IllegalArgumentException if the key is null 104 */ 105 public static synchronized Sendable getData(String key) { 106 Sendable data = tablesToData.get(key); 107 if (data == null) { 108 throw new IllegalArgumentException("SmartDashboard data does not exist: " + key); 109 } else { 110 return data; 111 } 112 } 113 114 /** 115 * Gets the entry for the specified key. 116 * 117 * @param key the key name 118 * @return Network table entry. 119 */ 120 public static NetworkTableEntry getEntry(String key) { 121 if (!m_reported) { 122 HAL.reportUsage("SmartDashboard", ""); 123 m_reported = true; 124 } 125 return table.getEntry(key); 126 } 127 128 /** 129 * Checks the table and tells if it contains the specified key. 130 * 131 * @param key the key to search for 132 * @return true if the table as a value assigned to the given key 133 */ 134 public static boolean containsKey(String key) { 135 return table.containsKey(key); 136 } 137 138 /** 139 * Get the keys stored in the SmartDashboard table of NetworkTables. 140 * 141 * @param types bitmask of types; 0 is treated as a "don't care". 142 * @return keys currently in the table 143 */ 144 public static Set<String> getKeys(int types) { 145 return table.getKeys(types); 146 } 147 148 /** 149 * Get the keys stored in the SmartDashboard table of NetworkTables. 150 * 151 * @return keys currently in the table. 152 */ 153 public static Set<String> getKeys() { 154 return table.getKeys(); 155 } 156 157 /** 158 * Makes a key's value persistent through program restarts. The key cannot be null. 159 * 160 * @param key the key name 161 */ 162 public static void setPersistent(String key) { 163 getEntry(key).setPersistent(); 164 } 165 166 /** 167 * Stop making a key's value persistent through program restarts. The key cannot be null. 168 * 169 * @param key the key name 170 */ 171 public static void clearPersistent(String key) { 172 getEntry(key).clearPersistent(); 173 } 174 175 /** 176 * Returns whether the value is persistent through program restarts. The key cannot be null. 177 * 178 * @param key the key name 179 * @return True if the value is persistent. 180 */ 181 public static boolean isPersistent(String key) { 182 return getEntry(key).isPersistent(); 183 } 184 185 /** 186 * Put a boolean in the table. 187 * 188 * @param key the key to be assigned to 189 * @param value the value that will be assigned 190 * @return False if the table key already exists with a different type 191 */ 192 public static boolean putBoolean(String key, boolean value) { 193 return getEntry(key).setBoolean(value); 194 } 195 196 /** 197 * Set the value in the table if key does not exist. 198 * 199 * @param key the key 200 * @param defaultValue the value to set if key does not exist 201 * @return True if the key did not already exist, otherwise False 202 */ 203 public static boolean setDefaultBoolean(String key, boolean defaultValue) { 204 return getEntry(key).setDefaultBoolean(defaultValue); 205 } 206 207 /** 208 * Returns the boolean the key maps to. If the key does not exist or is of different type, it will 209 * return the default value. 210 * 211 * @param key the key to look up 212 * @param defaultValue the value to be returned if no value is found 213 * @return the value associated with the given key or the given default value if there is no value 214 * associated with the key 215 */ 216 public static boolean getBoolean(String key, boolean defaultValue) { 217 return getEntry(key).getBoolean(defaultValue); 218 } 219 220 /** 221 * Put a number in the table. 222 * 223 * @param key the key to be assigned to 224 * @param value the value that will be assigned 225 * @return False if the table key already exists with a different type 226 */ 227 public static boolean putNumber(String key, double value) { 228 return getEntry(key).setDouble(value); 229 } 230 231 /** 232 * Set the value in the table if key does not exist. 233 * 234 * @param key the key 235 * @param defaultValue the value to set if key does not exist 236 * @return True if the key did not already exist, otherwise False 237 */ 238 public static boolean setDefaultNumber(String key, double defaultValue) { 239 return getEntry(key).setDefaultDouble(defaultValue); 240 } 241 242 /** 243 * Returns the number the key maps to. If the key does not exist or is of different type, it will 244 * return the default value. 245 * 246 * @param key the key to look up 247 * @param defaultValue the value to be returned if no value is found 248 * @return the value associated with the given key or the given default value if there is no value 249 * associated with the key 250 */ 251 public static double getNumber(String key, double defaultValue) { 252 return getEntry(key).getDouble(defaultValue); 253 } 254 255 /** 256 * Put a string in the table. 257 * 258 * @param key the key to be assigned to 259 * @param value the value that will be assigned 260 * @return False if the table key already exists with a different type 261 */ 262 public static boolean putString(String key, String value) { 263 return getEntry(key).setString(value); 264 } 265 266 /** 267 * Set the value in the table if key does not exist. 268 * 269 * @param key the key 270 * @param defaultValue the value to set if key does not exist 271 * @return True if the key did not already exist, otherwise False 272 */ 273 public static boolean setDefaultString(String key, String defaultValue) { 274 return getEntry(key).setDefaultString(defaultValue); 275 } 276 277 /** 278 * Returns the string the key maps to. If the key does not exist or is of different type, it will 279 * return the default value. 280 * 281 * @param key the key to look up 282 * @param defaultValue the value to be returned if no value is found 283 * @return the value associated with the given key or the given default value if there is no value 284 * associated with the key 285 */ 286 public static String getString(String key, String defaultValue) { 287 return getEntry(key).getString(defaultValue); 288 } 289 290 /** 291 * Put a boolean array in the table. 292 * 293 * @param key the key to be assigned to 294 * @param value the value that will be assigned 295 * @return False if the table key already exists with a different type 296 */ 297 public static boolean putBooleanArray(String key, boolean[] value) { 298 return getEntry(key).setBooleanArray(value); 299 } 300 301 /** 302 * Put a boolean array in the table. 303 * 304 * @param key the key to be assigned to 305 * @param value the value that will be assigned 306 * @return False if the table key already exists with a different type 307 */ 308 public static boolean putBooleanArray(String key, Boolean[] value) { 309 return getEntry(key).setBooleanArray(value); 310 } 311 312 /** 313 * Set the value in the table if key does not exist. 314 * 315 * @param key the key 316 * @param defaultValue the value to set if key does not exist 317 * @return True if the key did not already exist, otherwise False 318 */ 319 public static boolean setDefaultBooleanArray(String key, boolean[] defaultValue) { 320 return getEntry(key).setDefaultBooleanArray(defaultValue); 321 } 322 323 /** 324 * Set the value in the table if key does not exist. 325 * 326 * @param key the key 327 * @param defaultValue the value to set if key does not exist 328 * @return True if the key did not already exist, otherwise False 329 */ 330 public static boolean setDefaultBooleanArray(String key, Boolean[] defaultValue) { 331 return getEntry(key).setDefaultBooleanArray(defaultValue); 332 } 333 334 /** 335 * Returns the boolean array the key maps to. If the key does not exist or is of different type, 336 * it will return the default value. 337 * 338 * @param key the key to look up 339 * @param defaultValue the value to be returned if no value is found 340 * @return the value associated with the given key or the given default value if there is no value 341 * associated with the key 342 */ 343 public static boolean[] getBooleanArray(String key, boolean[] defaultValue) { 344 return getEntry(key).getBooleanArray(defaultValue); 345 } 346 347 /** 348 * Returns the boolean array the key maps to. If the key does not exist or is of different type, 349 * it will return the default value. 350 * 351 * @param key the key to look up 352 * @param defaultValue the value to be returned if no value is found 353 * @return the value associated with the given key or the given default value if there is no value 354 * associated with the key 355 */ 356 public static Boolean[] getBooleanArray(String key, Boolean[] defaultValue) { 357 return getEntry(key).getBooleanArray(defaultValue); 358 } 359 360 /** 361 * Put a number array in the table. 362 * 363 * @param key the key to be assigned to 364 * @param value the value that will be assigned 365 * @return False if the table key already exists with a different type 366 */ 367 public static boolean putNumberArray(String key, double[] value) { 368 return getEntry(key).setDoubleArray(value); 369 } 370 371 /** 372 * Put a number array in the table. 373 * 374 * @param key the key to be assigned to 375 * @param value the value that will be assigned 376 * @return False if the table key already exists with a different type 377 */ 378 public static boolean putNumberArray(String key, Double[] value) { 379 return getEntry(key).setNumberArray(value); 380 } 381 382 /** 383 * Set the value in the table if key does not exist. 384 * 385 * @param key the key 386 * @param defaultValue the value to set if key does not exist 387 * @return True if the key did not already exist, otherwise False 388 */ 389 public static boolean setDefaultNumberArray(String key, double[] defaultValue) { 390 return getEntry(key).setDefaultDoubleArray(defaultValue); 391 } 392 393 /** 394 * Set the value in the table if key does not exist. 395 * 396 * @param key the key 397 * @param defaultValue the value to set if key does not exist 398 * @return True if the key did not already exist, otherwise False 399 */ 400 public static boolean setDefaultNumberArray(String key, Double[] defaultValue) { 401 return getEntry(key).setDefaultNumberArray(defaultValue); 402 } 403 404 /** 405 * Returns the number array the key maps to. If the key does not exist or is of different type, it 406 * will return the default value. 407 * 408 * @param key the key to look up 409 * @param defaultValue the value to be returned if no value is found 410 * @return the value associated with the given key or the given default value if there is no value 411 * associated with the key 412 */ 413 public static double[] getNumberArray(String key, double[] defaultValue) { 414 return getEntry(key).getDoubleArray(defaultValue); 415 } 416 417 /** 418 * Returns the number array the key maps to. If the key does not exist or is of different type, it 419 * will return the default value. 420 * 421 * @param key the key to look up 422 * @param defaultValue the value to be returned if no value is found 423 * @return the value associated with the given key or the given default value if there is no value 424 * associated with the key 425 */ 426 public static Double[] getNumberArray(String key, Double[] defaultValue) { 427 return getEntry(key).getDoubleArray(defaultValue); 428 } 429 430 /** 431 * Put a string array in the table. 432 * 433 * @param key the key to be assigned to 434 * @param value the value that will be assigned 435 * @return False if the table key already exists with a different type 436 */ 437 public static boolean putStringArray(String key, String[] value) { 438 return getEntry(key).setStringArray(value); 439 } 440 441 /** 442 * Set the value in the table if key does not exist. 443 * 444 * @param key the key 445 * @param defaultValue the value to set if key does not exist 446 * @return True if the key did not already exist, otherwise False 447 */ 448 public static boolean setDefaultStringArray(String key, String[] defaultValue) { 449 return getEntry(key).setDefaultStringArray(defaultValue); 450 } 451 452 /** 453 * Returns the string array the key maps to. If the key does not exist or is of different type, it 454 * will return the default value. 455 * 456 * @param key the key to look up 457 * @param defaultValue the value to be returned if no value is found 458 * @return the value associated with the given key or the given default value if there is no value 459 * associated with the key 460 */ 461 public static String[] getStringArray(String key, String[] defaultValue) { 462 return getEntry(key).getStringArray(defaultValue); 463 } 464 465 /** 466 * Put a raw value (byte array) in the table. 467 * 468 * @param key the key to be assigned to 469 * @param value the value that will be assigned 470 * @return False if the table key already exists with a different type 471 */ 472 public static boolean putRaw(String key, byte[] value) { 473 return getEntry(key).setRaw(value); 474 } 475 476 /** 477 * Set the value in the table if key does not exist. 478 * 479 * @param key the key 480 * @param defaultValue the value to set if key does not exist 481 * @return True if the key did not already exist, otherwise False 482 */ 483 public static boolean setDefaultRaw(String key, byte[] defaultValue) { 484 return getEntry(key).setDefaultRaw(defaultValue); 485 } 486 487 /** 488 * Returns the raw value (byte array) the key maps to. If the key does not exist or is of 489 * different type, it will return the default value. 490 * 491 * @param key the key to look up 492 * @param defaultValue the value to be returned if no value is found 493 * @return the value associated with the given key or the given default value if there is no value 494 * associated with the key 495 */ 496 public static byte[] getRaw(String key, byte[] defaultValue) { 497 return getEntry(key).getRaw(defaultValue); 498 } 499 500 /** 501 * Posts a task from a listener to the ListenerExecutor, so that it can be run synchronously from 502 * the main loop on the next call to {@link SmartDashboard#updateValues()}. 503 * 504 * @param task The task to run synchronously from the main thread. 505 */ 506 public static void postListenerTask(Runnable task) { 507 listenerExecutor.execute(task); 508 } 509 510 /** Puts all sendable data to the dashboard. */ 511 public static synchronized void updateValues() { 512 // Execute posted listener tasks 513 listenerExecutor.runListenerTasks(); 514 for (Sendable data : tablesToData.values()) { 515 SendableRegistry.update(data); 516 } 517 } 518}