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