mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-23 01:21:42 +00:00
[ntcore] NetworkTables 4 (#3217)
This commit is contained in:
@@ -10,9 +10,10 @@ import edu.wpi.first.hal.HAL;
|
||||
import edu.wpi.first.hal.SimDevice;
|
||||
import edu.wpi.first.hal.SimDouble;
|
||||
import edu.wpi.first.hal.SimEnum;
|
||||
import edu.wpi.first.networktables.DoublePublisher;
|
||||
import edu.wpi.first.networktables.DoubleTopic;
|
||||
import edu.wpi.first.networktables.NTSendable;
|
||||
import edu.wpi.first.networktables.NTSendableBuilder;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.util.sendable.SendableRegistry;
|
||||
import edu.wpi.first.wpilibj.interfaces.Accelerometer;
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -231,15 +232,18 @@ public class ADXL345_I2C implements Accelerometer, NTSendable, AutoCloseable {
|
||||
@Override
|
||||
public void initSendable(NTSendableBuilder builder) {
|
||||
builder.setSmartDashboardType("3AxisAccelerometer");
|
||||
NetworkTableEntry entryX = builder.getEntry("X");
|
||||
NetworkTableEntry entryY = builder.getEntry("Y");
|
||||
NetworkTableEntry entryZ = builder.getEntry("Z");
|
||||
DoublePublisher pubX = new DoubleTopic(builder.getTopic("X")).publish();
|
||||
DoublePublisher pubY = new DoubleTopic(builder.getTopic("Y")).publish();
|
||||
DoublePublisher pubZ = new DoubleTopic(builder.getTopic("Z")).publish();
|
||||
builder.addCloseable(pubX);
|
||||
builder.addCloseable(pubY);
|
||||
builder.addCloseable(pubZ);
|
||||
builder.setUpdateTable(
|
||||
() -> {
|
||||
AllAxes data = getAccelerations();
|
||||
entryX.setDouble(data.XAxis);
|
||||
entryY.setDouble(data.YAxis);
|
||||
entryZ.setDouble(data.ZAxis);
|
||||
pubX.set(data.XAxis);
|
||||
pubY.set(data.YAxis);
|
||||
pubZ.set(data.ZAxis);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,10 @@ import edu.wpi.first.hal.HAL;
|
||||
import edu.wpi.first.hal.SimDevice;
|
||||
import edu.wpi.first.hal.SimDouble;
|
||||
import edu.wpi.first.hal.SimEnum;
|
||||
import edu.wpi.first.networktables.DoublePublisher;
|
||||
import edu.wpi.first.networktables.DoubleTopic;
|
||||
import edu.wpi.first.networktables.NTSendable;
|
||||
import edu.wpi.first.networktables.NTSendableBuilder;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.util.sendable.SendableRegistry;
|
||||
import edu.wpi.first.wpilibj.interfaces.Accelerometer;
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -234,15 +235,18 @@ public class ADXL345_SPI implements Accelerometer, NTSendable, AutoCloseable {
|
||||
@Override
|
||||
public void initSendable(NTSendableBuilder builder) {
|
||||
builder.setSmartDashboardType("3AxisAccelerometer");
|
||||
NetworkTableEntry entryX = builder.getEntry("X");
|
||||
NetworkTableEntry entryY = builder.getEntry("Y");
|
||||
NetworkTableEntry entryZ = builder.getEntry("Z");
|
||||
DoublePublisher pubX = new DoubleTopic(builder.getTopic("X")).publish();
|
||||
DoublePublisher pubY = new DoubleTopic(builder.getTopic("Y")).publish();
|
||||
DoublePublisher pubZ = new DoubleTopic(builder.getTopic("Z")).publish();
|
||||
builder.addCloseable(pubX);
|
||||
builder.addCloseable(pubY);
|
||||
builder.addCloseable(pubZ);
|
||||
builder.setUpdateTable(
|
||||
() -> {
|
||||
AllAxes data = getAccelerations();
|
||||
entryX.setDouble(data.XAxis);
|
||||
entryY.setDouble(data.YAxis);
|
||||
entryZ.setDouble(data.ZAxis);
|
||||
pubX.set(data.XAxis);
|
||||
pubY.set(data.YAxis);
|
||||
pubZ.set(data.ZAxis);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,10 @@ import edu.wpi.first.hal.HAL;
|
||||
import edu.wpi.first.hal.SimDevice;
|
||||
import edu.wpi.first.hal.SimDouble;
|
||||
import edu.wpi.first.hal.SimEnum;
|
||||
import edu.wpi.first.networktables.DoublePublisher;
|
||||
import edu.wpi.first.networktables.DoubleTopic;
|
||||
import edu.wpi.first.networktables.NTSendable;
|
||||
import edu.wpi.first.networktables.NTSendableBuilder;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.util.sendable.SendableRegistry;
|
||||
import edu.wpi.first.wpilibj.interfaces.Accelerometer;
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -263,15 +264,18 @@ public class ADXL362 implements Accelerometer, NTSendable, AutoCloseable {
|
||||
@Override
|
||||
public void initSendable(NTSendableBuilder builder) {
|
||||
builder.setSmartDashboardType("3AxisAccelerometer");
|
||||
NetworkTableEntry entryX = builder.getEntry("X");
|
||||
NetworkTableEntry entryY = builder.getEntry("Y");
|
||||
NetworkTableEntry entryZ = builder.getEntry("Z");
|
||||
DoublePublisher pubX = new DoubleTopic(builder.getTopic("X")).publish();
|
||||
DoublePublisher pubY = new DoubleTopic(builder.getTopic("Y")).publish();
|
||||
DoublePublisher pubZ = new DoubleTopic(builder.getTopic("Z")).publish();
|
||||
builder.addCloseable(pubX);
|
||||
builder.addCloseable(pubY);
|
||||
builder.addCloseable(pubZ);
|
||||
builder.setUpdateTable(
|
||||
() -> {
|
||||
AllAxes data = getAccelerations();
|
||||
entryX.setDouble(data.XAxis);
|
||||
entryY.setDouble(data.YAxis);
|
||||
entryZ.setDouble(data.ZAxis);
|
||||
pubX.set(data.XAxis);
|
||||
pubY.set(data.YAxis);
|
||||
pubZ.set(data.ZAxis);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,11 @@ import edu.wpi.first.hal.AllianceStationID;
|
||||
import edu.wpi.first.hal.ControlWord;
|
||||
import edu.wpi.first.hal.HAL;
|
||||
import edu.wpi.first.hal.MatchInfoData;
|
||||
import edu.wpi.first.networktables.BooleanPublisher;
|
||||
import edu.wpi.first.networktables.IntegerPublisher;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.networktables.StringPublisher;
|
||||
import edu.wpi.first.util.WPIUtilJNI;
|
||||
import edu.wpi.first.util.datalog.BooleanArrayLogEntry;
|
||||
import edu.wpi.first.util.datalog.BooleanLogEntry;
|
||||
@@ -83,15 +85,15 @@ public final class DriverStation {
|
||||
@SuppressWarnings("MemberName")
|
||||
private static class MatchDataSender {
|
||||
NetworkTable table;
|
||||
NetworkTableEntry typeMetadata;
|
||||
NetworkTableEntry gameSpecificMessage;
|
||||
NetworkTableEntry eventName;
|
||||
NetworkTableEntry matchNumber;
|
||||
NetworkTableEntry replayNumber;
|
||||
NetworkTableEntry matchType;
|
||||
NetworkTableEntry alliance;
|
||||
NetworkTableEntry station;
|
||||
NetworkTableEntry controlWord;
|
||||
StringPublisher typeMetadata;
|
||||
StringPublisher gameSpecificMessage;
|
||||
StringPublisher eventName;
|
||||
IntegerPublisher matchNumber;
|
||||
IntegerPublisher replayNumber;
|
||||
IntegerPublisher matchType;
|
||||
BooleanPublisher alliance;
|
||||
IntegerPublisher station;
|
||||
IntegerPublisher controlWord;
|
||||
boolean oldIsRedAlliance = true;
|
||||
int oldStationNumber = 1;
|
||||
String oldEventName = "";
|
||||
@@ -103,24 +105,24 @@ public final class DriverStation {
|
||||
|
||||
MatchDataSender() {
|
||||
table = NetworkTableInstance.getDefault().getTable("FMSInfo");
|
||||
typeMetadata = table.getEntry(".type");
|
||||
typeMetadata.forceSetString("FMSInfo");
|
||||
gameSpecificMessage = table.getEntry("GameSpecificMessage");
|
||||
gameSpecificMessage.forceSetString("");
|
||||
eventName = table.getEntry("EventName");
|
||||
eventName.forceSetString("");
|
||||
matchNumber = table.getEntry("MatchNumber");
|
||||
matchNumber.forceSetDouble(0);
|
||||
replayNumber = table.getEntry("ReplayNumber");
|
||||
replayNumber.forceSetDouble(0);
|
||||
matchType = table.getEntry("MatchType");
|
||||
matchType.forceSetDouble(0);
|
||||
alliance = table.getEntry("IsRedAlliance");
|
||||
alliance.forceSetBoolean(true);
|
||||
station = table.getEntry("StationNumber");
|
||||
station.forceSetDouble(1);
|
||||
controlWord = table.getEntry("FMSControlData");
|
||||
controlWord.forceSetDouble(0);
|
||||
typeMetadata = table.getStringTopic(".type").publish();
|
||||
typeMetadata.set("FMSInfo");
|
||||
gameSpecificMessage = table.getStringTopic("GameSpecificMessage").publish();
|
||||
gameSpecificMessage.set("");
|
||||
eventName = table.getStringTopic("EventName").publish();
|
||||
eventName.set("");
|
||||
matchNumber = table.getIntegerTopic("MatchNumber").publish();
|
||||
matchNumber.set(0);
|
||||
replayNumber = table.getIntegerTopic("ReplayNumber").publish();
|
||||
replayNumber.set(0);
|
||||
matchType = table.getIntegerTopic("MatchType").publish();
|
||||
matchType.set(0);
|
||||
alliance = table.getBooleanTopic("IsRedAlliance").publish();
|
||||
alliance.set(true);
|
||||
station = table.getIntegerTopic("StationNumber").publish();
|
||||
station.set(1);
|
||||
controlWord = table.getIntegerTopic("FMSControlData").publish();
|
||||
controlWord.set(0);
|
||||
}
|
||||
|
||||
private void sendMatchData() {
|
||||
@@ -173,35 +175,35 @@ public final class DriverStation {
|
||||
currentControlWord = HAL.nativeGetControlWord();
|
||||
|
||||
if (oldIsRedAlliance != isRedAlliance) {
|
||||
alliance.setBoolean(isRedAlliance);
|
||||
alliance.set(isRedAlliance);
|
||||
oldIsRedAlliance = isRedAlliance;
|
||||
}
|
||||
if (oldStationNumber != stationNumber) {
|
||||
station.setDouble(stationNumber);
|
||||
station.set(stationNumber);
|
||||
oldStationNumber = stationNumber;
|
||||
}
|
||||
if (!oldEventName.equals(currentEventName)) {
|
||||
eventName.setString(currentEventName);
|
||||
eventName.set(currentEventName);
|
||||
oldEventName = currentEventName;
|
||||
}
|
||||
if (!oldGameSpecificMessage.equals(currentGameSpecificMessage)) {
|
||||
gameSpecificMessage.setString(currentGameSpecificMessage);
|
||||
gameSpecificMessage.set(currentGameSpecificMessage);
|
||||
oldGameSpecificMessage = currentGameSpecificMessage;
|
||||
}
|
||||
if (currentMatchNumber != oldMatchNumber) {
|
||||
matchNumber.setDouble(currentMatchNumber);
|
||||
matchNumber.set(currentMatchNumber);
|
||||
oldMatchNumber = currentMatchNumber;
|
||||
}
|
||||
if (currentReplayNumber != oldReplayNumber) {
|
||||
replayNumber.setDouble(currentReplayNumber);
|
||||
replayNumber.set(currentReplayNumber);
|
||||
oldReplayNumber = currentReplayNumber;
|
||||
}
|
||||
if (currentMatchType != oldMatchType) {
|
||||
matchType.setDouble(currentMatchType);
|
||||
matchType.set(currentMatchType);
|
||||
oldMatchType = currentMatchType;
|
||||
}
|
||||
if (currentControlWord != oldControlWord) {
|
||||
controlWord.setDouble(currentControlWord);
|
||||
controlWord.set(currentControlWord);
|
||||
oldControlWord = currentControlWord;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
||||
private Mode m_lastMode = Mode.kNone;
|
||||
private final double m_period;
|
||||
private final Watchdog m_watchdog;
|
||||
private boolean m_ntFlushEnabled;
|
||||
private boolean m_ntFlushEnabled = true;
|
||||
|
||||
/**
|
||||
* Constructor for IterativeRobotBase.
|
||||
@@ -235,7 +235,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
||||
public void testExit() {}
|
||||
|
||||
/**
|
||||
* Enables or disables flushing NetworkTables every loop iteration. By default, this is disabled.
|
||||
* Enables or disables flushing NetworkTables every loop iteration. By default, this is enabled.
|
||||
*
|
||||
* @param enabled True to enable, false to disable
|
||||
*/
|
||||
@@ -343,7 +343,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
||||
|
||||
// Flush NetworkTables
|
||||
if (m_ntFlushEnabled) {
|
||||
NetworkTableInstance.getDefault().flush();
|
||||
NetworkTableInstance.getDefault().flushLocal();
|
||||
}
|
||||
|
||||
// Warn on loop time overruns
|
||||
|
||||
@@ -8,10 +8,13 @@ import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam;
|
||||
|
||||
import edu.wpi.first.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.hal.HAL;
|
||||
import edu.wpi.first.networktables.EntryListenerFlags;
|
||||
import edu.wpi.first.networktables.MultiSubscriber;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.networktables.StringPublisher;
|
||||
import edu.wpi.first.networktables.TopicListener;
|
||||
import edu.wpi.first.networktables.TopicListenerFlags;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
@@ -31,20 +34,51 @@ public final class Preferences {
|
||||
/** The Preferences table name. */
|
||||
private static final String TABLE_NAME = "Preferences";
|
||||
/** The network table. */
|
||||
private static final NetworkTable m_table;
|
||||
private static NetworkTable m_table;
|
||||
|
||||
private static StringPublisher m_typePublisher;
|
||||
private static MultiSubscriber m_tableSubscriber;
|
||||
private static TopicListener m_listener;
|
||||
|
||||
/** Creates a preference class. */
|
||||
private Preferences() {}
|
||||
|
||||
static {
|
||||
m_table = NetworkTableInstance.getDefault().getTable(TABLE_NAME);
|
||||
m_table.getEntry(".type").setString("RobotPreferences");
|
||||
setNetworkTableInstance(NetworkTableInstance.getDefault());
|
||||
HAL.report(tResourceType.kResourceType_Preferences, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the NetworkTable instance used for entries. For testing purposes; use with caution.
|
||||
*
|
||||
* @param inst NetworkTable instance
|
||||
*/
|
||||
public static synchronized void setNetworkTableInstance(NetworkTableInstance inst) {
|
||||
m_table = inst.getTable(TABLE_NAME);
|
||||
if (m_typePublisher != null) {
|
||||
m_typePublisher.close();
|
||||
}
|
||||
m_typePublisher = m_table.getStringTopic(".type").publish();
|
||||
m_typePublisher.set("RobotPreferences");
|
||||
|
||||
// Subscribe to all Preferences; this ensures we get the latest values
|
||||
// ahead of a getter call.
|
||||
if (m_tableSubscriber != null) {
|
||||
m_tableSubscriber.close();
|
||||
}
|
||||
m_tableSubscriber = new MultiSubscriber(inst, new String[] {m_table.getPath() + "/"});
|
||||
|
||||
// Listener to set all Preferences values to persistent
|
||||
// (for backwards compatibility with old dashboards).
|
||||
m_table.addEntryListener(
|
||||
(table, key, entry, value, flags) -> entry.setPersistent(),
|
||||
EntryListenerFlags.kImmediate | EntryListenerFlags.kNew);
|
||||
HAL.report(tResourceType.kResourceType_Preferences, 0);
|
||||
if (m_listener != null) {
|
||||
m_listener.close();
|
||||
}
|
||||
m_listener =
|
||||
new TopicListener(
|
||||
m_table.getInstance(),
|
||||
new String[] {m_table.getPath() + "/"},
|
||||
TopicListenerFlags.kImmediate | TopicListenerFlags.kPublish,
|
||||
event -> event.info.getTopic().setPersistent(true));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,7 +253,9 @@ public final class Preferences {
|
||||
* @param key the key
|
||||
*/
|
||||
public static void remove(String key) {
|
||||
m_table.delete(key);
|
||||
NetworkTableEntry entry = m_table.getEntry(key);
|
||||
entry.clearPersistent();
|
||||
entry.unpublish();
|
||||
}
|
||||
|
||||
/** Remove all preferences. */
|
||||
|
||||
@@ -14,6 +14,7 @@ import edu.wpi.first.hal.HALUtil;
|
||||
import edu.wpi.first.math.MathShared;
|
||||
import edu.wpi.first.math.MathSharedStore;
|
||||
import edu.wpi.first.math.MathUsageId;
|
||||
import edu.wpi.first.networktables.MultiSubscriber;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
|
||||
@@ -38,6 +39,8 @@ public abstract class RobotBase implements AutoCloseable {
|
||||
// This is usually 1, but it is best to make sure
|
||||
private static long m_threadId = -1;
|
||||
|
||||
private final MultiSubscriber m_suball;
|
||||
|
||||
private static void setupCameraServerShared() {
|
||||
CameraServerShared shared =
|
||||
new CameraServerShared() {
|
||||
@@ -142,12 +145,27 @@ public abstract class RobotBase implements AutoCloseable {
|
||||
setupCameraServerShared();
|
||||
setupMathShared();
|
||||
inst.setNetworkIdentity("Robot");
|
||||
// subscribe to "" to force persistent values to progagate to local
|
||||
m_suball = new MultiSubscriber(inst, new String[] {""});
|
||||
if (isReal()) {
|
||||
inst.startServer("/home/lvuser/networktables.ini");
|
||||
inst.startServer("/home/lvuser/networktables.json");
|
||||
} else {
|
||||
inst.startServer();
|
||||
}
|
||||
inst.getTable("LiveWindow").getSubTable(".status").getEntry("LW Enabled").setBoolean(false);
|
||||
|
||||
// wait for the NT server to actually start
|
||||
try {
|
||||
int count = 0;
|
||||
while ((inst.getNetworkMode() & NetworkTableInstance.kNetModeStarting) != 0) {
|
||||
Thread.sleep(10);
|
||||
count++;
|
||||
if (count > 100) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
System.err.println("timed out while waiting for NT server to start");
|
||||
}
|
||||
|
||||
LiveWindow.setEnabled(false);
|
||||
Shuffleboard.disableActuatorWidgets();
|
||||
@@ -158,7 +176,9 @@ public abstract class RobotBase implements AutoCloseable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {}
|
||||
public void close() {
|
||||
m_suball.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current runtime type.
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// 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.event;
|
||||
|
||||
import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam;
|
||||
|
||||
import edu.wpi.first.networktables.BooleanSubscriber;
|
||||
import edu.wpi.first.networktables.BooleanTopic;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
|
||||
/** This class provides an easy way to link NetworkTables boolean topics to callback actions. */
|
||||
public class NetworkBooleanEvent extends BooleanEvent {
|
||||
/**
|
||||
* Creates a new event with the given boolean topic determining whether it is active.
|
||||
*
|
||||
* @param loop the loop that polls this event
|
||||
* @param topic The boolean topic that contains the value
|
||||
*/
|
||||
public NetworkBooleanEvent(EventLoop loop, BooleanTopic topic) {
|
||||
this(loop, topic.subscribe(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new event with the given boolean subscriber determining whether it is active.
|
||||
*
|
||||
* @param loop the loop that polls this event
|
||||
* @param sub The boolean subscriber that provides the value
|
||||
*/
|
||||
public NetworkBooleanEvent(EventLoop loop, BooleanSubscriber sub) {
|
||||
super(loop, () -> sub.getTopic().getInstance().isConnected() && sub.get());
|
||||
requireNonNullParam(sub, "sub", "NetworkBooleanEvent");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new event with the given boolean topic determining whether it is active.
|
||||
*
|
||||
* @param loop the loop that polls this event
|
||||
* @param table The NetworkTable that contains the topic
|
||||
* @param topicName The topic name within the table that contains the value
|
||||
*/
|
||||
public NetworkBooleanEvent(EventLoop loop, NetworkTable table, String topicName) {
|
||||
this(loop, table.getBooleanTopic(topicName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new event with the given boolean topic determining whether it is active.
|
||||
*
|
||||
* @param loop the loop that polls this event
|
||||
* @param tableName The NetworkTable name that contains the topic
|
||||
* @param topicName The topic name within the table that contains the value
|
||||
*/
|
||||
public NetworkBooleanEvent(EventLoop loop, String tableName, String topicName) {
|
||||
this(loop, NetworkTableInstance.getDefault(), tableName, topicName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new event with the given boolean topic determining whether it is active.
|
||||
*
|
||||
* @param loop the loop that polls this event
|
||||
* @param inst The NetworkTable instance to use
|
||||
* @param tableName The NetworkTable that contains the topic
|
||||
* @param topicName The topic name within the table that contains the value
|
||||
*/
|
||||
public NetworkBooleanEvent(
|
||||
EventLoop loop, NetworkTableInstance inst, String tableName, String topicName) {
|
||||
this(loop, inst.getTable(tableName), topicName);
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,11 @@
|
||||
|
||||
package edu.wpi.first.wpilibj.livewindow;
|
||||
|
||||
import edu.wpi.first.networktables.BooleanPublisher;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.networktables.StringPublisher;
|
||||
import edu.wpi.first.networktables.StringTopic;
|
||||
import edu.wpi.first.util.sendable.Sendable;
|
||||
import edu.wpi.first.util.sendable.SendableRegistry;
|
||||
import edu.wpi.first.wpilibj.smartdashboard.SendableBuilderImpl;
|
||||
@@ -15,16 +17,31 @@ import edu.wpi.first.wpilibj.smartdashboard.SendableBuilderImpl;
|
||||
* The LiveWindow class is the public interface for putting sensors and actuators on the LiveWindow.
|
||||
*/
|
||||
public final class LiveWindow {
|
||||
private static class Component {
|
||||
private static class Component implements AutoCloseable {
|
||||
@Override
|
||||
public void close() {
|
||||
if (m_namePub != null) {
|
||||
m_namePub.close();
|
||||
m_namePub = null;
|
||||
}
|
||||
if (m_typePub != null) {
|
||||
m_typePub.close();
|
||||
m_typePub = null;
|
||||
}
|
||||
}
|
||||
|
||||
boolean m_firstTime = true;
|
||||
boolean m_telemetryEnabled;
|
||||
StringPublisher m_namePub;
|
||||
StringPublisher m_typePub;
|
||||
}
|
||||
|
||||
private static final int dataHandle = SendableRegistry.getDataHandle();
|
||||
private static final NetworkTable liveWindowTable =
|
||||
NetworkTableInstance.getDefault().getTable("LiveWindow");
|
||||
private static final NetworkTable statusTable = liveWindowTable.getSubTable(".status");
|
||||
private static final NetworkTableEntry enabledEntry = statusTable.getEntry("LW Enabled");
|
||||
private static final BooleanPublisher enabledPub =
|
||||
statusTable.getBooleanTopic("LW Enabled").publish();
|
||||
private static boolean startLiveWindow;
|
||||
private static boolean liveWindowEnabled;
|
||||
private static boolean telemetryEnabled;
|
||||
@@ -34,6 +51,7 @@ public final class LiveWindow {
|
||||
|
||||
static {
|
||||
SendableRegistry.setLiveWindowBuilderFactory(() -> new SendableBuilderImpl());
|
||||
enabledPub.set(false);
|
||||
}
|
||||
|
||||
private static Component getOrAdd(Sendable sendable) {
|
||||
@@ -95,7 +113,7 @@ public final class LiveWindow {
|
||||
disabledListener.run();
|
||||
}
|
||||
}
|
||||
enabledEntry.setBoolean(enabled);
|
||||
enabledPub.set(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,10 +195,12 @@ public final class LiveWindow {
|
||||
} else {
|
||||
table = ssTable.getSubTable(cbdata.name);
|
||||
}
|
||||
table.getEntry(".name").setString(cbdata.name);
|
||||
component.m_namePub = new StringTopic(table.getTopic(".name")).publish();
|
||||
component.m_namePub.set(cbdata.name);
|
||||
((SendableBuilderImpl) cbdata.builder).setTable(table);
|
||||
cbdata.sendable.initSendable(cbdata.builder);
|
||||
ssTable.getEntry(".type").setString("LW Subsystem");
|
||||
component.m_typePub = new StringTopic(ssTable.getTopic(".type")).publish();
|
||||
component.m_typePub.set("LW Subsystem");
|
||||
|
||||
component.m_firstTime = false;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import edu.wpi.first.wpilibj.interfaces.Accelerometer.Range;
|
||||
* <p>For example, setting a number to be displayed with a slider:
|
||||
*
|
||||
* <pre>{@code
|
||||
* NetworkTableEntry example = Shuffleboard.getTab("My Tab")
|
||||
* GenericEntry example = Shuffleboard.getTab("My Tab")
|
||||
* .add("My Number", 0)
|
||||
* .withWidget(BuiltInWidgets.kNumberSlider)
|
||||
* .withProperties(Map.of("min", 0, "max", 1))
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
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;
|
||||
@@ -18,6 +21,7 @@ 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. */
|
||||
@@ -68,6 +72,11 @@ final class ContainerHelper {
|
||||
}
|
||||
|
||||
SimpleWidget add(String title, Object defaultValue) {
|
||||
Objects.requireNonNull(defaultValue, "Default value cannot be null");
|
||||
return add(title, NetworkTableType.getStringFromObject(defaultValue), defaultValue);
|
||||
}
|
||||
|
||||
SimpleWidget add(String title, String typeString, Object defaultValue) {
|
||||
Objects.requireNonNull(title, "Title cannot be null");
|
||||
Objects.requireNonNull(defaultValue, "Default value cannot be null");
|
||||
checkTitle(title);
|
||||
@@ -75,43 +84,72 @@ final class ContainerHelper {
|
||||
|
||||
SimpleWidget widget = new SimpleWidget(m_container, title);
|
||||
m_components.add(widget);
|
||||
widget.getEntry().setDefaultValue(defaultValue);
|
||||
widget.getEntry(typeString).setDefaultValue(defaultValue);
|
||||
return widget;
|
||||
}
|
||||
|
||||
SuppliedValueWidget<String> addString(String title, Supplier<String> valueSupplier) {
|
||||
precheck(title, valueSupplier);
|
||||
return addSupplied(title, valueSupplier, NetworkTableEntry::setString);
|
||||
return addSupplied(title, "string", valueSupplier, GenericPublisher::setString);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<Double> addNumber(String title, DoubleSupplier valueSupplier) {
|
||||
return addDouble(title, valueSupplier);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<Double> addDouble(String title, DoubleSupplier valueSupplier) {
|
||||
precheck(title, valueSupplier);
|
||||
return addSupplied(title, valueSupplier::getAsDouble, NetworkTableEntry::setDouble);
|
||||
return addSupplied(title, "double", valueSupplier::getAsDouble, GenericPublisher::setDouble);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<Float> addFloat(String title, FloatSupplier valueSupplier) {
|
||||
precheck(title, valueSupplier);
|
||||
return addSupplied(title, "float", valueSupplier::getAsFloat, GenericPublisher::setFloat);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<Long> addInteger(String title, LongSupplier valueSupplier) {
|
||||
precheck(title, valueSupplier);
|
||||
return addSupplied(title, "int", valueSupplier::getAsLong, GenericPublisher::setInteger);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<Boolean> addBoolean(String title, BooleanSupplier valueSupplier) {
|
||||
precheck(title, valueSupplier);
|
||||
return addSupplied(title, valueSupplier::getAsBoolean, NetworkTableEntry::setBoolean);
|
||||
return addSupplied(title, "boolean", valueSupplier::getAsBoolean, GenericPublisher::setBoolean);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<String[]> addStringArray(String title, Supplier<String[]> valueSupplier) {
|
||||
precheck(title, valueSupplier);
|
||||
return addSupplied(title, valueSupplier, NetworkTableEntry::setStringArray);
|
||||
return addSupplied(title, "string[]", valueSupplier, GenericPublisher::setStringArray);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<double[]> addDoubleArray(String title, Supplier<double[]> valueSupplier) {
|
||||
precheck(title, valueSupplier);
|
||||
return addSupplied(title, valueSupplier, NetworkTableEntry::setDoubleArray);
|
||||
return addSupplied(title, "double[]", valueSupplier, GenericPublisher::setDoubleArray);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<float[]> addFloatArray(String title, Supplier<float[]> valueSupplier) {
|
||||
precheck(title, valueSupplier);
|
||||
return addSupplied(title, "float[]", valueSupplier, GenericPublisher::setFloatArray);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<long[]> addIntegerArray(String title, Supplier<long[]> valueSupplier) {
|
||||
precheck(title, valueSupplier);
|
||||
return addSupplied(title, "int[]", valueSupplier, GenericPublisher::setIntegerArray);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<boolean[]> addBooleanArray(String title, Supplier<boolean[]> valueSupplier) {
|
||||
precheck(title, valueSupplier);
|
||||
return addSupplied(title, valueSupplier, NetworkTableEntry::setBooleanArray);
|
||||
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);
|
||||
return addSupplied(title, valueSupplier, NetworkTableEntry::setRaw);
|
||||
return addSupplied(title, typeString, valueSupplier, GenericPublisher::setRaw);
|
||||
}
|
||||
|
||||
private void precheck(String title, Object valueSupplier) {
|
||||
@@ -121,8 +159,12 @@ final class ContainerHelper {
|
||||
}
|
||||
|
||||
private <T> SuppliedValueWidget<T> addSupplied(
|
||||
String title, Supplier<T> supplier, BiConsumer<NetworkTableEntry, T> setter) {
|
||||
SuppliedValueWidget<T> widget = new SuppliedValueWidget<>(m_container, title, supplier, setter);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.BooleanPublisher;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.networktables.StringPublisher;
|
||||
import edu.wpi.first.wpilibj.DriverStation;
|
||||
|
||||
/** Controls Shuffleboard recordings via NetworkTables. */
|
||||
@@ -16,30 +17,31 @@ final class RecordingController {
|
||||
private static final String kRecordingFileNameFormatKey = kRecordingTableName + "FileNameFormat";
|
||||
private static final String kEventMarkerTableName = kRecordingTableName + "events";
|
||||
|
||||
private final NetworkTableEntry m_recordingControlEntry;
|
||||
private final NetworkTableEntry m_recordingFileNameFormatEntry;
|
||||
private final BooleanPublisher m_recordingControlEntry;
|
||||
private final StringPublisher m_recordingFileNameFormatEntry;
|
||||
private final NetworkTable m_eventsTable;
|
||||
|
||||
RecordingController(NetworkTableInstance ntInstance) {
|
||||
m_recordingControlEntry = ntInstance.getEntry(kRecordingControlKey);
|
||||
m_recordingFileNameFormatEntry = ntInstance.getEntry(kRecordingFileNameFormatKey);
|
||||
m_recordingControlEntry = ntInstance.getBooleanTopic(kRecordingControlKey).publish();
|
||||
m_recordingFileNameFormatEntry =
|
||||
ntInstance.getStringTopic(kRecordingFileNameFormatKey).publish();
|
||||
m_eventsTable = ntInstance.getTable(kEventMarkerTableName);
|
||||
}
|
||||
|
||||
public void startRecording() {
|
||||
m_recordingControlEntry.setBoolean(true);
|
||||
m_recordingControlEntry.set(true);
|
||||
}
|
||||
|
||||
public void stopRecording() {
|
||||
m_recordingControlEntry.setBoolean(false);
|
||||
m_recordingControlEntry.set(false);
|
||||
}
|
||||
|
||||
public void setRecordingFileNameFormat(String format) {
|
||||
m_recordingFileNameFormatEntry.setString(format);
|
||||
m_recordingFileNameFormatEntry.set(format);
|
||||
}
|
||||
|
||||
public void clearRecordingFileNameFormat() {
|
||||
m_recordingFileNameFormatEntry.delete();
|
||||
m_recordingFileNameFormatEntry.set("");
|
||||
}
|
||||
|
||||
public void addEventMarker(String name, String description, EventImportance importance) {
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
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;
|
||||
@@ -19,7 +22,14 @@ public final class SendableCameraWrapper implements Sendable, AutoCloseable {
|
||||
|
||||
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
|
||||
@@ -36,6 +46,19 @@ public final class SendableCameraWrapper implements Sendable, AutoCloseable {
|
||||
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();
|
||||
@@ -44,6 +67,18 @@ public final class SendableCameraWrapper implements Sendable, AutoCloseable {
|
||||
@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");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,15 +124,7 @@ public final class SendableCameraWrapper implements Sendable, AutoCloseable {
|
||||
Objects.requireNonNull(cameraUrls[i], "Camera URL at index " + i + " was null");
|
||||
}
|
||||
|
||||
String streams = "/CameraPublisher/" + cameraName + "/streams";
|
||||
if (NetworkTableInstance.getDefault().getEntries(streams, 0).length != 0) {
|
||||
throw new IllegalStateException(
|
||||
"A camera is already being streamed with the name '" + cameraName + "'");
|
||||
}
|
||||
|
||||
NetworkTableInstance.getDefault().getEntry(streams).setStringArray(cameraUrls);
|
||||
|
||||
SendableCameraWrapper wrapper = new SendableCameraWrapper(cameraName);
|
||||
SendableCameraWrapper wrapper = new SendableCameraWrapper(cameraName, cameraUrls);
|
||||
m_wrappers.put(cameraName, wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
* <p>For example, displaying a boolean entry with a toggle button:
|
||||
*
|
||||
* <pre>{@code
|
||||
* NetworkTableEntry myBoolean = Shuffleboard.getTab("Example Tab")
|
||||
* GenericEntry myBoolean = Shuffleboard.getTab("Example Tab")
|
||||
* .add("My Boolean", false)
|
||||
* .withWidget("Toggle Button")
|
||||
* .getEntry();
|
||||
@@ -25,7 +25,7 @@ import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
* <p>Changing the colors of the boolean box:
|
||||
*
|
||||
* <pre>{@code
|
||||
* NetworkTableEntry myBoolean = Shuffleboard.getTab("Example Tab")
|
||||
* GenericEntry myBoolean = Shuffleboard.getTab("Example Tab")
|
||||
* .add("My Boolean", false)
|
||||
* .withWidget("Boolean Box")
|
||||
* .withProperties(Map.of("colorWhenTrue", "green", "colorWhenFalse", "maroon"))
|
||||
@@ -36,7 +36,7 @@ import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
* the layout has already been generated by a previously defined entry.
|
||||
*
|
||||
* <pre>{@code
|
||||
* NetworkTableEntry myBoolean = Shuffleboard.getTab("Example Tab")
|
||||
* GenericEntry myBoolean = Shuffleboard.getTab("Example Tab")
|
||||
* .getLayout("List", "Example List")
|
||||
* .add("My Boolean", false)
|
||||
* .withWidget("Toggle Button")
|
||||
|
||||
@@ -115,21 +115,21 @@ public abstract class ShuffleboardComponent<C extends ShuffleboardComponent<C>>
|
||||
}
|
||||
// Component type
|
||||
if (getType() == null) {
|
||||
metaTable.getEntry("PreferredComponent").delete();
|
||||
metaTable.getEntry("PreferredComponent").unpublish();
|
||||
} else {
|
||||
metaTable.getEntry("PreferredComponent").forceSetString(getType());
|
||||
metaTable.getEntry("PreferredComponent").setString(getType());
|
||||
}
|
||||
|
||||
// Tile size
|
||||
if (m_width <= 0 || m_height <= 0) {
|
||||
metaTable.getEntry("Size").delete();
|
||||
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").delete();
|
||||
metaTable.getEntry("Position").unpublish();
|
||||
} else {
|
||||
metaTable.getEntry("Position").setDoubleArray(new double[] {m_column, m_row});
|
||||
}
|
||||
|
||||
@@ -5,11 +5,14 @@
|
||||
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. */
|
||||
@@ -122,6 +125,19 @@ public interface ShuffleboardContainer extends ShuffleboardValue {
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@@ -162,6 +178,45 @@ public interface ShuffleboardContainer extends ShuffleboardValue {
|
||||
*/
|
||||
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
|
||||
@@ -201,6 +256,32 @@ public interface ShuffleboardContainer extends ShuffleboardValue {
|
||||
*/
|
||||
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
|
||||
@@ -225,7 +306,24 @@ public interface ShuffleboardContainer extends ShuffleboardValue {
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<byte[]> addRaw(String title, Supplier<byte[]> valueSupplier);
|
||||
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,
|
||||
@@ -240,8 +338,25 @@ public interface ShuffleboardContainer extends ShuffleboardValue {
|
||||
* @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().setPersistent();
|
||||
widget.getEntry(typeString).getTopic().setPersistent(true);
|
||||
return widget;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ import static edu.wpi.first.wpilibj.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.NetworkTableEntry;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.networktables.StringPublisher;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
@@ -21,7 +21,7 @@ final class ShuffleboardInstance implements ShuffleboardRoot {
|
||||
private boolean m_tabsChanged = false; // NOPMD redundant field initializer
|
||||
private final NetworkTable m_rootTable;
|
||||
private final NetworkTable m_rootMetaTable;
|
||||
private final NetworkTableEntry m_selectedTabEntry;
|
||||
private final StringPublisher m_selectedTabPub;
|
||||
|
||||
/**
|
||||
* Creates a new Shuffleboard instance.
|
||||
@@ -32,7 +32,7 @@ final class ShuffleboardInstance implements ShuffleboardRoot {
|
||||
requireNonNullParam(ntInstance, "ntInstance", "ShuffleboardInstance");
|
||||
m_rootTable = ntInstance.getTable(Shuffleboard.kBaseTableName);
|
||||
m_rootMetaTable = m_rootTable.getSubTable(".metadata");
|
||||
m_selectedTabEntry = m_rootMetaTable.getEntry("Selected");
|
||||
m_selectedTabPub = m_rootMetaTable.getStringTopic("Selected").publish();
|
||||
HAL.report(tResourceType.kResourceType_Shuffleboard, 0);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ final class ShuffleboardInstance implements ShuffleboardRoot {
|
||||
if (m_tabsChanged) {
|
||||
String[] tabTitles =
|
||||
m_tabs.values().stream().map(ShuffleboardTab::getTitle).toArray(String[]::new);
|
||||
m_rootMetaTable.getEntry("Tabs").forceSetStringArray(tabTitles);
|
||||
m_rootMetaTable.getEntry("Tabs").setStringArray(tabTitles);
|
||||
m_tabsChanged = false;
|
||||
}
|
||||
for (ShuffleboardTab tab : m_tabs.values()) {
|
||||
@@ -72,12 +72,12 @@ final class ShuffleboardInstance implements ShuffleboardRoot {
|
||||
|
||||
@Override
|
||||
public void selectTab(int index) {
|
||||
m_selectedTabEntry.forceSetDouble(index);
|
||||
selectTab(Integer.toString(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectTab(String title) {
|
||||
m_selectedTabEntry.forceSetString(title);
|
||||
m_selectedTabPub.set(title);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,10 +7,12 @@ package edu.wpi.first.wpilibj.shuffleboard;
|
||||
import static edu.wpi.first.wpilibj.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. */
|
||||
@@ -52,6 +54,11 @@ public class ShuffleboardLayout extends ShuffleboardComponent<ShuffleboardLayout
|
||||
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);
|
||||
@@ -62,6 +69,21 @@ public class ShuffleboardLayout extends ShuffleboardComponent<ShuffleboardLayout
|
||||
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);
|
||||
@@ -79,6 +101,16 @@ public class ShuffleboardLayout extends ShuffleboardComponent<ShuffleboardLayout
|
||||
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) {
|
||||
@@ -86,8 +118,9 @@ public class ShuffleboardLayout extends ShuffleboardComponent<ShuffleboardLayout
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<byte[]> addRaw(String title, Supplier<byte[]> valueSupplier) {
|
||||
return m_helper.addRaw(title, valueSupplier);
|
||||
public SuppliedValueWidget<byte[]> addRaw(
|
||||
String title, String typeString, Supplier<byte[]> valueSupplier) {
|
||||
return m_helper.addRaw(title, typeString, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -66,6 +68,11 @@ public final class ShuffleboardTab implements ShuffleboardContainer {
|
||||
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);
|
||||
@@ -76,6 +83,21 @@ public final class ShuffleboardTab implements ShuffleboardContainer {
|
||||
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);
|
||||
@@ -93,6 +115,16 @@ public final class ShuffleboardTab implements ShuffleboardContainer {
|
||||
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) {
|
||||
@@ -100,8 +132,9 @@ public final class ShuffleboardTab implements ShuffleboardContainer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<byte[]> addRaw(String title, Supplier<byte[]> valueSupplier) {
|
||||
return m_helper.addRaw(title, valueSupplier);
|
||||
public SuppliedValueWidget<byte[]> addRaw(
|
||||
String title, String typeString, Supplier<byte[]> valueSupplier) {
|
||||
return m_helper.addRaw(title, typeString, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.GenericEntry;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
|
||||
/** A Shuffleboard widget that handles a single data point such as a number or string. */
|
||||
public final class SimpleWidget extends ShuffleboardWidget<SimpleWidget> {
|
||||
private NetworkTableEntry m_entry;
|
||||
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);
|
||||
@@ -20,18 +21,39 @@ public final class SimpleWidget extends ShuffleboardWidget<SimpleWidget> {
|
||||
*
|
||||
* @return The NetworkTable entry that contains the data for this widget.
|
||||
*/
|
||||
public NetworkTableEntry getEntry() {
|
||||
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.getEntry(getTitle());
|
||||
m_entry = parentTable.getTopic(getTitle()).getGenericEntry(m_typeString);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
|
||||
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 edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -14,34 +16,52 @@ import java.util.function.Supplier;
|
||||
*
|
||||
* @param <T> the type of values in the widget
|
||||
*/
|
||||
public final class SuppliedValueWidget<T> extends ShuffleboardWidget<SuppliedValueWidget<T>> {
|
||||
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<NetworkTableEntry, T> m_setter;
|
||||
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<NetworkTableEntry, T> setter) {
|
||||
BiConsumer<GenericPublisher, T> setter) {
|
||||
super(parent, title);
|
||||
this.m_supplier = supplier;
|
||||
this.m_setter = setter;
|
||||
m_typeString = typeString;
|
||||
m_supplier = supplier;
|
||||
m_setter = setter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildInto(NetworkTable parentTable, NetworkTable metaTable) {
|
||||
buildMetadata(metaTable);
|
||||
metaTable.getEntry("Controllable").setBoolean(false);
|
||||
m_controllablePub = new BooleanTopic(metaTable.getTopic("Controllable")).publish();
|
||||
m_controllablePub.set(false);
|
||||
|
||||
NetworkTableEntry entry = parentTable.getEntry(getTitle());
|
||||
m_setter.accept(entry, m_supplier.get());
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import java.util.List;
|
||||
* using the getObject() function. Other objects can also have multiple poses (which will show the
|
||||
* object at multiple locations).
|
||||
*/
|
||||
public class Field2d implements NTSendable {
|
||||
public class Field2d implements NTSendable, AutoCloseable {
|
||||
/** Constructor. */
|
||||
public Field2d() {
|
||||
FieldObject2d obj = new FieldObject2d("Robot");
|
||||
@@ -38,6 +38,13 @@ public class Field2d implements NTSendable {
|
||||
SendableRegistry.add(this, "Field");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
for (FieldObject2d obj : m_objects) {
|
||||
obj.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the robot pose from a Pose object.
|
||||
*
|
||||
@@ -83,7 +90,7 @@ public class Field2d implements NTSendable {
|
||||
m_objects.add(obj);
|
||||
if (m_table != null) {
|
||||
synchronized (obj) {
|
||||
obj.m_entry = m_table.getEntry(name);
|
||||
obj.m_entry = m_table.getDoubleArrayTopic(name).getEntry(new double[] {});
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
@@ -106,7 +113,7 @@ public class Field2d implements NTSendable {
|
||||
m_table = builder.getTable();
|
||||
for (FieldObject2d obj : m_objects) {
|
||||
synchronized (obj) {
|
||||
obj.m_entry = m_table.getEntry(obj.m_name);
|
||||
obj.m_entry = m_table.getDoubleArrayTopic(obj.m_name).getEntry(new double[] {});
|
||||
obj.updateEntry(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,14 +8,12 @@ import edu.wpi.first.math.geometry.Pose2d;
|
||||
import edu.wpi.first.math.geometry.Rotation2d;
|
||||
import edu.wpi.first.math.geometry.Translation2d;
|
||||
import edu.wpi.first.math.trajectory.Trajectory;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import edu.wpi.first.networktables.DoubleArrayEntry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Game field object on a Field2d. */
|
||||
public class FieldObject2d {
|
||||
public class FieldObject2d implements AutoCloseable {
|
||||
/**
|
||||
* Package-local constructor.
|
||||
*
|
||||
@@ -25,6 +23,11 @@ public class FieldObject2d {
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
m_entry.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the pose from a Pose object.
|
||||
*
|
||||
@@ -116,39 +119,20 @@ public class FieldObject2d {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_poses.size() < (255 / 3)) {
|
||||
double[] arr = new double[m_poses.size() * 3];
|
||||
int ndx = 0;
|
||||
for (Pose2d pose : m_poses) {
|
||||
Translation2d translation = pose.getTranslation();
|
||||
arr[ndx + 0] = translation.getX();
|
||||
arr[ndx + 1] = translation.getY();
|
||||
arr[ndx + 2] = pose.getRotation().getDegrees();
|
||||
ndx += 3;
|
||||
}
|
||||
double[] arr = new double[m_poses.size() * 3];
|
||||
int ndx = 0;
|
||||
for (Pose2d pose : m_poses) {
|
||||
Translation2d translation = pose.getTranslation();
|
||||
arr[ndx + 0] = translation.getX();
|
||||
arr[ndx + 1] = translation.getY();
|
||||
arr[ndx + 2] = pose.getRotation().getDegrees();
|
||||
ndx += 3;
|
||||
}
|
||||
|
||||
if (setDefault) {
|
||||
m_entry.setDefaultDoubleArray(arr);
|
||||
} else {
|
||||
m_entry.setDoubleArray(arr);
|
||||
}
|
||||
if (setDefault) {
|
||||
m_entry.setDefault(arr);
|
||||
} else {
|
||||
// send as raw array of doubles if too big for NT array
|
||||
ByteBuffer output = ByteBuffer.allocate(m_poses.size() * 3 * 8);
|
||||
output.order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (Pose2d pose : m_poses) {
|
||||
Translation2d translation = pose.getTranslation();
|
||||
output.putDouble(translation.getX());
|
||||
output.putDouble(translation.getY());
|
||||
output.putDouble(pose.getRotation().getDegrees());
|
||||
}
|
||||
|
||||
if (setDefault) {
|
||||
m_entry.setDefaultRaw(output.array());
|
||||
} else {
|
||||
m_entry.forceSetRaw(output.array());
|
||||
}
|
||||
m_entry.set(arr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +141,7 @@ public class FieldObject2d {
|
||||
return;
|
||||
}
|
||||
|
||||
double[] arr = m_entry.getDoubleArray((double[]) null);
|
||||
double[] arr = m_entry.get((double[]) null);
|
||||
if (arr != null) {
|
||||
if ((arr.length % 3) != 0) {
|
||||
return;
|
||||
@@ -167,31 +151,10 @@ public class FieldObject2d {
|
||||
for (int i = 0; i < arr.length; i += 3) {
|
||||
m_poses.add(new Pose2d(arr[i], arr[i + 1], Rotation2d.fromDegrees(arr[i + 2])));
|
||||
}
|
||||
} else {
|
||||
// read as raw array of doubles
|
||||
byte[] data = m_entry.getRaw((byte[]) null);
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// must be triples of doubles
|
||||
if ((data.length % (3 * 8)) != 0) {
|
||||
return;
|
||||
}
|
||||
ByteBuffer input = ByteBuffer.wrap(data);
|
||||
input.order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
m_poses.clear();
|
||||
for (int i = 0; i < (data.length / (3 * 8)); i++) {
|
||||
double x = input.getDouble();
|
||||
double y = input.getDouble();
|
||||
double rot = input.getDouble();
|
||||
m_poses.add(new Pose2d(x, y, Rotation2d.fromDegrees(rot)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String m_name;
|
||||
NetworkTableEntry m_entry;
|
||||
DoubleArrayEntry m_entry;
|
||||
private final List<Pose2d> m_poses = new ArrayList<>();
|
||||
}
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
|
||||
package edu.wpi.first.wpilibj.smartdashboard;
|
||||
|
||||
import edu.wpi.first.networktables.DoubleArrayPublisher;
|
||||
import edu.wpi.first.networktables.NTSendable;
|
||||
import edu.wpi.first.networktables.NTSendableBuilder;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.StringPublisher;
|
||||
import edu.wpi.first.wpilibj.util.Color8Bit;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -23,12 +25,13 @@ import java.util.Map.Entry;
|
||||
* @see MechanismLigament2d
|
||||
* @see MechanismRoot2d
|
||||
*/
|
||||
public final class Mechanism2d implements NTSendable {
|
||||
private static final String kBackgroundColor = "backgroundColor";
|
||||
public final class Mechanism2d implements NTSendable, AutoCloseable {
|
||||
private NetworkTable m_table;
|
||||
private final Map<String, MechanismRoot2d> m_roots;
|
||||
private final double[] m_dims = new double[2];
|
||||
private String m_color;
|
||||
private DoubleArrayPublisher m_dimsPub;
|
||||
private StringPublisher m_colorPub;
|
||||
|
||||
/**
|
||||
* Create a new Mechanism2d with the given dimensions and default color (dark blue).
|
||||
@@ -58,6 +61,19 @@ public final class Mechanism2d implements NTSendable {
|
||||
setBackgroundColor(backgroundColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (m_dimsPub != null) {
|
||||
m_dimsPub.close();
|
||||
}
|
||||
if (m_colorPub != null) {
|
||||
m_colorPub.close();
|
||||
}
|
||||
for (MechanismRoot2d root : m_roots.values()) {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create a root in this Mechanism2d with the given name and position.
|
||||
*
|
||||
@@ -88,9 +104,9 @@ public final class Mechanism2d implements NTSendable {
|
||||
* @param color the new color
|
||||
*/
|
||||
public synchronized void setBackgroundColor(Color8Bit color) {
|
||||
this.m_color = color.toHexString();
|
||||
if (m_table != null) {
|
||||
m_table.getEntry(kBackgroundColor).setString(m_color);
|
||||
m_color = color.toHexString();
|
||||
if (m_colorPub != null) {
|
||||
m_colorPub.set(m_color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,8 +115,16 @@ public final class Mechanism2d implements NTSendable {
|
||||
builder.setSmartDashboardType("Mechanism2d");
|
||||
synchronized (this) {
|
||||
m_table = builder.getTable();
|
||||
m_table.getEntry("dims").setDoubleArray(m_dims);
|
||||
m_table.getEntry(kBackgroundColor).setString(m_color);
|
||||
if (m_dimsPub != null) {
|
||||
m_dimsPub.close();
|
||||
}
|
||||
m_dimsPub = m_table.getDoubleArrayTopic("dims").publish();
|
||||
m_dimsPub.set(m_dims);
|
||||
if (m_colorPub != null) {
|
||||
m_colorPub.close();
|
||||
}
|
||||
m_colorPub = m_table.getStringTopic("backgroundColor").publish();
|
||||
m_colorPub.set(m_color);
|
||||
for (Entry<String, MechanismRoot2d> entry : m_roots.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
MechanismRoot2d root = entry.getValue();
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
package edu.wpi.first.wpilibj.smartdashboard;
|
||||
|
||||
import edu.wpi.first.math.geometry.Rotation2d;
|
||||
import edu.wpi.first.networktables.DoubleEntry;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.networktables.StringEntry;
|
||||
import edu.wpi.first.networktables.StringPublisher;
|
||||
import edu.wpi.first.wpilibj.util.Color8Bit;
|
||||
|
||||
/**
|
||||
@@ -16,14 +18,15 @@ import edu.wpi.first.wpilibj.util.Color8Bit;
|
||||
* @see Mechanism2d
|
||||
*/
|
||||
public class MechanismLigament2d extends MechanismObject2d {
|
||||
private StringPublisher m_typePub;
|
||||
private double m_angle;
|
||||
private NetworkTableEntry m_angleEntry;
|
||||
private DoubleEntry m_angleEntry;
|
||||
private String m_color;
|
||||
private NetworkTableEntry m_colorEntry;
|
||||
private StringEntry m_colorEntry;
|
||||
private double m_length;
|
||||
private NetworkTableEntry m_lengthEntry;
|
||||
private DoubleEntry m_lengthEntry;
|
||||
private double m_weight;
|
||||
private NetworkTableEntry m_weightEntry;
|
||||
private DoubleEntry m_weightEntry;
|
||||
|
||||
/**
|
||||
* Create a new ligament.
|
||||
@@ -54,6 +57,26 @@ public class MechanismLigament2d extends MechanismObject2d {
|
||||
this(name, length, angle, 10, new Color8Bit(235, 137, 52));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
if (m_typePub != null) {
|
||||
m_typePub.close();
|
||||
}
|
||||
if (m_angleEntry != null) {
|
||||
m_angleEntry.close();
|
||||
}
|
||||
if (m_colorEntry != null) {
|
||||
m_colorEntry.close();
|
||||
}
|
||||
if (m_lengthEntry != null) {
|
||||
m_lengthEntry.close();
|
||||
}
|
||||
if (m_weightEntry != null) {
|
||||
m_weightEntry.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ligament's angle relative to its parent.
|
||||
*
|
||||
@@ -61,7 +84,9 @@ public class MechanismLigament2d extends MechanismObject2d {
|
||||
*/
|
||||
public synchronized void setAngle(double degrees) {
|
||||
m_angle = degrees;
|
||||
flush();
|
||||
if (m_angleEntry != null) {
|
||||
m_angleEntry.set(degrees);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,7 +105,7 @@ public class MechanismLigament2d extends MechanismObject2d {
|
||||
*/
|
||||
public synchronized double getAngle() {
|
||||
if (m_angleEntry != null) {
|
||||
m_angle = m_angleEntry.getDouble(0.0);
|
||||
m_angle = m_angleEntry.get();
|
||||
}
|
||||
return m_angle;
|
||||
}
|
||||
@@ -92,7 +117,9 @@ public class MechanismLigament2d extends MechanismObject2d {
|
||||
*/
|
||||
public synchronized void setLength(double length) {
|
||||
m_length = length;
|
||||
flush();
|
||||
if (m_lengthEntry != null) {
|
||||
m_lengthEntry.set(length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,7 +129,7 @@ public class MechanismLigament2d extends MechanismObject2d {
|
||||
*/
|
||||
public synchronized double getLength() {
|
||||
if (m_lengthEntry != null) {
|
||||
m_length = m_lengthEntry.getDouble(0.0);
|
||||
m_length = m_lengthEntry.get();
|
||||
}
|
||||
return m_length;
|
||||
}
|
||||
@@ -114,7 +141,9 @@ public class MechanismLigament2d extends MechanismObject2d {
|
||||
*/
|
||||
public synchronized void setColor(Color8Bit color) {
|
||||
m_color = String.format("#%02X%02X%02X", color.red, color.green, color.blue);
|
||||
flush();
|
||||
if (m_colorEntry != null) {
|
||||
m_colorEntry.set(m_color);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,7 +153,7 @@ public class MechanismLigament2d extends MechanismObject2d {
|
||||
*/
|
||||
public synchronized Color8Bit getColor() {
|
||||
if (m_colorEntry != null) {
|
||||
m_color = m_colorEntry.getString("");
|
||||
m_color = m_colorEntry.get();
|
||||
}
|
||||
int r = 0;
|
||||
int g = 0;
|
||||
@@ -150,7 +179,9 @@ public class MechanismLigament2d extends MechanismObject2d {
|
||||
*/
|
||||
public synchronized void setLineWeight(double weight) {
|
||||
m_weight = weight;
|
||||
flush();
|
||||
if (m_weightEntry != null) {
|
||||
m_weightEntry.set(weight);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,34 +191,41 @@ public class MechanismLigament2d extends MechanismObject2d {
|
||||
*/
|
||||
public synchronized double getLineWeight() {
|
||||
if (m_weightEntry != null) {
|
||||
m_weight = m_weightEntry.getDouble(0.0);
|
||||
m_weight = m_weightEntry.get();
|
||||
}
|
||||
return m_weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateEntries(NetworkTable table) {
|
||||
table.getEntry(".type").setString("line");
|
||||
m_angleEntry = table.getEntry("angle");
|
||||
m_lengthEntry = table.getEntry("length");
|
||||
m_colorEntry = table.getEntry("color");
|
||||
m_weightEntry = table.getEntry("weight");
|
||||
flush();
|
||||
}
|
||||
if (m_typePub != null) {
|
||||
m_typePub.close();
|
||||
}
|
||||
m_typePub = table.getStringTopic(".type").publish();
|
||||
m_typePub.set("line");
|
||||
|
||||
/** Flush latest data to NT. */
|
||||
private void flush() {
|
||||
if (m_angleEntry != null) {
|
||||
m_angleEntry.setDouble(m_angle);
|
||||
m_angleEntry.close();
|
||||
}
|
||||
m_angleEntry = table.getDoubleTopic("angle").getEntry(0.0);
|
||||
m_angleEntry.set(m_angle);
|
||||
|
||||
if (m_lengthEntry != null) {
|
||||
m_lengthEntry.setDouble(m_length);
|
||||
m_lengthEntry.close();
|
||||
}
|
||||
m_lengthEntry = table.getDoubleTopic("length").getEntry(0.0);
|
||||
m_lengthEntry.set(m_length);
|
||||
|
||||
if (m_colorEntry != null) {
|
||||
m_colorEntry.setString(m_color);
|
||||
m_colorEntry.close();
|
||||
}
|
||||
m_colorEntry = table.getStringTopic("color").getEntry("");
|
||||
m_colorEntry.set(m_color);
|
||||
|
||||
if (m_weightEntry != null) {
|
||||
m_weightEntry.setDouble(m_weight);
|
||||
m_weightEntry.close();
|
||||
}
|
||||
m_weightEntry = table.getDoubleTopic("weight").getEntry(0.0);
|
||||
m_weightEntry.set(m_weight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import java.util.Map;
|
||||
*
|
||||
* @see Mechanism2d
|
||||
*/
|
||||
public abstract class MechanismObject2d {
|
||||
public abstract class MechanismObject2d implements AutoCloseable {
|
||||
/** Relative to parent. */
|
||||
private final String m_name;
|
||||
|
||||
@@ -32,6 +32,13 @@ public abstract class MechanismObject2d {
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
for (MechanismObject2d obj : m_objects.values()) {
|
||||
obj.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a Mechanism object that is based on this one.
|
||||
*
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
package edu.wpi.first.wpilibj.smartdashboard;
|
||||
|
||||
import edu.wpi.first.networktables.DoublePublisher;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -19,14 +19,14 @@ import java.util.Map;
|
||||
*
|
||||
* <p>Append other nodes by using {@link #append(MechanismObject2d)}.
|
||||
*/
|
||||
public final class MechanismRoot2d {
|
||||
public final class MechanismRoot2d implements AutoCloseable {
|
||||
private final String m_name;
|
||||
private NetworkTable m_table;
|
||||
private final Map<String, MechanismObject2d> m_objects = new HashMap<>(1);
|
||||
private double m_x;
|
||||
private NetworkTableEntry m_xEntry;
|
||||
private DoublePublisher m_xPub;
|
||||
private double m_y;
|
||||
private NetworkTableEntry m_yEntry;
|
||||
private DoublePublisher m_yPub;
|
||||
|
||||
/**
|
||||
* Package-private constructor for roots.
|
||||
@@ -41,6 +41,19 @@ public final class MechanismRoot2d {
|
||||
m_y = y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (m_xPub != null) {
|
||||
m_xPub.close();
|
||||
}
|
||||
if (m_yPub != null) {
|
||||
m_yPub.close();
|
||||
}
|
||||
for (MechanismObject2d obj : m_objects.values()) {
|
||||
obj.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a Mechanism object that is based on this one.
|
||||
*
|
||||
@@ -75,8 +88,14 @@ public final class MechanismRoot2d {
|
||||
|
||||
synchronized void update(NetworkTable table) {
|
||||
m_table = table;
|
||||
m_xEntry = m_table.getEntry("x");
|
||||
m_yEntry = m_table.getEntry("y");
|
||||
if (m_xPub != null) {
|
||||
m_xPub.close();
|
||||
}
|
||||
m_xPub = m_table.getDoubleTopic("x").publish();
|
||||
if (m_yPub != null) {
|
||||
m_yPub.close();
|
||||
}
|
||||
m_yPub = m_table.getDoubleTopic("y").publish();
|
||||
flush();
|
||||
for (MechanismObject2d obj : m_objects.values()) {
|
||||
obj.update(m_table.getSubTable(obj.getName()));
|
||||
@@ -88,11 +107,11 @@ public final class MechanismRoot2d {
|
||||
}
|
||||
|
||||
private void flush() {
|
||||
if (m_xEntry != null) {
|
||||
m_xEntry.setDouble(m_x);
|
||||
if (m_xPub != null) {
|
||||
m_xPub.set(m_x);
|
||||
}
|
||||
if (m_yEntry != null) {
|
||||
m_yEntry.setDouble(m_y);
|
||||
if (m_yPub != null) {
|
||||
m_yPub.set(m_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,58 +4,132 @@
|
||||
|
||||
package edu.wpi.first.wpilibj.smartdashboard;
|
||||
|
||||
import edu.wpi.first.networktables.EntryListenerFlags;
|
||||
import edu.wpi.first.networktables.BooleanArrayPublisher;
|
||||
import edu.wpi.first.networktables.BooleanArraySubscriber;
|
||||
import edu.wpi.first.networktables.BooleanArrayTopic;
|
||||
import edu.wpi.first.networktables.BooleanPublisher;
|
||||
import edu.wpi.first.networktables.BooleanSubscriber;
|
||||
import edu.wpi.first.networktables.BooleanTopic;
|
||||
import edu.wpi.first.networktables.DoubleArrayPublisher;
|
||||
import edu.wpi.first.networktables.DoubleArraySubscriber;
|
||||
import edu.wpi.first.networktables.DoubleArrayTopic;
|
||||
import edu.wpi.first.networktables.DoublePublisher;
|
||||
import edu.wpi.first.networktables.DoubleSubscriber;
|
||||
import edu.wpi.first.networktables.DoubleTopic;
|
||||
import edu.wpi.first.networktables.FloatArrayPublisher;
|
||||
import edu.wpi.first.networktables.FloatArraySubscriber;
|
||||
import edu.wpi.first.networktables.FloatArrayTopic;
|
||||
import edu.wpi.first.networktables.FloatPublisher;
|
||||
import edu.wpi.first.networktables.FloatSubscriber;
|
||||
import edu.wpi.first.networktables.FloatTopic;
|
||||
import edu.wpi.first.networktables.IntegerArrayPublisher;
|
||||
import edu.wpi.first.networktables.IntegerArraySubscriber;
|
||||
import edu.wpi.first.networktables.IntegerArrayTopic;
|
||||
import edu.wpi.first.networktables.IntegerPublisher;
|
||||
import edu.wpi.first.networktables.IntegerSubscriber;
|
||||
import edu.wpi.first.networktables.IntegerTopic;
|
||||
import edu.wpi.first.networktables.NTSendableBuilder;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.networktables.NetworkTableValue;
|
||||
import edu.wpi.first.networktables.Publisher;
|
||||
import edu.wpi.first.networktables.RawPublisher;
|
||||
import edu.wpi.first.networktables.RawSubscriber;
|
||||
import edu.wpi.first.networktables.RawTopic;
|
||||
import edu.wpi.first.networktables.StringArrayPublisher;
|
||||
import edu.wpi.first.networktables.StringArraySubscriber;
|
||||
import edu.wpi.first.networktables.StringArrayTopic;
|
||||
import edu.wpi.first.networktables.StringPublisher;
|
||||
import edu.wpi.first.networktables.StringSubscriber;
|
||||
import edu.wpi.first.networktables.StringTopic;
|
||||
import edu.wpi.first.networktables.Subscriber;
|
||||
import edu.wpi.first.networktables.Topic;
|
||||
import edu.wpi.first.util.WPIUtilJNI;
|
||||
import edu.wpi.first.util.function.BooleanConsumer;
|
||||
import edu.wpi.first.util.function.FloatConsumer;
|
||||
import edu.wpi.first.util.function.FloatSupplier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.DoubleConsumer;
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.LongConsumer;
|
||||
import java.util.function.LongSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SendableBuilderImpl implements NTSendableBuilder {
|
||||
private static class Property implements AutoCloseable {
|
||||
Property(NetworkTable table, String key) {
|
||||
m_entry = table.getEntry(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
stopListener();
|
||||
}
|
||||
|
||||
void startListener() {
|
||||
if (m_entry.isValid() && m_listener == 0 && m_createListener != null) {
|
||||
m_listener = m_createListener.apply(m_entry);
|
||||
}
|
||||
}
|
||||
|
||||
void stopListener() {
|
||||
if (m_entry.isValid() && m_listener != 0) {
|
||||
m_entry.removeListener(m_listener);
|
||||
m_listener = 0;
|
||||
}
|
||||
}
|
||||
|
||||
final NetworkTableEntry m_entry;
|
||||
int m_listener;
|
||||
Consumer<NetworkTableEntry> m_update;
|
||||
Function<NetworkTableEntry, Integer> m_createListener;
|
||||
@FunctionalInterface
|
||||
private interface TimedConsumer<T> {
|
||||
void accept(T value, long time);
|
||||
}
|
||||
|
||||
private final List<Property> m_properties = new ArrayList<>();
|
||||
private static class Property<P extends Publisher, S extends Subscriber>
|
||||
implements AutoCloseable {
|
||||
@Override
|
||||
@SuppressWarnings("PMD.AvoidCatchingGenericException")
|
||||
public void close() {
|
||||
try {
|
||||
if (m_pub != null) {
|
||||
m_pub.close();
|
||||
}
|
||||
if (m_sub != null) {
|
||||
m_sub.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
void update(boolean controllable, long time) {
|
||||
if (controllable && m_sub != null && m_updateLocal != null) {
|
||||
m_updateLocal.accept(m_sub);
|
||||
} else if (m_pub != null && m_updateNetwork != null) {
|
||||
m_updateNetwork.accept(m_pub, time);
|
||||
}
|
||||
}
|
||||
|
||||
P m_pub;
|
||||
S m_sub;
|
||||
TimedConsumer<P> m_updateNetwork;
|
||||
Consumer<S> m_updateLocal;
|
||||
}
|
||||
|
||||
private final List<Property<?, ?>> m_properties = new ArrayList<>();
|
||||
private Runnable m_safeState;
|
||||
private final List<Runnable> m_updateTables = new ArrayList<>();
|
||||
private NetworkTable m_table;
|
||||
private NetworkTableEntry m_controllableEntry;
|
||||
private boolean m_controllable;
|
||||
private boolean m_actuator;
|
||||
|
||||
private BooleanPublisher m_controllablePub;
|
||||
private StringPublisher m_typePub;
|
||||
private BooleanPublisher m_actuatorPub;
|
||||
|
||||
private final List<AutoCloseable> m_closeables = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("PMD.AvoidCatchingGenericException")
|
||||
public void close() {
|
||||
if (m_controllablePub != null) {
|
||||
m_controllablePub.close();
|
||||
}
|
||||
if (m_typePub != null) {
|
||||
m_typePub.close();
|
||||
}
|
||||
if (m_actuatorPub != null) {
|
||||
m_actuatorPub.close();
|
||||
}
|
||||
for (Property<?, ?> property : m_properties) {
|
||||
property.close();
|
||||
}
|
||||
for (AutoCloseable closeable : m_closeables) {
|
||||
try {
|
||||
closeable.close();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the network table. Must be called prior to any Add* functions being called.
|
||||
*
|
||||
@@ -63,7 +137,8 @@ public class SendableBuilderImpl implements NTSendableBuilder {
|
||||
*/
|
||||
public void setTable(NetworkTable table) {
|
||||
m_table = table;
|
||||
m_controllableEntry = table.getEntry(".controllable");
|
||||
m_controllablePub = table.getBooleanTopic(".controllable").publish();
|
||||
m_controllablePub.setDefault(false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,10 +173,9 @@ public class SendableBuilderImpl implements NTSendableBuilder {
|
||||
/** Update the network table values by calling the getters for all properties. */
|
||||
@Override
|
||||
public void update() {
|
||||
for (Property property : m_properties) {
|
||||
if (property.m_update != null) {
|
||||
property.m_update.accept(property.m_entry);
|
||||
}
|
||||
long time = WPIUtilJNI.now();
|
||||
for (Property<?, ?> property : m_properties) {
|
||||
property.update(m_controllable, time);
|
||||
}
|
||||
for (Runnable updateTable : m_updateTables) {
|
||||
updateTable.run();
|
||||
@@ -110,21 +184,17 @@ public class SendableBuilderImpl implements NTSendableBuilder {
|
||||
|
||||
/** Hook setters for all properties. */
|
||||
public void startListeners() {
|
||||
for (Property property : m_properties) {
|
||||
property.startListener();
|
||||
}
|
||||
if (m_controllableEntry != null) {
|
||||
m_controllableEntry.setBoolean(true);
|
||||
m_controllable = true;
|
||||
if (m_controllablePub != null) {
|
||||
m_controllablePub.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
/** Unhook setters for all properties. */
|
||||
public void stopListeners() {
|
||||
for (Property property : m_properties) {
|
||||
property.stopListener();
|
||||
}
|
||||
if (m_controllableEntry != null) {
|
||||
m_controllableEntry.setBoolean(false);
|
||||
m_controllable = false;
|
||||
if (m_controllablePub != null) {
|
||||
m_controllablePub.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,9 +224,17 @@ public class SendableBuilderImpl implements NTSendableBuilder {
|
||||
@Override
|
||||
public void clearProperties() {
|
||||
stopListeners();
|
||||
for (Property<?, ?> property : m_properties) {
|
||||
property.close();
|
||||
}
|
||||
m_properties.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCloseable(AutoCloseable closeable) {
|
||||
m_closeables.add(closeable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the string representation of the named data type that will be used by the smart dashboard
|
||||
* for this sendable.
|
||||
@@ -165,7 +243,10 @@ public class SendableBuilderImpl implements NTSendableBuilder {
|
||||
*/
|
||||
@Override
|
||||
public void setSmartDashboardType(String type) {
|
||||
m_table.getEntry(".type").setString(type);
|
||||
if (m_typePub == null) {
|
||||
m_typePub = m_table.getStringTopic(".type").publish();
|
||||
}
|
||||
m_typePub.set(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,7 +257,10 @@ public class SendableBuilderImpl implements NTSendableBuilder {
|
||||
*/
|
||||
@Override
|
||||
public void setActuator(boolean value) {
|
||||
m_table.getEntry(".actuator").setBoolean(value);
|
||||
if (m_actuatorPub == null) {
|
||||
m_actuatorPub = m_table.getBooleanTopic(".actuator").publish();
|
||||
}
|
||||
m_actuatorPub.set(value);
|
||||
m_actuator = value;
|
||||
}
|
||||
|
||||
@@ -194,7 +278,7 @@ public class SendableBuilderImpl implements NTSendableBuilder {
|
||||
/**
|
||||
* Set the function that should be called to update the network table for things other than
|
||||
* properties. Note this function is not passed the network table object; instead it should use
|
||||
* the entry handles returned by getEntry().
|
||||
* the topics returned by getTopic().
|
||||
*
|
||||
* @param func function
|
||||
*/
|
||||
@@ -211,8 +295,8 @@ public class SendableBuilderImpl implements NTSendableBuilder {
|
||||
* @return Network table entry
|
||||
*/
|
||||
@Override
|
||||
public NetworkTableEntry getEntry(String key) {
|
||||
return m_table.getEntry(key);
|
||||
public Topic getTopic(String key) {
|
||||
return m_table.getTopic(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,23 +308,74 @@ public class SendableBuilderImpl implements NTSendableBuilder {
|
||||
*/
|
||||
@Override
|
||||
public void addBooleanProperty(String key, BooleanSupplier getter, BooleanConsumer setter) {
|
||||
Property property = new Property(m_table, key);
|
||||
Property<BooleanPublisher, BooleanSubscriber> property = new Property<>();
|
||||
BooleanTopic topic = m_table.getBooleanTopic(key);
|
||||
if (getter != null) {
|
||||
property.m_update = entry -> entry.setBoolean(getter.getAsBoolean());
|
||||
property.m_pub = topic.publish();
|
||||
property.m_updateNetwork = (pub, time) -> pub.set(getter.getAsBoolean(), time);
|
||||
}
|
||||
if (setter != null) {
|
||||
property.m_createListener =
|
||||
entry ->
|
||||
entry.addListener(
|
||||
event -> {
|
||||
if (event.value.isBoolean()) {
|
||||
SmartDashboard.postListenerTask(
|
||||
() -> setter.accept(event.value.getBoolean()));
|
||||
}
|
||||
},
|
||||
EntryListenerFlags.kImmediate
|
||||
| EntryListenerFlags.kNew
|
||||
| EntryListenerFlags.kUpdate);
|
||||
property.m_sub = topic.subscribe(false);
|
||||
property.m_updateLocal =
|
||||
sub -> {
|
||||
for (boolean val : sub.readQueueValues()) {
|
||||
setter.accept(val);
|
||||
}
|
||||
};
|
||||
}
|
||||
m_properties.add(property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an integer property.
|
||||
*
|
||||
* @param key property name
|
||||
* @param getter getter function (returns current value)
|
||||
* @param setter setter function (sets new value)
|
||||
*/
|
||||
@Override
|
||||
public void addIntegerProperty(String key, LongSupplier getter, LongConsumer setter) {
|
||||
Property<IntegerPublisher, IntegerSubscriber> property = new Property<>();
|
||||
IntegerTopic topic = m_table.getIntegerTopic(key);
|
||||
if (getter != null) {
|
||||
property.m_pub = topic.publish();
|
||||
property.m_updateNetwork = (pub, time) -> pub.set(getter.getAsLong(), time);
|
||||
}
|
||||
if (setter != null) {
|
||||
property.m_sub = topic.subscribe(0);
|
||||
property.m_updateLocal =
|
||||
sub -> {
|
||||
for (long val : sub.readQueueValues()) {
|
||||
setter.accept(val);
|
||||
}
|
||||
};
|
||||
}
|
||||
m_properties.add(property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a float property.
|
||||
*
|
||||
* @param key property name
|
||||
* @param getter getter function (returns current value)
|
||||
* @param setter setter function (sets new value)
|
||||
*/
|
||||
@Override
|
||||
public void addFloatProperty(String key, FloatSupplier getter, FloatConsumer setter) {
|
||||
Property<FloatPublisher, FloatSubscriber> property = new Property<>();
|
||||
FloatTopic topic = m_table.getFloatTopic(key);
|
||||
if (getter != null) {
|
||||
property.m_pub = topic.publish();
|
||||
property.m_updateNetwork = (pub, time) -> pub.set(getter.getAsFloat(), time);
|
||||
}
|
||||
if (setter != null) {
|
||||
property.m_sub = topic.subscribe(0.0f);
|
||||
property.m_updateLocal =
|
||||
sub -> {
|
||||
for (float val : sub.readQueueValues()) {
|
||||
setter.accept(val);
|
||||
}
|
||||
};
|
||||
}
|
||||
m_properties.add(property);
|
||||
}
|
||||
@@ -254,22 +389,20 @@ public class SendableBuilderImpl implements NTSendableBuilder {
|
||||
*/
|
||||
@Override
|
||||
public void addDoubleProperty(String key, DoubleSupplier getter, DoubleConsumer setter) {
|
||||
Property property = new Property(m_table, key);
|
||||
Property<DoublePublisher, DoubleSubscriber> property = new Property<>();
|
||||
DoubleTopic topic = m_table.getDoubleTopic(key);
|
||||
if (getter != null) {
|
||||
property.m_update = entry -> entry.setDouble(getter.getAsDouble());
|
||||
property.m_pub = topic.publish();
|
||||
property.m_updateNetwork = (pub, time) -> pub.set(getter.getAsDouble(), time);
|
||||
}
|
||||
if (setter != null) {
|
||||
property.m_createListener =
|
||||
entry ->
|
||||
entry.addListener(
|
||||
event -> {
|
||||
if (event.value.isDouble()) {
|
||||
SmartDashboard.postListenerTask(() -> setter.accept(event.value.getDouble()));
|
||||
}
|
||||
},
|
||||
EntryListenerFlags.kImmediate
|
||||
| EntryListenerFlags.kNew
|
||||
| EntryListenerFlags.kUpdate);
|
||||
property.m_sub = topic.subscribe(0.0);
|
||||
property.m_updateLocal =
|
||||
sub -> {
|
||||
for (double val : sub.readQueueValues()) {
|
||||
setter.accept(val);
|
||||
}
|
||||
};
|
||||
}
|
||||
m_properties.add(property);
|
||||
}
|
||||
@@ -283,22 +416,20 @@ public class SendableBuilderImpl implements NTSendableBuilder {
|
||||
*/
|
||||
@Override
|
||||
public void addStringProperty(String key, Supplier<String> getter, Consumer<String> setter) {
|
||||
Property property = new Property(m_table, key);
|
||||
Property<StringPublisher, StringSubscriber> property = new Property<>();
|
||||
StringTopic topic = m_table.getStringTopic(key);
|
||||
if (getter != null) {
|
||||
property.m_update = entry -> entry.setString(getter.get());
|
||||
property.m_pub = topic.publish();
|
||||
property.m_updateNetwork = (pub, time) -> pub.set(getter.get(), time);
|
||||
}
|
||||
if (setter != null) {
|
||||
property.m_createListener =
|
||||
entry ->
|
||||
entry.addListener(
|
||||
event -> {
|
||||
if (event.value.isString()) {
|
||||
SmartDashboard.postListenerTask(() -> setter.accept(event.value.getString()));
|
||||
}
|
||||
},
|
||||
EntryListenerFlags.kImmediate
|
||||
| EntryListenerFlags.kNew
|
||||
| EntryListenerFlags.kUpdate);
|
||||
property.m_sub = topic.subscribe("");
|
||||
property.m_updateLocal =
|
||||
sub -> {
|
||||
for (String val : sub.readQueueValues()) {
|
||||
setter.accept(val);
|
||||
}
|
||||
};
|
||||
}
|
||||
m_properties.add(property);
|
||||
}
|
||||
@@ -313,23 +444,76 @@ public class SendableBuilderImpl implements NTSendableBuilder {
|
||||
@Override
|
||||
public void addBooleanArrayProperty(
|
||||
String key, Supplier<boolean[]> getter, Consumer<boolean[]> setter) {
|
||||
Property property = new Property(m_table, key);
|
||||
Property<BooleanArrayPublisher, BooleanArraySubscriber> property = new Property<>();
|
||||
BooleanArrayTopic topic = m_table.getBooleanArrayTopic(key);
|
||||
if (getter != null) {
|
||||
property.m_update = entry -> entry.setBooleanArray(getter.get());
|
||||
property.m_pub = topic.publish();
|
||||
property.m_updateNetwork = (pub, time) -> pub.set(getter.get(), time);
|
||||
}
|
||||
if (setter != null) {
|
||||
property.m_createListener =
|
||||
entry ->
|
||||
entry.addListener(
|
||||
event -> {
|
||||
if (event.value.isBooleanArray()) {
|
||||
SmartDashboard.postListenerTask(
|
||||
() -> setter.accept(event.value.getBooleanArray()));
|
||||
}
|
||||
},
|
||||
EntryListenerFlags.kImmediate
|
||||
| EntryListenerFlags.kNew
|
||||
| EntryListenerFlags.kUpdate);
|
||||
property.m_sub = topic.subscribe(new boolean[] {});
|
||||
property.m_updateLocal =
|
||||
sub -> {
|
||||
for (boolean[] val : sub.readQueueValues()) {
|
||||
setter.accept(val);
|
||||
}
|
||||
};
|
||||
}
|
||||
m_properties.add(property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an integer array property.
|
||||
*
|
||||
* @param key property name
|
||||
* @param getter getter function (returns current value)
|
||||
* @param setter setter function (sets new value)
|
||||
*/
|
||||
@Override
|
||||
public void addIntegerArrayProperty(
|
||||
String key, Supplier<long[]> getter, Consumer<long[]> setter) {
|
||||
Property<IntegerArrayPublisher, IntegerArraySubscriber> property = new Property<>();
|
||||
IntegerArrayTopic topic = m_table.getIntegerArrayTopic(key);
|
||||
if (getter != null) {
|
||||
property.m_pub = topic.publish();
|
||||
property.m_updateNetwork = (pub, time) -> pub.set(getter.get(), time);
|
||||
}
|
||||
if (setter != null) {
|
||||
property.m_sub = topic.subscribe(new long[] {});
|
||||
property.m_updateLocal =
|
||||
sub -> {
|
||||
for (long[] val : sub.readQueueValues()) {
|
||||
setter.accept(val);
|
||||
}
|
||||
};
|
||||
}
|
||||
m_properties.add(property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a float array property.
|
||||
*
|
||||
* @param key property name
|
||||
* @param getter getter function (returns current value)
|
||||
* @param setter setter function (sets new value)
|
||||
*/
|
||||
@Override
|
||||
public void addFloatArrayProperty(
|
||||
String key, Supplier<float[]> getter, Consumer<float[]> setter) {
|
||||
Property<FloatArrayPublisher, FloatArraySubscriber> property = new Property<>();
|
||||
FloatArrayTopic topic = m_table.getFloatArrayTopic(key);
|
||||
if (getter != null) {
|
||||
property.m_pub = topic.publish();
|
||||
property.m_updateNetwork = (pub, time) -> pub.set(getter.get(), time);
|
||||
}
|
||||
if (setter != null) {
|
||||
property.m_sub = topic.subscribe(new float[] {});
|
||||
property.m_updateLocal =
|
||||
sub -> {
|
||||
for (float[] val : sub.readQueueValues()) {
|
||||
setter.accept(val);
|
||||
}
|
||||
};
|
||||
}
|
||||
m_properties.add(property);
|
||||
}
|
||||
@@ -344,23 +528,20 @@ public class SendableBuilderImpl implements NTSendableBuilder {
|
||||
@Override
|
||||
public void addDoubleArrayProperty(
|
||||
String key, Supplier<double[]> getter, Consumer<double[]> setter) {
|
||||
Property property = new Property(m_table, key);
|
||||
Property<DoubleArrayPublisher, DoubleArraySubscriber> property = new Property<>();
|
||||
DoubleArrayTopic topic = m_table.getDoubleArrayTopic(key);
|
||||
if (getter != null) {
|
||||
property.m_update = entry -> entry.setDoubleArray(getter.get());
|
||||
property.m_pub = topic.publish();
|
||||
property.m_updateNetwork = (pub, time) -> pub.set(getter.get(), time);
|
||||
}
|
||||
if (setter != null) {
|
||||
property.m_createListener =
|
||||
entry ->
|
||||
entry.addListener(
|
||||
event -> {
|
||||
if (event.value.isDoubleArray()) {
|
||||
SmartDashboard.postListenerTask(
|
||||
() -> setter.accept(event.value.getDoubleArray()));
|
||||
}
|
||||
},
|
||||
EntryListenerFlags.kImmediate
|
||||
| EntryListenerFlags.kNew
|
||||
| EntryListenerFlags.kUpdate);
|
||||
property.m_sub = topic.subscribe(new double[] {});
|
||||
property.m_updateLocal =
|
||||
sub -> {
|
||||
for (double[] val : sub.readQueueValues()) {
|
||||
setter.accept(val);
|
||||
}
|
||||
};
|
||||
}
|
||||
m_properties.add(property);
|
||||
}
|
||||
@@ -375,23 +556,20 @@ public class SendableBuilderImpl implements NTSendableBuilder {
|
||||
@Override
|
||||
public void addStringArrayProperty(
|
||||
String key, Supplier<String[]> getter, Consumer<String[]> setter) {
|
||||
Property property = new Property(m_table, key);
|
||||
Property<StringArrayPublisher, StringArraySubscriber> property = new Property<>();
|
||||
StringArrayTopic topic = m_table.getStringArrayTopic(key);
|
||||
if (getter != null) {
|
||||
property.m_update = entry -> entry.setStringArray(getter.get());
|
||||
property.m_pub = topic.publish();
|
||||
property.m_updateNetwork = (pub, time) -> pub.set(getter.get(), time);
|
||||
}
|
||||
if (setter != null) {
|
||||
property.m_createListener =
|
||||
entry ->
|
||||
entry.addListener(
|
||||
event -> {
|
||||
if (event.value.isStringArray()) {
|
||||
SmartDashboard.postListenerTask(
|
||||
() -> setter.accept(event.value.getStringArray()));
|
||||
}
|
||||
},
|
||||
EntryListenerFlags.kImmediate
|
||||
| EntryListenerFlags.kNew
|
||||
| EntryListenerFlags.kUpdate);
|
||||
property.m_sub = topic.subscribe(new String[] {});
|
||||
property.m_updateLocal =
|
||||
sub -> {
|
||||
for (String[] val : sub.readQueueValues()) {
|
||||
setter.accept(val);
|
||||
}
|
||||
};
|
||||
}
|
||||
m_properties.add(property);
|
||||
}
|
||||
@@ -400,55 +578,27 @@ public class SendableBuilderImpl implements NTSendableBuilder {
|
||||
* Add a raw property.
|
||||
*
|
||||
* @param key property name
|
||||
* @param typeString type string
|
||||
* @param getter getter function (returns current value)
|
||||
* @param setter setter function (sets new value)
|
||||
*/
|
||||
@Override
|
||||
public void addRawProperty(String key, Supplier<byte[]> getter, Consumer<byte[]> setter) {
|
||||
Property property = new Property(m_table, key);
|
||||
public void addRawProperty(
|
||||
String key, String typeString, Supplier<byte[]> getter, Consumer<byte[]> setter) {
|
||||
Property<RawPublisher, RawSubscriber> property = new Property<>();
|
||||
RawTopic topic = m_table.getRawTopic(key);
|
||||
if (getter != null) {
|
||||
property.m_update = entry -> entry.setRaw(getter.get());
|
||||
property.m_pub = topic.publish(typeString);
|
||||
property.m_updateNetwork = (pub, time) -> pub.set(getter.get(), time);
|
||||
}
|
||||
if (setter != null) {
|
||||
property.m_createListener =
|
||||
entry ->
|
||||
entry.addListener(
|
||||
event -> {
|
||||
if (event.value.isRaw()) {
|
||||
SmartDashboard.postListenerTask(() -> setter.accept(event.value.getRaw()));
|
||||
}
|
||||
},
|
||||
EntryListenerFlags.kImmediate
|
||||
| EntryListenerFlags.kNew
|
||||
| EntryListenerFlags.kUpdate);
|
||||
}
|
||||
m_properties.add(property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a NetworkTableValue property.
|
||||
*
|
||||
* @param key property name
|
||||
* @param getter getter function (returns current value)
|
||||
* @param setter setter function (sets new value)
|
||||
*/
|
||||
@Override
|
||||
public void addValueProperty(
|
||||
String key, Supplier<NetworkTableValue> getter, Consumer<NetworkTableValue> setter) {
|
||||
Property property = new Property(m_table, key);
|
||||
if (getter != null) {
|
||||
property.m_update = entry -> entry.setValue(getter.get());
|
||||
}
|
||||
if (setter != null) {
|
||||
property.m_createListener =
|
||||
entry ->
|
||||
entry.addListener(
|
||||
event -> {
|
||||
SmartDashboard.postListenerTask(() -> setter.accept(event.value));
|
||||
},
|
||||
EntryListenerFlags.kImmediate
|
||||
| EntryListenerFlags.kNew
|
||||
| EntryListenerFlags.kUpdate);
|
||||
property.m_sub = topic.subscribe(typeString, new byte[] {});
|
||||
property.m_updateLocal =
|
||||
sub -> {
|
||||
for (byte[] val : sub.readQueueValues()) {
|
||||
setter.accept(val);
|
||||
}
|
||||
};
|
||||
}
|
||||
m_properties.add(property);
|
||||
}
|
||||
|
||||
@@ -6,9 +6,12 @@ package edu.wpi.first.wpilibj.smartdashboard;
|
||||
|
||||
import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam;
|
||||
|
||||
import edu.wpi.first.networktables.IntegerPublisher;
|
||||
import edu.wpi.first.networktables.IntegerTopic;
|
||||
import edu.wpi.first.networktables.NTSendable;
|
||||
import edu.wpi.first.networktables.NTSendableBuilder;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.networktables.StringPublisher;
|
||||
import edu.wpi.first.networktables.StringTopic;
|
||||
import edu.wpi.first.util.sendable.SendableRegistry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -56,6 +59,14 @@ public class SendableChooser<V> implements NTSendable, AutoCloseable {
|
||||
@Override
|
||||
public void close() {
|
||||
SendableRegistry.remove(this);
|
||||
m_mutex.lock();
|
||||
try {
|
||||
for (StringPublisher pub : m_activePubs) {
|
||||
pub.close();
|
||||
}
|
||||
} finally {
|
||||
m_mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,13 +115,15 @@ public class SendableChooser<V> implements NTSendable, AutoCloseable {
|
||||
}
|
||||
|
||||
private String m_selected;
|
||||
private final List<NetworkTableEntry> m_activeEntries = new ArrayList<>();
|
||||
private final List<StringPublisher> m_activePubs = new ArrayList<>();
|
||||
private final ReentrantLock m_mutex = new ReentrantLock();
|
||||
|
||||
@Override
|
||||
public void initSendable(NTSendableBuilder builder) {
|
||||
builder.setSmartDashboardType("String Chooser");
|
||||
builder.getEntry(INSTANCE).setDouble(m_instance);
|
||||
IntegerPublisher instancePub = new IntegerTopic(builder.getTopic(INSTANCE)).publish();
|
||||
instancePub.set(m_instance);
|
||||
builder.addCloseable(instancePub);
|
||||
builder.addStringProperty(DEFAULT, () -> m_defaultChoice, null);
|
||||
builder.addStringArrayProperty(OPTIONS, () -> m_map.keySet().toArray(new String[0]), null);
|
||||
builder.addStringProperty(
|
||||
@@ -130,7 +143,7 @@ public class SendableChooser<V> implements NTSendable, AutoCloseable {
|
||||
null);
|
||||
m_mutex.lock();
|
||||
try {
|
||||
m_activeEntries.add(builder.getEntry(ACTIVE));
|
||||
m_activePubs.add(new StringTopic(builder.getTopic(ACTIVE)).publish());
|
||||
} finally {
|
||||
m_mutex.unlock();
|
||||
}
|
||||
@@ -141,8 +154,8 @@ public class SendableChooser<V> implements NTSendable, AutoCloseable {
|
||||
m_mutex.lock();
|
||||
try {
|
||||
m_selected = val;
|
||||
for (NetworkTableEntry entry : m_activeEntries) {
|
||||
entry.setString(val);
|
||||
for (StringPublisher pub : m_activePubs) {
|
||||
pub.set(val);
|
||||
}
|
||||
} finally {
|
||||
m_mutex.unlock();
|
||||
|
||||
@@ -11,7 +11,6 @@ import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.util.sendable.Sendable;
|
||||
import edu.wpi.first.util.sendable.SendableRegistry;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -25,8 +24,7 @@ import java.util.Set;
|
||||
*/
|
||||
public final class SmartDashboard {
|
||||
/** The {@link NetworkTable} used by {@link SmartDashboard}. */
|
||||
private static final NetworkTable table =
|
||||
NetworkTableInstance.getDefault().getTable("SmartDashboard");
|
||||
private static NetworkTable table;
|
||||
|
||||
/**
|
||||
* A table linking tables in the SmartDashboard to the {@link Sendable} objects they came from.
|
||||
@@ -38,6 +36,7 @@ public final class SmartDashboard {
|
||||
private static final ListenerExecutor listenerExecutor = new ListenerExecutor();
|
||||
|
||||
static {
|
||||
setNetworkTableInstance(NetworkTableInstance.getDefault());
|
||||
HAL.report(tResourceType.kResourceType_SmartDashboard, 0);
|
||||
}
|
||||
|
||||
@@ -45,6 +44,16 @@ public final class SmartDashboard {
|
||||
throw new UnsupportedOperationException("This is a utility class!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the NetworkTable instance used for entries. For testing purposes; use with caution.
|
||||
*
|
||||
* @param inst NetworkTable instance
|
||||
*/
|
||||
public static synchronized void setNetworkTableInstance(NetworkTableInstance inst) {
|
||||
SmartDashboard.table = inst.getTable("SmartDashboard");
|
||||
tablesToData.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the specified key to the specified value in this table. The key can not be null. The value
|
||||
* can be retrieved by calling the get method with a key that is equal to the original key.
|
||||
@@ -165,45 +174,6 @@ public final class SmartDashboard {
|
||||
return getEntry(key).isPersistent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets flags on the specified key in this table. The key can not be null.
|
||||
*
|
||||
* @param key the key name
|
||||
* @param flags the flags to set (bitmask)
|
||||
*/
|
||||
public static void setFlags(String key, int flags) {
|
||||
getEntry(key).setFlags(flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears flags on the specified key in this table. The key can not be null.
|
||||
*
|
||||
* @param key the key name
|
||||
* @param flags the flags to clear (bitmask)
|
||||
*/
|
||||
public static void clearFlags(String key, int flags) {
|
||||
getEntry(key).clearFlags(flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the flags for the specified key.
|
||||
*
|
||||
* @param key the key name
|
||||
* @return the flags, or 0 if the key is not defined
|
||||
*/
|
||||
public static int getFlags(String key) {
|
||||
return getEntry(key).getFlags();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the specified key in this table. The key can not be null.
|
||||
*
|
||||
* @param key the key name
|
||||
*/
|
||||
public static void delete(String key) {
|
||||
table.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a boolean in the table.
|
||||
*
|
||||
@@ -495,18 +465,6 @@ public final class SmartDashboard {
|
||||
return getEntry(key).setRaw(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a raw value (bytes from a byte buffer) in the table.
|
||||
*
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @param len the length of the value
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
public static boolean putRaw(String key, ByteBuffer value, int len) {
|
||||
return getEntry(key).setRaw(value, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user