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