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