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.shuffleboard; 006 007import edu.wpi.first.networktables.NetworkTableInstance; 008 009/** 010 * The Shuffleboard class provides a mechanism with which data can be added and laid out in the 011 * Shuffleboard dashboard application from a robot program. Tabs and layouts can be specified, as 012 * well as choosing which widgets to display with and setting properties of these widgets; for 013 * example, programmers can specify a specific {@code boolean} value to be displayed with a toggle 014 * button instead of the default colored box, or set custom colors for that box. 015 * 016 * <p>For example, displaying a boolean entry with a toggle button: 017 * 018 * <pre>{@code 019 * GenericEntry myBoolean = Shuffleboard.getTab("Example Tab") 020 * .add("My Boolean", false) 021 * .withWidget("Toggle Button") 022 * .getEntry(); 023 * }</pre> 024 * 025 * <p>Changing the colors of the boolean box: 026 * 027 * <pre>{@code 028 * GenericEntry myBoolean = Shuffleboard.getTab("Example Tab") 029 * .add("My Boolean", false) 030 * .withWidget("Boolean Box") 031 * .withProperties(Map.of("colorWhenTrue", "green", "colorWhenFalse", "maroon")) 032 * .getEntry(); 033 * }</pre> 034 * 035 * <p>Specifying a parent layout. Note that the layout type must <i>always</i> be specified, even if 036 * the layout has already been generated by a previously defined entry. 037 * 038 * <pre>{@code 039 * GenericEntry myBoolean = Shuffleboard.getTab("Example Tab") 040 * .getLayout("List", "Example List") 041 * .add("My Boolean", false) 042 * .withWidget("Toggle Button") 043 * .getEntry(); 044 * }</pre> 045 * 046 * <p>Teams are encouraged to set up shuffleboard layouts at the start of the robot program. 047 */ 048public final class Shuffleboard { 049 /** The name of the base NetworkTable into which all Shuffleboard data will be added. */ 050 public static final String kBaseTableName = "/Shuffleboard"; 051 052 private static final ShuffleboardRoot root = 053 new ShuffleboardInstance(NetworkTableInstance.getDefault()); 054 private static final RecordingController recordingController = 055 new RecordingController(NetworkTableInstance.getDefault()); 056 057 private Shuffleboard() { 058 throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); 059 } 060 061 /** 062 * Updates all the values in Shuffleboard. Iterative and timed robots are pre-configured to call 063 * this method in the main robot loop; teams using custom robot base classes, or subclass 064 * SampleRobot, should make sure to call this repeatedly to keep data on the dashboard up to date. 065 */ 066 public static void update() { 067 root.update(); 068 } 069 070 /** 071 * Gets the Shuffleboard tab with the given title, creating it if it does not already exist. 072 * 073 * @param title the title of the tab 074 * @return the tab with the given title 075 */ 076 public static ShuffleboardTab getTab(String title) { 077 return root.getTab(title); 078 } 079 080 /** 081 * Selects the tab in the dashboard with the given index in the range [0..n-1], where <i>n</i> is 082 * the number of tabs in the dashboard at the time this method is called. 083 * 084 * @param index the index of the tab to select 085 */ 086 public static void selectTab(int index) { 087 root.selectTab(index); 088 } 089 090 /** 091 * Selects the tab in the dashboard with the given title. 092 * 093 * @param title the title of the tab to select 094 */ 095 public static void selectTab(String title) { 096 root.selectTab(title); 097 } 098 099 /** 100 * Enables user control of widgets containing actuators: motor controllers, relays, etc. This 101 * should only be used when the robot is in test mode. IterativeRobotBase and SampleRobot are both 102 * configured to call this method when entering test mode; most users should not need to use this 103 * method directly. 104 */ 105 public static void enableActuatorWidgets() { 106 root.enableActuatorWidgets(); 107 } 108 109 /** 110 * Disables user control of widgets containing actuators. For safety reasons, actuators should 111 * only be controlled while in test mode. IterativeRobotBase and SampleRobot are both configured 112 * to call this method when exiting in test mode; most users should not need to use this method 113 * directly. 114 */ 115 public static void disableActuatorWidgets() { 116 update(); // Need to update to make sure the sendable builders are initialized 117 root.disableActuatorWidgets(); 118 } 119 120 /** 121 * Starts data recording on the dashboard. Has no effect if recording is already in progress. 122 * 123 * @see #stopRecording() 124 */ 125 public static void startRecording() { 126 recordingController.startRecording(); 127 } 128 129 /** 130 * Stops data recording on the dashboard. Has no effect if no recording is in progress. 131 * 132 * @see #startRecording() 133 */ 134 public static void stopRecording() { 135 recordingController.stopRecording(); 136 } 137 138 /** 139 * Sets the file name format for new recording files to use. If recording is in progress when this 140 * method is called, it will continue to use the same file. New recordings will use the format. 141 * 142 * <p>To avoid recording files overwriting each other, make sure to use unique recording file 143 * names. File name formats accept templates for inserting the date and time when the recording 144 * started with the {@code ${date}} and {@code ${time}} templates, respectively. For example, the 145 * default format is {@code "recording-${time}"} and recording files created with it will have 146 * names like {@code "recording-2018.01.15.sbr"}. Users are <strong>strongly</strong> recommended 147 * to use the {@code ${time}} template to ensure unique file names. 148 * 149 * @param format the format for the 150 * @see #clearRecordingFileNameFormat() 151 */ 152 public static void setRecordingFileNameFormat(String format) { 153 recordingController.setRecordingFileNameFormat(format); 154 } 155 156 /** 157 * Clears the custom name format for recording files. New recordings will use the default format. 158 * 159 * @see #setRecordingFileNameFormat(String) 160 */ 161 public static void clearRecordingFileNameFormat() { 162 recordingController.clearRecordingFileNameFormat(); 163 } 164 165 /** 166 * Notifies Shuffleboard of an event. Events can range from as trivial as a change in a command 167 * state to as critical as a total power loss or component failure. If Shuffleboard is recording, 168 * the event will also be recorded. 169 * 170 * <p>If {@code name} is {@code null} or empty, or {@code importance} is {@code null}, then no 171 * event will be sent and an error will be printed to the driver station. 172 * 173 * @param name the name of the event 174 * @param description a description of the event 175 * @param importance the importance of the event 176 */ 177 public static void addEventMarker(String name, String description, EventImportance importance) { 178 recordingController.addEventMarker(name, description, importance); 179 } 180 181 /** 182 * Notifies Shuffleboard of an event. Events can range from as trivial as a change in a command 183 * state to as critical as a total power loss or component failure. If Shuffleboard is recording, 184 * the event will also be recorded. 185 * 186 * <p>If {@code name} is {@code null} or empty, or {@code importance} is {@code null}, then no 187 * event will be sent and an error will be printed to the driver station. 188 * 189 * @param name the name of the event 190 * @param importance the importance of the event 191 */ 192 public static void addEventMarker(String name, EventImportance importance) { 193 addEventMarker(name, "", importance); 194 } 195}