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