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}