mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-22 01:11:42 +00:00
[wpilib] Remove Shuffleboard API (#7730)
This commit is contained in:
@@ -10,7 +10,6 @@ import edu.wpi.first.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.hal.HAL;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
|
||||
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
|
||||
import java.util.ConcurrentModificationException;
|
||||
|
||||
@@ -331,7 +330,6 @@ public abstract class IterativeRobotBase extends RobotBase {
|
||||
case kTest -> {
|
||||
if (m_lwEnabledInTest) {
|
||||
LiveWindow.setEnabled(false);
|
||||
Shuffleboard.disableActuatorWidgets();
|
||||
}
|
||||
testExit();
|
||||
}
|
||||
@@ -357,7 +355,6 @@ public abstract class IterativeRobotBase extends RobotBase {
|
||||
case kTest -> {
|
||||
if (m_lwEnabledInTest) {
|
||||
LiveWindow.setEnabled(true);
|
||||
Shuffleboard.enableActuatorWidgets();
|
||||
}
|
||||
testInit();
|
||||
m_watchdog.addEpoch("testInit()");
|
||||
@@ -404,8 +401,6 @@ public abstract class IterativeRobotBase extends RobotBase {
|
||||
m_watchdog.addEpoch("SmartDashboard.updateValues()");
|
||||
LiveWindow.updateValues();
|
||||
m_watchdog.addEpoch("LiveWindow.updateValues()");
|
||||
Shuffleboard.update();
|
||||
m_watchdog.addEpoch("Shuffleboard.update()");
|
||||
|
||||
if (isSimulation()) {
|
||||
HAL.simPeriodicBefore();
|
||||
|
||||
@@ -18,7 +18,6 @@ import edu.wpi.first.networktables.NetworkTableEvent;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.util.WPIUtilJNI;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
|
||||
import edu.wpi.first.wpilibj.util.WPILibVersion;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -227,7 +226,6 @@ public abstract class RobotBase implements AutoCloseable {
|
||||
});
|
||||
|
||||
LiveWindow.setEnabled(false);
|
||||
Shuffleboard.disableActuatorWidgets();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
/**
|
||||
* The types of layouts bundled with Shuffleboard.
|
||||
*
|
||||
* <pre>{@code
|
||||
* ShuffleboardLayout myList = Shuffleboard.getTab("My Tab")
|
||||
* .getLayout(BuiltinLayouts.kList, "My List");
|
||||
* }</pre>
|
||||
*/
|
||||
public enum BuiltInLayouts implements LayoutType {
|
||||
/**
|
||||
* Groups components in a vertical list. New widgets added to the layout will be placed at the
|
||||
* bottom of the list. <br>
|
||||
* Custom properties:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
|
||||
* <tr><td>Label position</td><td>String</td><td>"BOTTOM"</td>
|
||||
* <td>The position of component labels inside the grid. One of
|
||||
* {@code ["TOP", "LEFT", "BOTTOM", "RIGHT", "HIDDEN"}</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
kList("List Layout"),
|
||||
|
||||
/**
|
||||
* Groups components in an <i>n</i> x <i>m</i> grid. Grid layouts default to 3x3. <br>
|
||||
* Custom properties:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
|
||||
* <tr><td>Number of columns</td><td>Number</td><td>3</td><td>Must be in the range [1,15]</td>
|
||||
* </tr>
|
||||
* <tr><td>Number of rows</td><td>Number</td><td>3</td><td>Must be in the range [1,15]</td></tr>
|
||||
* <tr>
|
||||
* <td>Label position</td>
|
||||
* <td>String</td>
|
||||
* <td>"BOTTOM"</td>
|
||||
* <td>The position of component labels inside the grid.
|
||||
* One of {@code ["TOP", "LEFT", "BOTTOM", "RIGHT", "HIDDEN"}</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*/
|
||||
kGrid("Grid Layout"),
|
||||
;
|
||||
|
||||
private final String m_layoutName;
|
||||
|
||||
BuiltInLayouts(String layoutName) {
|
||||
m_layoutName = layoutName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLayoutName() {
|
||||
return m_layoutName;
|
||||
}
|
||||
}
|
||||
@@ -1,476 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
/**
|
||||
* The types of the widgets bundled with Shuffleboard.
|
||||
*
|
||||
* <p>For example, setting a number to be displayed with a slider:
|
||||
*
|
||||
* <pre>{@code
|
||||
* GenericEntry example = Shuffleboard.getTab("My Tab")
|
||||
* .add("My Number", 0)
|
||||
* .withWidget(BuiltInWidgets.kNumberSlider)
|
||||
* .withProperties(Map.of("min", 0, "max", 1))
|
||||
* .getEntry();
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Each value in this enum goes into detail on what data types that widget can support, as well
|
||||
* as the custom properties that widget uses.
|
||||
*/
|
||||
public enum BuiltInWidgets implements WidgetType {
|
||||
/**
|
||||
* Displays a value with a simple text field. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>String
|
||||
* <li>Number
|
||||
* <li>Boolean
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kTextView("Text View"),
|
||||
/**
|
||||
* Displays a number with a controllable slider. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Number
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* Custom properties:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
|
||||
* <tr><td>Min</td><td>Number</td><td>-1.0</td><td>The minimum value of the slider</td></tr>
|
||||
* <tr><td>Max</td><td>Number</td><td>1.0</td><td>The maximum value of the slider</td></tr>
|
||||
* <tr><td>Block increment</td><td>Number</td><td>0.0625</td>
|
||||
* <td>How much to move the slider by with the arrow keys</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
kNumberSlider("Number Slider"),
|
||||
/**
|
||||
* Displays a number with a view-only bar. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Number
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* Custom properties:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
|
||||
* <tr><td>Min</td><td>Number</td><td>-1.0</td><td>The minimum value of the bar</td></tr>
|
||||
* <tr><td>Max</td><td>Number</td><td>1.0</td><td>The maximum value of the bar</td></tr>
|
||||
* <tr><td>Center</td><td>Number</td><td>0</td><td>The center ("zero") value of the bar</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
kNumberBar("Number Bar"),
|
||||
/**
|
||||
* Displays a number with a view-only dial. Displayed values are rounded to the nearest integer.
|
||||
* <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Number
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* Custom properties:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
|
||||
* <tr><td>Min</td><td>Number</td><td>0</td><td>The minimum value of the dial</td></tr>
|
||||
* <tr><td>Max</td><td>Number</td><td>100</td><td>The maximum value of the dial</td></tr>
|
||||
* <tr><td>Show value</td><td>Boolean</td><td>true</td>
|
||||
* <td>Whether or not to show the value as text</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
kDial("Simple Dial"),
|
||||
/**
|
||||
* Displays a number with a graph. <strong>NOTE:</strong> graphs can be taxing on the computer
|
||||
* running the dashboard. Keep the number of visible data points to a minimum. Making the widget
|
||||
* smaller also helps with performance, but may cause the graph to become difficult to read. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Number
|
||||
* <li>Number array
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* Custom properties:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
|
||||
* <tr><td>Visible time</td><td>Number</td><td>30</td>
|
||||
* <td>How long, in seconds, should past data be visible for</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
kGraph("Graph"),
|
||||
/**
|
||||
* Displays a boolean value as a large colored box. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Boolean
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* Custom properties:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
|
||||
* <tr><td>Color when true</td><td>Color</td><td>"green"</td>
|
||||
* <td>Can be specified as a string ({@code "#00FF00"}) or a rgba integer ({@code 0x00FF0000})
|
||||
* </td></tr>
|
||||
* <tr><td>Color when false</td><td>Color</td><td>"red"</td>
|
||||
* <td>Can be specified as a string or a number</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
kBooleanBox("Boolean Box"),
|
||||
/**
|
||||
* Displays a boolean with a large interactive toggle button. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Boolean
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kToggleButton("Toggle Button"),
|
||||
/**
|
||||
* Displays a boolean with a fixed-size toggle switch. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Boolean
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kToggleSwitch("Toggle Switch"),
|
||||
/**
|
||||
* Displays an analog input or a raw number with a number bar. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Number
|
||||
* <li>{@link edu.wpi.first.wpilibj.AnalogInput}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* Custom properties:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
|
||||
* <tr><td>Min</td><td>Number</td><td>0</td><td>The minimum value of the bar</td></tr>
|
||||
* <tr><td>Max</td><td>Number</td><td>5</td><td>The maximum value of the bar</td></tr>
|
||||
* <tr><td>Center</td><td>Number</td><td>0</td><td>The center ("zero") value of the bar</td></tr>
|
||||
* <tr><td>Orientation</td><td>String</td><td>"HORIZONTAL"</td>
|
||||
* <td>The orientation of the bar. One of {@code ["HORIZONTAL", "VERTICAL"]}</td></tr>
|
||||
* <tr><td>Number of tick marks</td><td>Number</td><td>5</td>
|
||||
* <td>The number of discrete ticks on the bar</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
kVoltageView("Voltage View"),
|
||||
/**
|
||||
* Displays a {@link edu.wpi.first.wpilibj.PowerDistribution PowerDistribution}. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.wpilibj.PowerDistribution}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* Custom properties:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
|
||||
* <tr><td>Show voltage and current values</td><td>Boolean</td><td>true</td>
|
||||
* <td>Whether or not to display the voltage and current draw</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
kPowerDistribution("PDP"),
|
||||
/**
|
||||
* Displays a {@link edu.wpi.first.wpilibj.smartdashboard.SendableChooser SendableChooser} with a
|
||||
* dropdown combo box with a list of options. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.wpilibj.smartdashboard.SendableChooser}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kComboBoxChooser("ComboBox Chooser"),
|
||||
/**
|
||||
* Displays a {@link edu.wpi.first.wpilibj.smartdashboard.SendableChooser SendableChooser} with a
|
||||
* toggle button for each available option. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.wpilibj.smartdashboard.SendableChooser}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kSplitButtonChooser("Split Button Chooser"),
|
||||
/**
|
||||
* Displays an {@link edu.wpi.first.wpilibj.Encoder} displaying its speed, total traveled
|
||||
* distance, and its distance per tick. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.wpilibj.Encoder}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kEncoder("Encoder"),
|
||||
/**
|
||||
* Displays a {@link edu.wpi.first.wpilibj.motorcontrol.MotorController MotorController}. The
|
||||
* motor controller will be controllable from the dashboard when test mode is enabled, but will
|
||||
* otherwise be view-only. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.PWMMotorController}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.DMC60}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.Jaguar}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.PWMTalonFX}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.PWMTalonSRX}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.PWMVenom}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.PWMVictorSPX}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.SD540}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.Spark}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.Talon}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.Victor}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.VictorSP}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup}
|
||||
* <li>Any custom subclass of {@code MotorController}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* Custom properties:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
|
||||
* <tr><td>Orientation</td><td>String</td><td>"HORIZONTAL"</td>
|
||||
* <td>One of {@code ["HORIZONTAL", "VERTICAL"]}</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
kMotorController("Motor Controller"),
|
||||
/**
|
||||
* Displays a command with a toggle button. Pressing the button will start the command, and the
|
||||
* button will automatically release when the command completes. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.wpilibj2.command.Command}
|
||||
* <li>Any custom subclass of {@code Command}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kCommand("Command"),
|
||||
/**
|
||||
* Displays a PID command with a checkbox and an editor for the PIDF constants. Selecting the
|
||||
* checkbox will start the command, and the checkbox will automatically deselect when the command
|
||||
* completes. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.wpilibj2.command.PIDCommand}
|
||||
* <li>Any custom subclass of {@code PIDCommand}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kPIDCommand("PID Command"),
|
||||
/**
|
||||
* Displays a PID controller with an editor for the PIDF constants and a toggle switch for
|
||||
* enabling and disabling the controller. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.math.controller.PIDController}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kPIDController("PID Controller"),
|
||||
/**
|
||||
* Displays an accelerometer with a number bar displaying the magnitude of the acceleration and
|
||||
* text displaying the exact value. <br>
|
||||
* Custom properties:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
|
||||
* <tr><td>Min</td><td>Number</td><td>-1</td>
|
||||
* <td>The minimum acceleration value to display</td></tr>
|
||||
* <tr><td>Max</td><td>Number</td><td>1</td>
|
||||
* <td>The maximum acceleration value to display</td></tr>
|
||||
* <tr><td>Show text</td><td>Boolean</td><td>true</td>
|
||||
* <td>Show or hide the acceleration values</td></tr>
|
||||
* <tr><td>Precision</td><td>Number</td><td>2</td>
|
||||
* <td>How many numbers to display after the decimal point</td></tr>
|
||||
* <tr><td>Show tick marks</td><td>Boolean</td><td>false</td>
|
||||
* <td>Show or hide the tick marks on the number bars</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
kAccelerometer("Accelerometer"),
|
||||
/**
|
||||
* Displays a 3-axis accelerometer with a number bar for each axis' acceleration. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.wpilibj.ADXL345_I2C}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* Custom properties:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
|
||||
* <tr><td>Show value</td><td>Boolean</td><td>true</td>
|
||||
* <td>Show or hide the acceleration values</td></tr>
|
||||
* <tr><td>Precision</td><td>Number</td><td>2</td>
|
||||
* <td>How many numbers to display after the decimal point</td></tr>
|
||||
* <tr><td>Show tick marks</td><td>Boolean</td><td>false</td>
|
||||
* <td>Show or hide the tick marks on the number bars</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
k3AxisAccelerometer("3-Axis Accelerometer"),
|
||||
/**
|
||||
* Displays a gyro with a dial from 0 to 360 degrees. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.wpilibj.AnalogGyro}
|
||||
* <li>Any custom subclass of {@code GyroBase} (such as a MXP gyro)
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* Custom properties:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
|
||||
* <tr><td>Major tick spacing</td><td>Number</td><td>45</td><td>Degrees</td></tr>
|
||||
* <tr><td>Starting angle</td><td>Number</td><td>180</td>
|
||||
* <td>How far to rotate the entire dial, in degrees</td></tr>
|
||||
* <tr><td>Show tick mark ring</td><td>Boolean</td><td>true</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
kGyro("Gyro"),
|
||||
/**
|
||||
* Displays a relay with toggle buttons for each supported mode (off, on, forward, reverse). <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kRelay("Relay"),
|
||||
/**
|
||||
* Displays a differential drive with a widget that displays the speed of each side of the
|
||||
* drivebase and a vector for the direction and rotation of the drivebase. The widget will be
|
||||
* controllable if the robot is in test mode. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.wpilibj.drive.DifferentialDrive}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* Custom properties:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
|
||||
* <tr><td>Number of wheels</td><td>Number</td><td>4</td><td>Must be a positive even integer
|
||||
* </td></tr>
|
||||
* <tr><td>Wheel diameter</td><td>Number</td><td>80</td><td>Pixels</td></tr>
|
||||
* <tr><td>Show velocity vectors</td><td>Boolean</td><td>true</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
kDifferentialDrive("Differential Drivebase"),
|
||||
/**
|
||||
* Displays a mecanum drive with a widget that displays the speed of each wheel, and vectors for
|
||||
* the direction and rotation of the drivebase. The widget will be controllable if the robot is in
|
||||
* test mode. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.wpilibj.drive.MecanumDrive}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* Custom properties:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
|
||||
* <tr><td>Show velocity vectors</td><td>Boolean</td><td>true</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
kMecanumDrive("Mecanum Drivebase"),
|
||||
/**
|
||||
* Displays a camera stream. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.cscore.VideoSource} (as long as it is streaming on an MJPEG server)
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* Custom properties:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
|
||||
* <tr><td>Show crosshair</td><td>Boolean</td><td>true</td>
|
||||
* <td>Show or hide a crosshair on the image</td></tr>
|
||||
* <tr><td>Crosshair color</td><td>Color</td><td>"white"</td>
|
||||
* <td>Can be a string or a rgba integer</td></tr>
|
||||
* <tr><td>Show controls</td><td>Boolean</td><td>true</td><td>Show or hide the stream controls
|
||||
* </td></tr>
|
||||
* <tr><td>Rotation</td><td>String</td><td>"NONE"</td>
|
||||
* <td>Rotates the displayed image. One of {@code ["NONE", "QUARTER_CW", "QUARTER_CCW", "HALF"]}
|
||||
* </td></tr>
|
||||
* </table>
|
||||
*/
|
||||
kCameraStream("Camera Stream"),
|
||||
/**
|
||||
* Displays a Field2d object.<br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.wpilibj.smartdashboard.Field2d}
|
||||
* </ul>
|
||||
*/
|
||||
kField("Field"),
|
||||
;
|
||||
|
||||
private final String m_widgetName;
|
||||
|
||||
BuiltInWidgets(String widgetName) {
|
||||
this.m_widgetName = widgetName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWidgetName() {
|
||||
return m_widgetName;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.util.sendable.Sendable;
|
||||
import edu.wpi.first.wpilibj.smartdashboard.SendableBuilderImpl;
|
||||
|
||||
/**
|
||||
* A Shuffleboard widget that handles a {@link Sendable} object such as a motor controller or
|
||||
* sensor.
|
||||
*/
|
||||
public final class ComplexWidget extends ShuffleboardWidget<ComplexWidget> {
|
||||
private final Sendable m_sendable;
|
||||
private SendableBuilderImpl m_builder;
|
||||
|
||||
ComplexWidget(ShuffleboardContainer parent, String title, Sendable sendable) {
|
||||
super(parent, title);
|
||||
m_sendable = sendable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildInto(NetworkTable parentTable, NetworkTable metaTable) {
|
||||
buildMetadata(metaTable);
|
||||
if (m_builder == null) {
|
||||
m_builder = new SendableBuilderImpl();
|
||||
m_builder.setTable(parentTable.getSubTable(getTitle()));
|
||||
m_sendable.initSendable(m_builder);
|
||||
m_builder.startListeners();
|
||||
}
|
||||
m_builder.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables user control of this widget in the Shuffleboard application. This method is
|
||||
* package-private to prevent users from enabling control themselves. Has no effect if the
|
||||
* sendable is not marked as an actuator with {@link SendableBuilder#setActuator}.
|
||||
*/
|
||||
void enableIfActuator() {
|
||||
if (m_builder.isActuator()) {
|
||||
m_builder.startLiveWindowMode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables user control of this widget in the Shuffleboard application. This method is
|
||||
* package-private to prevent users from enabling control themselves. Has no effect if the
|
||||
* sendable is not marked as an actuator with {@link SendableBuilder#setActuator}.
|
||||
*/
|
||||
void disableIfActuator() {
|
||||
if (m_builder.isActuator()) {
|
||||
m_builder.stopLiveWindowMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
|
||||
|
||||
import edu.wpi.first.networktables.GenericPublisher;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.networktables.NetworkTableType;
|
||||
import edu.wpi.first.util.function.FloatSupplier;
|
||||
import edu.wpi.first.util.sendable.Sendable;
|
||||
import edu.wpi.first.util.sendable.SendableRegistry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.function.LongSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/** A helper class for Shuffleboard containers to handle common child operations. */
|
||||
final class ContainerHelper {
|
||||
private final ShuffleboardContainer m_container;
|
||||
private final Set<String> m_usedTitles = new HashSet<>();
|
||||
private final List<ShuffleboardComponent<?>> m_components = new ArrayList<>();
|
||||
private final Map<String, ShuffleboardLayout> m_layouts = new LinkedHashMap<>();
|
||||
|
||||
ContainerHelper(ShuffleboardContainer container) {
|
||||
m_container = container;
|
||||
}
|
||||
|
||||
List<ShuffleboardComponent<?>> getComponents() {
|
||||
return m_components;
|
||||
}
|
||||
|
||||
ShuffleboardLayout getLayout(String title, String type) {
|
||||
if (!m_layouts.containsKey(title)) {
|
||||
ShuffleboardLayout layout = new ShuffleboardLayout(m_container, title, type);
|
||||
m_components.add(layout);
|
||||
m_layouts.put(title, layout);
|
||||
}
|
||||
return m_layouts.get(title);
|
||||
}
|
||||
|
||||
ShuffleboardLayout getLayout(String title) {
|
||||
ShuffleboardLayout layout = m_layouts.get(title);
|
||||
if (layout == null) {
|
||||
throw new NoSuchElementException("No layout has been defined with the title '" + title + "'");
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
ComplexWidget add(String title, Sendable sendable) {
|
||||
requireNonNullParam(sendable, "sendable", "add");
|
||||
checkTitle(title);
|
||||
ComplexWidget widget = new ComplexWidget(m_container, title, sendable);
|
||||
m_components.add(widget);
|
||||
return widget;
|
||||
}
|
||||
|
||||
ComplexWidget add(Sendable sendable) {
|
||||
requireNonNullParam(sendable, "sendable", "add");
|
||||
String name = SendableRegistry.getName(sendable);
|
||||
if (name.isEmpty()) {
|
||||
throw new IllegalArgumentException("Sendable must have a name");
|
||||
}
|
||||
return add(name, sendable);
|
||||
}
|
||||
|
||||
SimpleWidget add(String title, Object defaultValue) {
|
||||
requireNonNullParam(defaultValue, "defaultValue", "add");
|
||||
return add(title, NetworkTableType.getStringFromObject(defaultValue), defaultValue);
|
||||
}
|
||||
|
||||
SimpleWidget add(String title, String typeString, Object defaultValue) {
|
||||
requireNonNullParam(title, "title", "add");
|
||||
requireNonNullParam(defaultValue, "defaultValue", "add");
|
||||
checkTitle(title);
|
||||
checkNtType(defaultValue);
|
||||
|
||||
SimpleWidget widget = new SimpleWidget(m_container, title);
|
||||
m_components.add(widget);
|
||||
widget.getEntry(typeString).setDefaultValue(defaultValue);
|
||||
return widget;
|
||||
}
|
||||
|
||||
SuppliedValueWidget<String> addString(String title, Supplier<String> valueSupplier) {
|
||||
precheck(title, valueSupplier, "addString");
|
||||
return addSupplied(title, "string", valueSupplier, GenericPublisher::setString);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<Double> addNumber(String title, DoubleSupplier valueSupplier) {
|
||||
requireNonNullParam(title, "title", "addNumber");
|
||||
requireNonNullParam(valueSupplier, "valueSupplier", "addNumber");
|
||||
return addDouble(title, valueSupplier);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<Double> addDouble(String title, DoubleSupplier valueSupplier) {
|
||||
precheck(title, valueSupplier, "addDouble");
|
||||
return addSupplied(title, "double", valueSupplier::getAsDouble, GenericPublisher::setDouble);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<Float> addFloat(String title, FloatSupplier valueSupplier) {
|
||||
precheck(title, valueSupplier, "addFloat");
|
||||
return addSupplied(title, "float", valueSupplier::getAsFloat, GenericPublisher::setFloat);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<Long> addInteger(String title, LongSupplier valueSupplier) {
|
||||
precheck(title, valueSupplier, "addInteger");
|
||||
return addSupplied(title, "int", valueSupplier::getAsLong, GenericPublisher::setInteger);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<Boolean> addBoolean(String title, BooleanSupplier valueSupplier) {
|
||||
precheck(title, valueSupplier, "addBoolean");
|
||||
return addSupplied(title, "boolean", valueSupplier::getAsBoolean, GenericPublisher::setBoolean);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<String[]> addStringArray(String title, Supplier<String[]> valueSupplier) {
|
||||
precheck(title, valueSupplier, "addStringArray");
|
||||
return addSupplied(title, "string[]", valueSupplier, GenericPublisher::setStringArray);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<double[]> addDoubleArray(String title, Supplier<double[]> valueSupplier) {
|
||||
precheck(title, valueSupplier, "addDoubleArray");
|
||||
return addSupplied(title, "double[]", valueSupplier, GenericPublisher::setDoubleArray);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<float[]> addFloatArray(String title, Supplier<float[]> valueSupplier) {
|
||||
precheck(title, valueSupplier, "addFloatArray");
|
||||
return addSupplied(title, "float[]", valueSupplier, GenericPublisher::setFloatArray);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<long[]> addIntegerArray(String title, Supplier<long[]> valueSupplier) {
|
||||
precheck(title, valueSupplier, "addIntegerArray");
|
||||
return addSupplied(title, "int[]", valueSupplier, GenericPublisher::setIntegerArray);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<boolean[]> addBooleanArray(String title, Supplier<boolean[]> valueSupplier) {
|
||||
precheck(title, valueSupplier, "addBooleanArray");
|
||||
return addSupplied(title, "boolean[]", valueSupplier, GenericPublisher::setBooleanArray);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<byte[]> addRaw(String title, Supplier<byte[]> valueSupplier) {
|
||||
return addRaw(title, "raw", valueSupplier);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<byte[]> addRaw(
|
||||
String title, String typeString, Supplier<byte[]> valueSupplier) {
|
||||
precheck(title, valueSupplier, "addRaw");
|
||||
return addSupplied(title, typeString, valueSupplier, GenericPublisher::setRaw);
|
||||
}
|
||||
|
||||
private void precheck(String title, Object valueSupplier, String methodName) {
|
||||
requireNonNullParam(title, "title", methodName);
|
||||
requireNonNullParam(valueSupplier, "valueSupplier", methodName);
|
||||
checkTitle(title);
|
||||
}
|
||||
|
||||
private <T> SuppliedValueWidget<T> addSupplied(
|
||||
String title,
|
||||
String typeString,
|
||||
Supplier<T> supplier,
|
||||
BiConsumer<GenericPublisher, T> setter) {
|
||||
SuppliedValueWidget<T> widget =
|
||||
new SuppliedValueWidget<>(m_container, title, typeString, supplier, setter);
|
||||
m_components.add(widget);
|
||||
return widget;
|
||||
}
|
||||
|
||||
private static void checkNtType(Object data) {
|
||||
if (!NetworkTableEntry.isValidDataType(data)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot add data of type " + data.getClass().getName() + " to Shuffleboard");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkTitle(String title) {
|
||||
if (m_usedTitles.contains(title)) {
|
||||
throw new IllegalArgumentException("Title is already in use: " + title);
|
||||
}
|
||||
m_usedTitles.add(title);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
/**
|
||||
* The importance of an event marker in Shuffleboard. The exact meaning of each importance level is
|
||||
* up for interpretation on a team-to-team basis, but users should follow the general guidelines of
|
||||
* the various importance levels. The examples given are for reference and may be ignored or
|
||||
* considered to be more or less important from team to team.
|
||||
*/
|
||||
public enum EventImportance {
|
||||
// Maintainer note: this enum is mirrored in WPILibC and in Shuffleboard
|
||||
// Modifying the enum or enum strings requires a corresponding change to the C++ enum
|
||||
// and the enum in Shuffleboard
|
||||
|
||||
/** A trivial event such as a change in command state. */
|
||||
kTrivial("TRIVIAL"),
|
||||
|
||||
/** A low importance event such as acquisition of a game piece. */
|
||||
kLow("LOW"),
|
||||
|
||||
/**
|
||||
* A "normal" importance event, such as a transition from autonomous mode to teleoperated control.
|
||||
*/
|
||||
kNormal("NORMAL"),
|
||||
|
||||
/** A high-importance event such as scoring a game piece. */
|
||||
kHigh("HIGH"),
|
||||
|
||||
/** A critically important event such as a brownout, component failure, or software deadlock. */
|
||||
kCritical("CRITICAL");
|
||||
|
||||
private final String m_simpleName;
|
||||
|
||||
EventImportance(String simpleName) {
|
||||
m_simpleName = simpleName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of the given enum.
|
||||
*
|
||||
* @return Name of the given enum.
|
||||
*/
|
||||
public String getSimpleName() {
|
||||
return m_simpleName;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
/**
|
||||
* Represents the type of a layout in Shuffleboard. Using this is preferred over specifying raw
|
||||
* strings, to avoid typos and having to know or look up the exact string name for a desired layout.
|
||||
*
|
||||
* @see BuiltInWidgets the built-in widget types
|
||||
*/
|
||||
public interface LayoutType {
|
||||
/**
|
||||
* Gets the string type of the layout as defined by that layout in Shuffleboard.
|
||||
*
|
||||
* @return The string type of the layout.
|
||||
*/
|
||||
String getLayoutName();
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.BooleanPublisher;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.networktables.StringPublisher;
|
||||
import edu.wpi.first.wpilibj.DriverStation;
|
||||
|
||||
/** Controls Shuffleboard recordings via NetworkTables. */
|
||||
final class RecordingController {
|
||||
private static final String kRecordingTableName = "/Shuffleboard/.recording/";
|
||||
private static final String kRecordingControlKey = kRecordingTableName + "RecordData";
|
||||
private static final String kRecordingFileNameFormatKey = kRecordingTableName + "FileNameFormat";
|
||||
private static final String kEventMarkerTableName = kRecordingTableName + "events";
|
||||
|
||||
private final BooleanPublisher m_recordingControlEntry;
|
||||
private final StringPublisher m_recordingFileNameFormatEntry;
|
||||
private final NetworkTable m_eventsTable;
|
||||
|
||||
RecordingController(NetworkTableInstance ntInstance) {
|
||||
m_recordingControlEntry = ntInstance.getBooleanTopic(kRecordingControlKey).publish();
|
||||
m_recordingFileNameFormatEntry =
|
||||
ntInstance.getStringTopic(kRecordingFileNameFormatKey).publish();
|
||||
m_eventsTable = ntInstance.getTable(kEventMarkerTableName);
|
||||
}
|
||||
|
||||
public void startRecording() {
|
||||
m_recordingControlEntry.set(true);
|
||||
}
|
||||
|
||||
public void stopRecording() {
|
||||
m_recordingControlEntry.set(false);
|
||||
}
|
||||
|
||||
public void setRecordingFileNameFormat(String format) {
|
||||
m_recordingFileNameFormatEntry.set(format);
|
||||
}
|
||||
|
||||
public void clearRecordingFileNameFormat() {
|
||||
m_recordingFileNameFormatEntry.set("");
|
||||
}
|
||||
|
||||
public void addEventMarker(String name, String description, EventImportance importance) {
|
||||
if (name == null || name.isEmpty()) {
|
||||
DriverStation.reportError("Shuffleboard event name was not specified", true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (importance == null) {
|
||||
DriverStation.reportError("Shuffleboard event importance was null", true);
|
||||
return;
|
||||
}
|
||||
|
||||
String eventDescription = description == null ? "" : description;
|
||||
|
||||
m_eventsTable
|
||||
.getSubTable(name)
|
||||
.getEntry("Info")
|
||||
.setStringArray(new String[] {eventDescription, importance.getSimpleName()});
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
|
||||
|
||||
import edu.wpi.first.cscore.VideoSource;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.networktables.StringArrayPublisher;
|
||||
import edu.wpi.first.networktables.StringArrayTopic;
|
||||
import edu.wpi.first.util.sendable.Sendable;
|
||||
import edu.wpi.first.util.sendable.SendableBuilder;
|
||||
import edu.wpi.first.util.sendable.SendableRegistry;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/** A wrapper to make video sources sendable and usable from Shuffleboard. */
|
||||
public final class SendableCameraWrapper implements Sendable, AutoCloseable {
|
||||
private static final String kProtocol = "camera_server://";
|
||||
|
||||
private static Map<String, SendableCameraWrapper> m_wrappers = new WeakHashMap<>();
|
||||
|
||||
private static NetworkTable m_table;
|
||||
|
||||
static {
|
||||
setNetworkTableInstance(NetworkTableInstance.getDefault());
|
||||
}
|
||||
|
||||
private final String m_uri;
|
||||
private StringArrayPublisher m_streams;
|
||||
|
||||
/**
|
||||
* Creates a new sendable wrapper. Private constructor to avoid direct instantiation with multiple
|
||||
* wrappers floating around for the same camera.
|
||||
*
|
||||
* @param source the source to wrap
|
||||
*/
|
||||
private SendableCameraWrapper(VideoSource source) {
|
||||
this(source.getName());
|
||||
}
|
||||
|
||||
private SendableCameraWrapper(String cameraName) {
|
||||
SendableRegistry.add(this, cameraName);
|
||||
m_uri = kProtocol + cameraName;
|
||||
}
|
||||
|
||||
private SendableCameraWrapper(String cameraName, String[] cameraUrls) {
|
||||
this(cameraName);
|
||||
|
||||
StringArrayTopic streams = new StringArrayTopic(m_table.getTopic(cameraName + "/streams"));
|
||||
if (streams.exists()) {
|
||||
throw new IllegalStateException(
|
||||
"A camera is already being streamed with the name '" + cameraName + "'");
|
||||
}
|
||||
|
||||
m_streams = streams.publish();
|
||||
m_streams.set(cameraUrls);
|
||||
}
|
||||
|
||||
/** Clears all cached wrapper objects. This should only be used in tests. */
|
||||
static void clearWrappers() {
|
||||
m_wrappers.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
SendableRegistry.remove(this);
|
||||
if (m_streams != null) {
|
||||
m_streams.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets NetworkTable instance used for camera publisher entries.
|
||||
*
|
||||
* @param inst NetworkTable instance
|
||||
*/
|
||||
public static synchronized void setNetworkTableInstance(NetworkTableInstance inst) {
|
||||
m_table = inst.getTable("CameraPublisher");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a sendable wrapper object for the given video source, creating the wrapper if one does not
|
||||
* already exist for the source.
|
||||
*
|
||||
* @param source the video source to wrap
|
||||
* @return a sendable wrapper object for the video source, usable in Shuffleboard via {@link
|
||||
* ShuffleboardTab#add(Sendable)} and {@link ShuffleboardLayout#add(Sendable)}
|
||||
*/
|
||||
public static SendableCameraWrapper wrap(VideoSource source) {
|
||||
return m_wrappers.computeIfAbsent(source.getName(), name -> new SendableCameraWrapper(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wrapper for an arbitrary camera stream. The stream URLs <i>must</i> be specified
|
||||
* using a host resolvable by a program running on a different host (such as a dashboard); prefer
|
||||
* using static IP addresses (if known) or DHCP identifiers such as {@code "raspberrypi.local"}.
|
||||
*
|
||||
* <p>If a wrapper already exists for the given camera, that wrapper is returned and the specified
|
||||
* URLs are ignored.
|
||||
*
|
||||
* @param cameraName the name of the camera. Cannot be null or empty
|
||||
* @param cameraUrls the URLs with which the camera stream may be accessed. At least one URL must
|
||||
* be specified
|
||||
* @return a sendable wrapper object for the video source, usable in Shuffleboard via {@link
|
||||
* ShuffleboardTab#add(Sendable)} and {@link ShuffleboardLayout#add(Sendable)}
|
||||
*/
|
||||
public static SendableCameraWrapper wrap(String cameraName, String... cameraUrls) {
|
||||
if (m_wrappers.containsKey(cameraName)) {
|
||||
return m_wrappers.get(cameraName);
|
||||
}
|
||||
|
||||
requireNonNullParam(cameraName, "cameraName", "wrap");
|
||||
requireNonNullParam(cameraUrls, "cameraUrls", "wrap");
|
||||
if (cameraName.isEmpty()) {
|
||||
throw new IllegalArgumentException("Camera name not specified");
|
||||
}
|
||||
if (cameraUrls.length == 0) {
|
||||
throw new IllegalArgumentException("No camera URLs specified");
|
||||
}
|
||||
for (int i = 0; i < cameraUrls.length; i++) {
|
||||
Objects.requireNonNull(cameraUrls[i], "Camera URL at index " + i + " was null");
|
||||
}
|
||||
|
||||
SendableCameraWrapper wrapper = new SendableCameraWrapper(cameraName, cameraUrls);
|
||||
m_wrappers.put(cameraName, wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initSendable(SendableBuilder builder) {
|
||||
builder.addStringProperty(".ShuffleboardURI", () -> m_uri, null);
|
||||
}
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
|
||||
/**
|
||||
* The Shuffleboard class provides a mechanism with which data can be added and laid out in the
|
||||
* Shuffleboard dashboard application from a robot program. Tabs and layouts can be specified, as
|
||||
* well as choosing which widgets to display with and setting properties of these widgets; for
|
||||
* example, programmers can specify a specific {@code boolean} value to be displayed with a toggle
|
||||
* button instead of the default colored box, or set custom colors for that box.
|
||||
*
|
||||
* <p>For example, displaying a boolean entry with a toggle button:
|
||||
*
|
||||
* <pre>{@code
|
||||
* GenericEntry myBoolean = Shuffleboard.getTab("Example Tab")
|
||||
* .add("My Boolean", false)
|
||||
* .withWidget("Toggle Button")
|
||||
* .getEntry();
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Changing the colors of the boolean box:
|
||||
*
|
||||
* <pre>{@code
|
||||
* GenericEntry myBoolean = Shuffleboard.getTab("Example Tab")
|
||||
* .add("My Boolean", false)
|
||||
* .withWidget("Boolean Box")
|
||||
* .withProperties(Map.of("colorWhenTrue", "green", "colorWhenFalse", "maroon"))
|
||||
* .getEntry();
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Specifying a parent layout. Note that the layout type must <i>always</i> be specified, even if
|
||||
* the layout has already been generated by a previously defined entry.
|
||||
*
|
||||
* <pre>{@code
|
||||
* GenericEntry myBoolean = Shuffleboard.getTab("Example Tab")
|
||||
* .getLayout("List", "Example List")
|
||||
* .add("My Boolean", false)
|
||||
* .withWidget("Toggle Button")
|
||||
* .getEntry();
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Teams are encouraged to set up shuffleboard layouts at the start of the robot program.
|
||||
*/
|
||||
public final class Shuffleboard {
|
||||
/** The name of the base NetworkTable into which all Shuffleboard data will be added. */
|
||||
public static final String kBaseTableName = "/Shuffleboard";
|
||||
|
||||
private static final ShuffleboardRoot root =
|
||||
new ShuffleboardInstance(NetworkTableInstance.getDefault());
|
||||
private static final RecordingController recordingController =
|
||||
new RecordingController(NetworkTableInstance.getDefault());
|
||||
|
||||
private Shuffleboard() {
|
||||
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all the values in Shuffleboard. Iterative and timed robots are pre-configured to call
|
||||
* this method in the main robot loop; teams using custom robot base classes, or subclass
|
||||
* SampleRobot, should make sure to call this repeatedly to keep data on the dashboard up to date.
|
||||
*/
|
||||
public static void update() {
|
||||
root.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Shuffleboard tab with the given title, creating it if it does not already exist.
|
||||
*
|
||||
* @param title the title of the tab
|
||||
* @return the tab with the given title
|
||||
*/
|
||||
public static ShuffleboardTab getTab(String title) {
|
||||
return root.getTab(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the tab in the dashboard with the given index in the range [0..n-1], where <i>n</i> is
|
||||
* the number of tabs in the dashboard at the time this method is called.
|
||||
*
|
||||
* @param index the index of the tab to select
|
||||
*/
|
||||
public static void selectTab(int index) {
|
||||
root.selectTab(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the tab in the dashboard with the given title.
|
||||
*
|
||||
* @param title the title of the tab to select
|
||||
*/
|
||||
public static void selectTab(String title) {
|
||||
root.selectTab(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables user control of widgets containing actuators: motor controllers, relays, etc. This
|
||||
* should only be used when the robot is in test mode. IterativeRobotBase and SampleRobot are both
|
||||
* configured to call this method when entering test mode; most users should not need to use this
|
||||
* method directly.
|
||||
*/
|
||||
public static void enableActuatorWidgets() {
|
||||
root.enableActuatorWidgets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables user control of widgets containing actuators. For safety reasons, actuators should
|
||||
* only be controlled while in test mode. IterativeRobotBase and SampleRobot are both configured
|
||||
* to call this method when exiting in test mode; most users should not need to use this method
|
||||
* directly.
|
||||
*/
|
||||
public static void disableActuatorWidgets() {
|
||||
update(); // Need to update to make sure the sendable builders are initialized
|
||||
root.disableActuatorWidgets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts data recording on the dashboard. Has no effect if recording is already in progress.
|
||||
*
|
||||
* @see #stopRecording()
|
||||
*/
|
||||
public static void startRecording() {
|
||||
recordingController.startRecording();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops data recording on the dashboard. Has no effect if no recording is in progress.
|
||||
*
|
||||
* @see #startRecording()
|
||||
*/
|
||||
public static void stopRecording() {
|
||||
recordingController.stopRecording();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file name format for new recording files to use. If recording is in progress when this
|
||||
* method is called, it will continue to use the same file. New recordings will use the format.
|
||||
*
|
||||
* <p>To avoid recording files overwriting each other, make sure to use unique recording file
|
||||
* names. File name formats accept templates for inserting the date and time when the recording
|
||||
* started with the {@code ${date}} and {@code ${time}} templates, respectively. For example, the
|
||||
* default format is {@code "recording-${time}"} and recording files created with it will have
|
||||
* names like {@code "recording-2018.01.15.sbr"}. Users are <strong>strongly</strong> recommended
|
||||
* to use the {@code ${time}} template to ensure unique file names.
|
||||
*
|
||||
* @param format the format for the
|
||||
* @see #clearRecordingFileNameFormat()
|
||||
*/
|
||||
public static void setRecordingFileNameFormat(String format) {
|
||||
recordingController.setRecordingFileNameFormat(format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the custom name format for recording files. New recordings will use the default format.
|
||||
*
|
||||
* @see #setRecordingFileNameFormat(String)
|
||||
*/
|
||||
public static void clearRecordingFileNameFormat() {
|
||||
recordingController.clearRecordingFileNameFormat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies Shuffleboard of an event. Events can range from as trivial as a change in a command
|
||||
* state to as critical as a total power loss or component failure. If Shuffleboard is recording,
|
||||
* the event will also be recorded.
|
||||
*
|
||||
* <p>If {@code name} is {@code null} or empty, or {@code importance} is {@code null}, then no
|
||||
* event will be sent and an error will be printed to the driver station.
|
||||
*
|
||||
* @param name the name of the event
|
||||
* @param description a description of the event
|
||||
* @param importance the importance of the event
|
||||
*/
|
||||
public static void addEventMarker(String name, String description, EventImportance importance) {
|
||||
recordingController.addEventMarker(name, description, importance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies Shuffleboard of an event. Events can range from as trivial as a change in a command
|
||||
* state to as critical as a total power loss or component failure. If Shuffleboard is recording,
|
||||
* the event will also be recorded.
|
||||
*
|
||||
* <p>If {@code name} is {@code null} or empty, or {@code importance} is {@code null}, then no
|
||||
* event will be sent and an error will be printed to the driver station.
|
||||
*
|
||||
* @param name the name of the event
|
||||
* @param importance the importance of the event
|
||||
*/
|
||||
public static void addEventMarker(String name, EventImportance importance) {
|
||||
addEventMarker(name, "", importance);
|
||||
}
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
|
||||
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A generic component in Shuffleboard.
|
||||
*
|
||||
* @param <C> the self type
|
||||
*/
|
||||
public abstract class ShuffleboardComponent<C extends ShuffleboardComponent<C>>
|
||||
implements ShuffleboardValue {
|
||||
private final ShuffleboardContainer m_parent;
|
||||
private final String m_title;
|
||||
private String m_type;
|
||||
private Map<String, Object> m_properties;
|
||||
private boolean m_metadataDirty = true;
|
||||
private int m_column = -1;
|
||||
private int m_row = -1;
|
||||
private int m_width = -1;
|
||||
private int m_height = -1;
|
||||
|
||||
/**
|
||||
* Constructs a ShuffleboardComponent.
|
||||
*
|
||||
* @param parent The parent container.
|
||||
* @param title The component title.
|
||||
* @param type The component type.
|
||||
*/
|
||||
protected ShuffleboardComponent(ShuffleboardContainer parent, String title, String type) {
|
||||
m_parent = requireNonNullParam(parent, "parent", "ShuffleboardComponent");
|
||||
m_title = requireNonNullParam(title, "title", "ShuffleboardComponent");
|
||||
m_type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a ShuffleboardComponent.
|
||||
*
|
||||
* @param parent The parent container.
|
||||
* @param title The component title.
|
||||
*/
|
||||
protected ShuffleboardComponent(ShuffleboardContainer parent, String title) {
|
||||
this(parent, title, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent container.
|
||||
*
|
||||
* @return The parent container.
|
||||
*/
|
||||
public final ShuffleboardContainer getParent() {
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the component type.
|
||||
*
|
||||
* @param type The component type.
|
||||
*/
|
||||
protected final void setType(String type) {
|
||||
m_type = type;
|
||||
m_metadataDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the component type.
|
||||
*
|
||||
* @return The component type.
|
||||
*/
|
||||
public final String getType() {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getTitle() {
|
||||
return m_title;
|
||||
}
|
||||
|
||||
/** Gets the custom properties for this component. May be null. */
|
||||
final Map<String, Object> getProperties() {
|
||||
return m_properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets custom properties for this component. Property names are case- and whitespace-insensitive
|
||||
* (capitalization and spaces do not matter).
|
||||
*
|
||||
* @param properties the properties for this component
|
||||
* @return this component
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final C withProperties(Map<String, Object> properties) {
|
||||
m_properties = properties;
|
||||
m_metadataDirty = true;
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the position of this component in the tab. This has no effect if this component is inside
|
||||
* a layout.
|
||||
*
|
||||
* <p>If the position of a single component is set, it is recommended to set the positions of
|
||||
* <i>all</i> components inside a tab to prevent Shuffleboard from automatically placing another
|
||||
* component there before the one with the specific position is sent.
|
||||
*
|
||||
* @param columnIndex the column in the tab to place this component
|
||||
* @param rowIndex the row in the tab to place this component
|
||||
* @return this component
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final C withPosition(int columnIndex, int rowIndex) {
|
||||
m_column = columnIndex;
|
||||
m_row = rowIndex;
|
||||
m_metadataDirty = true;
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of this component in the tab. This has no effect if this component is inside a
|
||||
* layout.
|
||||
*
|
||||
* @param width how many columns wide the component should be
|
||||
* @param height how many rows high the component should be
|
||||
* @return this component
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final C withSize(int width, int height) {
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_metadataDirty = true;
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds NT metadata.
|
||||
*
|
||||
* @param metaTable The NT metadata table.
|
||||
*/
|
||||
protected final void buildMetadata(NetworkTable metaTable) {
|
||||
if (!m_metadataDirty) {
|
||||
return;
|
||||
}
|
||||
// Component type
|
||||
if (getType() == null) {
|
||||
metaTable.getEntry("PreferredComponent").unpublish();
|
||||
} else {
|
||||
metaTable.getEntry("PreferredComponent").setString(getType());
|
||||
}
|
||||
|
||||
// Tile size
|
||||
if (m_width <= 0 || m_height <= 0) {
|
||||
metaTable.getEntry("Size").unpublish();
|
||||
} else {
|
||||
metaTable.getEntry("Size").setDoubleArray(new double[] {m_width, m_height});
|
||||
}
|
||||
|
||||
// Tile position
|
||||
if (m_column < 0 || m_row < 0) {
|
||||
metaTable.getEntry("Position").unpublish();
|
||||
} else {
|
||||
metaTable.getEntry("Position").setDoubleArray(new double[] {m_column, m_row});
|
||||
}
|
||||
|
||||
// Custom properties
|
||||
if (getProperties() != null) {
|
||||
NetworkTable propTable = metaTable.getSubTable("Properties");
|
||||
getProperties().forEach((name, value) -> propTable.getEntry(name).setValue(value));
|
||||
}
|
||||
m_metadataDirty = false;
|
||||
}
|
||||
}
|
||||
@@ -1,363 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.cscore.VideoSource;
|
||||
import edu.wpi.first.networktables.NetworkTableType;
|
||||
import edu.wpi.first.util.function.FloatSupplier;
|
||||
import edu.wpi.first.util.sendable.Sendable;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.function.LongSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/** Common interface for objects that can contain shuffleboard components. */
|
||||
public sealed interface ShuffleboardContainer extends ShuffleboardValue
|
||||
permits ShuffleboardLayout, ShuffleboardTab {
|
||||
/**
|
||||
* Gets the components that are direct children of this container.
|
||||
*
|
||||
* @return The components that are direct children of this container.
|
||||
*/
|
||||
List<ShuffleboardComponent<?>> getComponents();
|
||||
|
||||
/**
|
||||
* Gets the layout with the given type and title, creating it if it does not already exist at the
|
||||
* time this method is called. Note: this method should only be used to use a layout type that is
|
||||
* not already built into Shuffleboard. To use a layout built into Shuffleboard, use {@link
|
||||
* #getLayout(String, LayoutType)} and the layouts in {@link BuiltInLayouts}.
|
||||
*
|
||||
* @param title the title of the layout
|
||||
* @param type the type of the layout, eg "List Layout" or "Grid Layout"
|
||||
* @return the layout
|
||||
* @see #getLayout(String, LayoutType)
|
||||
*/
|
||||
ShuffleboardLayout getLayout(String title, String type);
|
||||
|
||||
/**
|
||||
* Gets the layout with the given type and title, creating it if it does not already exist at the
|
||||
* time this method is called.
|
||||
*
|
||||
* @param title the title of the layout
|
||||
* @param layoutType the type of the layout, eg "List" or "Grid"
|
||||
* @return the layout
|
||||
*/
|
||||
default ShuffleboardLayout getLayout(String title, LayoutType layoutType) {
|
||||
return getLayout(title, layoutType.getLayoutName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the already-defined layout in this container with the given title.
|
||||
*
|
||||
* <pre>{@code
|
||||
* Shuffleboard.getTab("Example Tab")
|
||||
* .getLayout("My Layout", BuiltInLayouts.kList);
|
||||
*
|
||||
* // Later...
|
||||
* Shuffleboard.getTab("Example Tab")
|
||||
* .getLayout("My Layout");
|
||||
* }</pre>
|
||||
*
|
||||
* @param title the title of the layout to get
|
||||
* @return the layout with the given title
|
||||
* @throws NoSuchElementException if no layout has yet been defined with the given title
|
||||
*/
|
||||
ShuffleboardLayout getLayout(String title);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given sendable.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param sendable the sendable to display
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
ComplexWidget add(String title, Sendable sendable);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given video stream.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param video the video stream to display
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
default ComplexWidget add(String title, VideoSource video) {
|
||||
return add(title, SendableCameraWrapper.wrap(video));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given sendable.
|
||||
*
|
||||
* @param sendable the sendable to display
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title, or if the sendable's name has not been specified
|
||||
*/
|
||||
ComplexWidget add(Sendable sendable);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given video stream.
|
||||
*
|
||||
* @param video the video to display
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the same
|
||||
* title as the video source
|
||||
*/
|
||||
default ComplexWidget add(VideoSource video) {
|
||||
return add(SendableCameraWrapper.wrap(video));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given data.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
* @see #addPersistent(String, Object) add(String title, Object defaultValue)
|
||||
*/
|
||||
SimpleWidget add(String title, Object defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given data.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param typeString the NT type string
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
* @see #addPersistent(String, Object) add(String title, Object defaultValue)
|
||||
*/
|
||||
SimpleWidget add(String title, String typeString, Object defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a video stream.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param cameraName the name of the streamed camera
|
||||
* @param cameraUrls the URLs with which the dashboard can access the camera stream
|
||||
* @return a widget to display the camera stream
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
default ComplexWidget addCamera(String title, String cameraName, String... cameraUrls) {
|
||||
return add(title, SendableCameraWrapper.wrap(cameraName, cameraUrls));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<String> addString(String title, Supplier<String> valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<Double> addNumber(String title, DoubleSupplier valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<Double> addDouble(String title, DoubleSupplier valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<Float> addFloat(String title, FloatSupplier valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<Long> addInteger(String title, LongSupplier valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<Boolean> addBoolean(String title, BooleanSupplier valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<String[]> addStringArray(String title, Supplier<String[]> valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<double[]> addDoubleArray(String title, Supplier<double[]> valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<float[]> addFloatArray(String title, Supplier<float[]> valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<long[]> addIntegerArray(String title, Supplier<long[]> valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<boolean[]> addBooleanArray(String title, Supplier<boolean[]> valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
default SuppliedValueWidget<byte[]> addRaw(String title, Supplier<byte[]> valueSupplier) {
|
||||
return addRaw(title, "raw", valueSupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param typeString the NT type string for the value
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<byte[]> addRaw(
|
||||
String title, String typeString, Supplier<byte[]> valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a simple piece of data. Unlike {@link #add(String,
|
||||
* Object)}, the value in the widget will be saved on the robot and will be used when the robot
|
||||
* program next starts rather than {@code defaultValue}.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
* @see #add(String, Object) add(String title, Object defaultValue)
|
||||
*/
|
||||
default SimpleWidget addPersistent(String title, Object defaultValue) {
|
||||
return addPersistent(title, NetworkTableType.getStringFromObject(defaultValue), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a simple piece of data. Unlike {@link #add(String,
|
||||
* Object)}, the value in the widget will be saved on the robot and will be used when the robot
|
||||
* program next starts rather than {@code defaultValue}.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param typeString the NT type string
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
* @see #add(String, Object) add(String title, Object defaultValue)
|
||||
*/
|
||||
default SimpleWidget addPersistent(String title, String typeString, Object defaultValue) {
|
||||
SimpleWidget widget = add(title, defaultValue);
|
||||
widget.getEntry(typeString).getTopic().setPersistent(true);
|
||||
return widget;
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
|
||||
|
||||
import edu.wpi.first.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.hal.HAL;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.networktables.PubSubOption;
|
||||
import edu.wpi.first.networktables.StringPublisher;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
final class ShuffleboardInstance implements ShuffleboardRoot {
|
||||
private final Map<String, ShuffleboardTab> m_tabs = new LinkedHashMap<>();
|
||||
private boolean m_reported = false; // NOPMD redundant field initializer
|
||||
private boolean m_tabsChanged = false; // NOPMD redundant field initializer
|
||||
private final NetworkTable m_rootTable;
|
||||
private final NetworkTable m_rootMetaTable;
|
||||
private final StringPublisher m_selectedTabPub;
|
||||
|
||||
/**
|
||||
* Creates a new Shuffleboard instance.
|
||||
*
|
||||
* @param ntInstance the NetworkTables instance to use
|
||||
*/
|
||||
ShuffleboardInstance(NetworkTableInstance ntInstance) {
|
||||
requireNonNullParam(ntInstance, "ntInstance", "ShuffleboardInstance");
|
||||
m_rootTable = ntInstance.getTable(Shuffleboard.kBaseTableName);
|
||||
m_rootMetaTable = m_rootTable.getSubTable(".metadata");
|
||||
m_selectedTabPub =
|
||||
m_rootMetaTable.getStringTopic("Selected").publish(PubSubOption.keepDuplicates(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShuffleboardTab getTab(String title) {
|
||||
requireNonNullParam(title, "title", "getTab");
|
||||
if (!m_reported) {
|
||||
HAL.report(tResourceType.kResourceType_Shuffleboard, 0);
|
||||
m_reported = true;
|
||||
}
|
||||
if (!m_tabs.containsKey(title)) {
|
||||
m_tabs.put(title, new ShuffleboardTab(this, title));
|
||||
m_tabsChanged = true;
|
||||
}
|
||||
return m_tabs.get(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
if (m_tabsChanged) {
|
||||
String[] tabTitles =
|
||||
m_tabs.values().stream().map(ShuffleboardTab::getTitle).toArray(String[]::new);
|
||||
m_rootMetaTable.getEntry("Tabs").setStringArray(tabTitles);
|
||||
m_tabsChanged = false;
|
||||
}
|
||||
for (ShuffleboardTab tab : m_tabs.values()) {
|
||||
String title = tab.getTitle();
|
||||
tab.buildInto(m_rootTable, m_rootMetaTable.getSubTable(title));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableActuatorWidgets() {
|
||||
applyToAllComplexWidgets(ComplexWidget::enableIfActuator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableActuatorWidgets() {
|
||||
applyToAllComplexWidgets(ComplexWidget::disableIfActuator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectTab(int index) {
|
||||
selectTab(Integer.toString(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectTab(String title) {
|
||||
m_selectedTabPub.set(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the function {@code func} to all complex widgets in this root, regardless of how they
|
||||
* are nested.
|
||||
*
|
||||
* @param func the function to apply to all complex widgets
|
||||
*/
|
||||
private void applyToAllComplexWidgets(Consumer<ComplexWidget> func) {
|
||||
for (ShuffleboardTab tab : m_tabs.values()) {
|
||||
apply(tab, func);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the function {@code func} to all complex widgets in {@code container}. Helper method
|
||||
* for {@link #applyToAllComplexWidgets}.
|
||||
*/
|
||||
private void apply(ShuffleboardContainer container, Consumer<ComplexWidget> func) {
|
||||
for (ShuffleboardComponent<?> component : container.getComponents()) {
|
||||
if (component instanceof ComplexWidget widget) {
|
||||
func.accept(widget);
|
||||
}
|
||||
if (component instanceof ShuffleboardContainer nestedContainer) {
|
||||
apply(nestedContainer, func);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
|
||||
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.util.function.FloatSupplier;
|
||||
import edu.wpi.first.util.sendable.Sendable;
|
||||
import java.util.List;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.function.LongSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/** A layout in a Shuffleboard tab. Layouts can contain widgets and other layouts. */
|
||||
public final class ShuffleboardLayout extends ShuffleboardComponent<ShuffleboardLayout>
|
||||
implements ShuffleboardContainer {
|
||||
private static final String kSmartDashboardType = "ShuffleboardLayout";
|
||||
|
||||
private final ContainerHelper m_helper = new ContainerHelper(this);
|
||||
|
||||
ShuffleboardLayout(ShuffleboardContainer parent, String title, String type) {
|
||||
super(parent, title, requireNonNullParam(type, "type", "ShuffleboardLayout"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ShuffleboardComponent<?>> getComponents() {
|
||||
return m_helper.getComponents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShuffleboardLayout getLayout(String title, String type) {
|
||||
return m_helper.getLayout(title, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShuffleboardLayout getLayout(String title) {
|
||||
return m_helper.getLayout(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexWidget add(String title, Sendable sendable) {
|
||||
return m_helper.add(title, sendable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexWidget add(Sendable sendable) {
|
||||
return m_helper.add(sendable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleWidget add(String title, Object defaultValue) {
|
||||
return m_helper.add(title, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleWidget add(String title, String typeString, Object defaultValue) {
|
||||
return m_helper.add(title, typeString, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<String> addString(String title, Supplier<String> valueSupplier) {
|
||||
return m_helper.addString(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Double> addNumber(String title, DoubleSupplier valueSupplier) {
|
||||
return m_helper.addNumber(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Double> addDouble(String title, DoubleSupplier valueSupplier) {
|
||||
return m_helper.addDouble(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Float> addFloat(String title, FloatSupplier valueSupplier) {
|
||||
return m_helper.addFloat(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Long> addInteger(String title, LongSupplier valueSupplier) {
|
||||
return m_helper.addInteger(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Boolean> addBoolean(String title, BooleanSupplier valueSupplier) {
|
||||
return m_helper.addBoolean(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<String[]> addStringArray(
|
||||
String title, Supplier<String[]> valueSupplier) {
|
||||
return m_helper.addStringArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<double[]> addDoubleArray(
|
||||
String title, Supplier<double[]> valueSupplier) {
|
||||
return m_helper.addDoubleArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<float[]> addFloatArray(String title, Supplier<float[]> valueSupplier) {
|
||||
return m_helper.addFloatArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<long[]> addIntegerArray(String title, Supplier<long[]> valueSupplier) {
|
||||
return m_helper.addIntegerArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<boolean[]> addBooleanArray(
|
||||
String title, Supplier<boolean[]> valueSupplier) {
|
||||
return m_helper.addBooleanArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<byte[]> addRaw(
|
||||
String title, String typeString, Supplier<byte[]> valueSupplier) {
|
||||
return m_helper.addRaw(title, typeString, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildInto(NetworkTable parentTable, NetworkTable metaTable) {
|
||||
buildMetadata(metaTable);
|
||||
NetworkTable table = parentTable.getSubTable(getTitle());
|
||||
table.getEntry(".type").setString(kSmartDashboardType);
|
||||
table
|
||||
.getEntry(".type")
|
||||
.getTopic()
|
||||
.setProperty("SmartDashboard", "\"" + kSmartDashboardType + "\"");
|
||||
for (ShuffleboardComponent<?> component : getComponents()) {
|
||||
component.buildInto(table, metaTable.getSubTable(component.getTitle()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
/**
|
||||
* The root of the data placed in Shuffleboard. It contains the tabs, but no data is placed directly
|
||||
* in the root.
|
||||
*
|
||||
* <p>This class is package-private to minimize API surface area.
|
||||
*/
|
||||
interface ShuffleboardRoot {
|
||||
/**
|
||||
* Gets the tab with the given title, creating it if it does not already exist.
|
||||
*
|
||||
* @param title the title of the tab
|
||||
* @return the tab with the given title
|
||||
*/
|
||||
ShuffleboardTab getTab(String title);
|
||||
|
||||
/** Updates all tabs. */
|
||||
void update();
|
||||
|
||||
/** Enables all widgets in Shuffleboard that offer user control over actuators. */
|
||||
void enableActuatorWidgets();
|
||||
|
||||
/** Disables all widgets in Shuffleboard that offer user control over actuators. */
|
||||
void disableActuatorWidgets();
|
||||
|
||||
/**
|
||||
* Selects the tab in the dashboard with the given index in the range [0..n-1], where <i>n</i> is
|
||||
* the number of tabs in the dashboard at the time this method is called.
|
||||
*
|
||||
* @param index the index of the tab to select
|
||||
*/
|
||||
void selectTab(int index);
|
||||
|
||||
/**
|
||||
* Selects the tab in the dashboard with the given title.
|
||||
*
|
||||
* @param title the title of the tab to select
|
||||
*/
|
||||
void selectTab(String title);
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.util.function.FloatSupplier;
|
||||
import edu.wpi.first.util.sendable.Sendable;
|
||||
import java.util.List;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.function.LongSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Represents a tab in the Shuffleboard dashboard. Widgets can be added to the tab with {@link
|
||||
* #add(Sendable)}, {@link #add(String, Object)}, and {@link #add(String, Sendable)}. Widgets can
|
||||
* also be added to layouts with {@link #getLayout(String, String)}; layouts can be nested
|
||||
* arbitrarily deep (note that too many levels may make deeper components unusable).
|
||||
*/
|
||||
public final class ShuffleboardTab implements ShuffleboardContainer {
|
||||
private static final String kSmartDashboardType = "ShuffleboardTab";
|
||||
|
||||
private final ContainerHelper m_helper = new ContainerHelper(this);
|
||||
private final ShuffleboardRoot m_root;
|
||||
private final String m_title;
|
||||
|
||||
ShuffleboardTab(ShuffleboardRoot root, String title) {
|
||||
m_root = root;
|
||||
m_title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return m_title;
|
||||
}
|
||||
|
||||
ShuffleboardRoot getRoot() {
|
||||
return m_root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ShuffleboardComponent<?>> getComponents() {
|
||||
return m_helper.getComponents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShuffleboardLayout getLayout(String title, String type) {
|
||||
return m_helper.getLayout(title, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShuffleboardLayout getLayout(String title) {
|
||||
return m_helper.getLayout(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexWidget add(String title, Sendable sendable) {
|
||||
return m_helper.add(title, sendable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexWidget add(Sendable sendable) {
|
||||
return m_helper.add(sendable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleWidget add(String title, Object defaultValue) {
|
||||
return m_helper.add(title, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleWidget add(String title, String typeString, Object defaultValue) {
|
||||
return m_helper.add(title, typeString, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<String> addString(String title, Supplier<String> valueSupplier) {
|
||||
return m_helper.addString(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Double> addNumber(String title, DoubleSupplier valueSupplier) {
|
||||
return m_helper.addNumber(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Double> addDouble(String title, DoubleSupplier valueSupplier) {
|
||||
return m_helper.addDouble(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Float> addFloat(String title, FloatSupplier valueSupplier) {
|
||||
return m_helper.addFloat(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Long> addInteger(String title, LongSupplier valueSupplier) {
|
||||
return m_helper.addInteger(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Boolean> addBoolean(String title, BooleanSupplier valueSupplier) {
|
||||
return m_helper.addBoolean(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<String[]> addStringArray(
|
||||
String title, Supplier<String[]> valueSupplier) {
|
||||
return m_helper.addStringArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<double[]> addDoubleArray(
|
||||
String title, Supplier<double[]> valueSupplier) {
|
||||
return m_helper.addDoubleArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<float[]> addFloatArray(String title, Supplier<float[]> valueSupplier) {
|
||||
return m_helper.addFloatArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<long[]> addIntegerArray(String title, Supplier<long[]> valueSupplier) {
|
||||
return m_helper.addIntegerArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<boolean[]> addBooleanArray(
|
||||
String title, Supplier<boolean[]> valueSupplier) {
|
||||
return m_helper.addBooleanArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<byte[]> addRaw(
|
||||
String title, String typeString, Supplier<byte[]> valueSupplier) {
|
||||
return m_helper.addRaw(title, typeString, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildInto(NetworkTable parentTable, NetworkTable metaTable) {
|
||||
NetworkTable tabTable = parentTable.getSubTable(m_title);
|
||||
tabTable.getEntry(".type").setString(kSmartDashboardType);
|
||||
tabTable
|
||||
.getEntry(".type")
|
||||
.getTopic()
|
||||
.setProperty("SmartDashboard", "\"" + kSmartDashboardType + "\"");
|
||||
for (ShuffleboardComponent<?> component : m_helper.getComponents()) {
|
||||
component.buildInto(tabTable, metaTable.getSubTable(component.getTitle()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
|
||||
interface ShuffleboardValue {
|
||||
/**
|
||||
* Gets the title of this Shuffleboard value.
|
||||
*
|
||||
* @return The title of this Shuffleboard value.
|
||||
*/
|
||||
String getTitle();
|
||||
|
||||
/**
|
||||
* Builds the entries for this value.
|
||||
*
|
||||
* @param parentTable the table containing all the data for the parent. Values that require a
|
||||
* complex entry or table structure should call {@code parentTable.getSubTable(getTitle())} to
|
||||
* get the table to put data into. Values that only use a single entry should call {@code
|
||||
* parentTable.getEntry(getTitle())} to get that entry.
|
||||
* @param metaTable the table containing all the metadata for this value and its sub-values
|
||||
*/
|
||||
void buildInto(NetworkTable parentTable, NetworkTable metaTable);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
/**
|
||||
* Abstract superclass for widgets.
|
||||
*
|
||||
* <p>This class is package-private to minimize API surface area.
|
||||
*
|
||||
* @param <W> the self type
|
||||
*/
|
||||
abstract class ShuffleboardWidget<W extends ShuffleboardWidget<W>>
|
||||
extends ShuffleboardComponent<W> {
|
||||
ShuffleboardWidget(ShuffleboardContainer parent, String title) {
|
||||
super(parent, title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of widget used to display the data. If not set, the default widget type will be
|
||||
* used.
|
||||
*
|
||||
* @param widgetType the type of the widget used to display the data
|
||||
* @return this widget object
|
||||
* @see BuiltInWidgets
|
||||
*/
|
||||
public final W withWidget(WidgetType widgetType) {
|
||||
return withWidget(widgetType.getWidgetName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of widget used to display the data. If not set, the default widget type will be
|
||||
* used. This method should only be used to use a widget that does not come built into
|
||||
* Shuffleboard (i.e. one that comes with a custom or third-party plugin). To use a widget that is
|
||||
* built into Shuffleboard, use {@link #withWidget(WidgetType)} and {@link BuiltInWidgets}.
|
||||
*
|
||||
* @param widgetType the type of the widget used to display the data
|
||||
* @return this widget object
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final W withWidget(String widgetType) {
|
||||
setType(widgetType);
|
||||
return (W) this;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.GenericEntry;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
|
||||
/** A Shuffleboard widget that handles a single data point such as a number or string. */
|
||||
public final class SimpleWidget extends ShuffleboardWidget<SimpleWidget> implements AutoCloseable {
|
||||
private String m_typeString = "";
|
||||
private GenericEntry m_entry;
|
||||
|
||||
SimpleWidget(ShuffleboardContainer parent, String title) {
|
||||
super(parent, title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the NetworkTable entry that contains the data for this widget.
|
||||
*
|
||||
* @return The NetworkTable entry that contains the data for this widget.
|
||||
*/
|
||||
public GenericEntry getEntry() {
|
||||
if (m_entry == null) {
|
||||
forceGenerate();
|
||||
}
|
||||
return m_entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the NetworkTable entry that contains the data for this widget.
|
||||
*
|
||||
* @param typeString NetworkTable type string
|
||||
* @return The NetworkTable entry that contains the data for this widget.
|
||||
*/
|
||||
public GenericEntry getEntry(String typeString) {
|
||||
if (m_entry == null) {
|
||||
m_typeString = typeString;
|
||||
forceGenerate();
|
||||
}
|
||||
return m_entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (m_entry != null) {
|
||||
m_entry.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildInto(NetworkTable parentTable, NetworkTable metaTable) {
|
||||
buildMetadata(metaTable);
|
||||
if (m_entry == null) {
|
||||
m_entry = parentTable.getTopic(getTitle()).getGenericEntry(m_typeString);
|
||||
}
|
||||
}
|
||||
|
||||
private void forceGenerate() {
|
||||
ShuffleboardContainer parent = getParent();
|
||||
while (parent instanceof ShuffleboardLayout layout) {
|
||||
parent = layout.getParent();
|
||||
}
|
||||
ShuffleboardTab tab = (ShuffleboardTab) parent;
|
||||
tab.getRoot().update();
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.BooleanPublisher;
|
||||
import edu.wpi.first.networktables.BooleanTopic;
|
||||
import edu.wpi.first.networktables.GenericPublisher;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* A Shuffleboard widget whose value is provided by user code.
|
||||
*
|
||||
* @param <T> the type of values in the widget
|
||||
*/
|
||||
public final class SuppliedValueWidget<T> extends ShuffleboardWidget<SuppliedValueWidget<T>>
|
||||
implements AutoCloseable {
|
||||
private final String m_typeString;
|
||||
private final Supplier<T> m_supplier;
|
||||
private final BiConsumer<GenericPublisher, T> m_setter;
|
||||
private BooleanPublisher m_controllablePub;
|
||||
private GenericPublisher m_entry;
|
||||
|
||||
/**
|
||||
* Package-private constructor for use by the Shuffleboard API.
|
||||
*
|
||||
* @param parent the parent container for the widget
|
||||
* @param title the title of the widget
|
||||
* @param typeString the NetworkTables string type
|
||||
* @param supplier the supplier for values to place in the NetworkTable entry
|
||||
* @param setter the function for placing values in the NetworkTable entry
|
||||
*/
|
||||
SuppliedValueWidget(
|
||||
ShuffleboardContainer parent,
|
||||
String title,
|
||||
String typeString,
|
||||
Supplier<T> supplier,
|
||||
BiConsumer<GenericPublisher, T> setter) {
|
||||
super(parent, title);
|
||||
m_typeString = typeString;
|
||||
m_supplier = supplier;
|
||||
m_setter = setter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildInto(NetworkTable parentTable, NetworkTable metaTable) {
|
||||
buildMetadata(metaTable);
|
||||
if (m_controllablePub == null) {
|
||||
m_controllablePub = new BooleanTopic(metaTable.getTopic("Controllable")).publish();
|
||||
m_controllablePub.set(false);
|
||||
}
|
||||
|
||||
if (m_entry == null) {
|
||||
m_entry = parentTable.getTopic(getTitle()).genericPublish(m_typeString);
|
||||
}
|
||||
m_setter.accept(m_entry, m_supplier.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (m_controllablePub != null) {
|
||||
m_controllablePub.close();
|
||||
}
|
||||
if (m_entry != null) {
|
||||
m_entry.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
/**
|
||||
* Represents the type of a widget in Shuffleboard. Using this is preferred over specifying raw
|
||||
* strings, to avoid typos and having to know or look up the exact string name for a desired widget.
|
||||
*
|
||||
* @see BuiltInWidgets the built-in widget types
|
||||
*/
|
||||
public interface WidgetType {
|
||||
/**
|
||||
* Gets the string type of the widget as defined by that widget in Shuffleboard.
|
||||
*
|
||||
* @return The string type of the widget.
|
||||
*/
|
||||
String getWidgetName();
|
||||
}
|
||||
Reference in New Issue
Block a user