Update Java SmartDashboard and LiveWindow to match C++

This commit is contained in:
Peter Johnson
2019-10-18 13:36:01 -07:00
parent 05c25deb7b
commit f3ad927f45
4 changed files with 133 additions and 81 deletions

View File

@@ -12,7 +12,6 @@ import edu.wpi.first.networktables.NetworkTableEntry;
import edu.wpi.first.networktables.NetworkTableInstance; import edu.wpi.first.networktables.NetworkTableInstance;
import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.Sendable;
import edu.wpi.first.wpilibj.command.Scheduler; import edu.wpi.first.wpilibj.command.Scheduler;
import edu.wpi.first.wpilibj.smartdashboard.SendableBuilderImpl;
import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry;
@@ -22,7 +21,6 @@ import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry;
*/ */
public class LiveWindow { public class LiveWindow {
private static class Component { private static class Component {
final SendableBuilderImpl m_builder = new SendableBuilderImpl();
boolean m_firstTime = true; boolean m_firstTime = true;
boolean m_telemetryEnabled = true; boolean m_telemetryEnabled = true;
} }
@@ -73,13 +71,9 @@ public class LiveWindow {
scheduler.removeAll(); scheduler.removeAll();
} else { } else {
System.out.println("stopping live window mode."); System.out.println("stopping live window mode.");
SendableRegistry.foreachLiveWindow(dataHandle, SendableRegistry.foreachLiveWindow(dataHandle, cbdata -> {
(sendable, name, subsystem, parent, data) -> { cbdata.builder.stopLiveWindowMode();
if (data != null) { });
((Component) data).m_builder.stopLiveWindowMode();
}
return data;
});
scheduler.enable(); scheduler.enable();
} }
enabledEntry.setBoolean(enabled); enabledEntry.setBoolean(enabled);
@@ -111,12 +105,11 @@ public class LiveWindow {
*/ */
public static synchronized void disableAllTelemetry() { public static synchronized void disableAllTelemetry() {
telemetryEnabled = false; telemetryEnabled = false;
SendableRegistry.foreachLiveWindow(dataHandle, (sendable, name, subsystem, parent, data) -> { SendableRegistry.foreachLiveWindow(dataHandle, cbdata -> {
if (data == null) { if (cbdata.data == null) {
data = new Component(); cbdata.data = new Component();
} }
((Component) data).m_telemetryEnabled = false; ((Component) cbdata.data).m_telemetryEnabled = false;
return data;
}); });
} }
@@ -133,19 +126,19 @@ public class LiveWindow {
return; return;
} }
SendableRegistry.foreachLiveWindow(dataHandle, (sendable, name, subsystem, parent, data) -> { SendableRegistry.foreachLiveWindow(dataHandle, cbdata -> {
if (sendable == null || parent != null) { if (cbdata.sendable == null || cbdata.parent != null) {
return data; return;
} }
if (data == null) { if (cbdata.data == null) {
data = new Component(); cbdata.data = new Component();
} }
Component component = (Component) data; Component component = (Component) cbdata.data;
if (!liveWindowEnabled && !component.m_telemetryEnabled) { if (!liveWindowEnabled && !component.m_telemetryEnabled) {
return data; return;
} }
if (component.m_firstTime) { if (component.m_firstTime) {
@@ -153,30 +146,29 @@ public class LiveWindow {
// components to be redefined. This allows default sensor and actuator // components to be redefined. This allows default sensor and actuator
// values to be created that are replaced with the custom names from // values to be created that are replaced with the custom names from
// users calling setName. // users calling setName.
if (name.isEmpty()) { if (cbdata.name.isEmpty()) {
return data; return;
} }
NetworkTable ssTable = liveWindowTable.getSubTable(subsystem); NetworkTable ssTable = liveWindowTable.getSubTable(cbdata.subsystem);
NetworkTable table; NetworkTable table;
// Treat name==subsystem as top level of subsystem // Treat name==subsystem as top level of subsystem
if (name.equals(subsystem)) { if (cbdata.name.equals(cbdata.subsystem)) {
table = ssTable; table = ssTable;
} else { } else {
table = ssTable.getSubTable(name); table = ssTable.getSubTable(cbdata.name);
} }
table.getEntry(".name").setString(name); table.getEntry(".name").setString(cbdata.name);
component.m_builder.setTable(table); cbdata.builder.setTable(table);
sendable.initSendable(component.m_builder); cbdata.sendable.initSendable(cbdata.builder);
ssTable.getEntry(".type").setString("LW Subsystem"); ssTable.getEntry(".type").setString("LW Subsystem");
component.m_firstTime = false; component.m_firstTime = false;
} }
if (startLiveWindow) { if (startLiveWindow) {
component.m_builder.startLiveWindowMode(); cbdata.builder.startLiveWindowMode();
} }
component.m_builder.updateTable(); cbdata.builder.updateTable();
return data;
}); });
startLiveWindow = false; startLiveWindow = false;

View File

@@ -79,6 +79,14 @@ public class SendableBuilderImpl implements SendableBuilder {
return m_table; return m_table;
} }
/**
* Return whether this sendable has an associated table.
* @return True if it has a table, false if not.
*/
public boolean hasTable() {
return m_table != null;
}
/** /**
* Return whether this sendable should be treated as an actuator. * Return whether this sendable should be treated as an actuator.
* *
@@ -148,6 +156,14 @@ public class SendableBuilderImpl implements SendableBuilder {
} }
} }
/**
* Clear properties.
*/
public void clearProperties() {
stopListeners();
m_properties.clear();
}
/** /**
* Set the string representation of the named data type that will be used by the smart dashboard * Set the string representation of the named data type that will be used by the smart dashboard
* for this sendable. * for this sendable.

View File

@@ -11,7 +11,9 @@ import java.lang.ref.WeakReference;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.function.Consumer;
import edu.wpi.first.networktables.NetworkTable;
import edu.wpi.first.wpilibj.DriverStation; import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.Sendable;
@@ -30,6 +32,7 @@ public class SendableRegistry {
} }
WeakReference<Sendable> m_sendable; WeakReference<Sendable> m_sendable;
SendableBuilderImpl m_builder = new SendableBuilderImpl();
String m_name; String m_name;
String m_subsystem = "Ungrouped"; String m_subsystem = "Ungrouped";
WeakReference<Sendable> m_parent; WeakReference<Sendable> m_parent;
@@ -384,21 +387,71 @@ public class SendableRegistry {
} }
/** /**
* Functional interface for foreachLiveWindow(). * Publishes an object in the registry to a network table.
*
* @param sendable object
* @param table network table
*/ */
@FunctionalInterface public static synchronized void publish(Sendable sendable, NetworkTable table) {
public interface LiveWindowForeachCallback { Component comp = getOrAdd(sendable);
comp.m_builder.clearProperties();
comp.m_builder.setTable(table);
sendable.initSendable(comp.m_builder);
comp.m_builder.updateTable();
comp.m_builder.startListeners();
}
/**
* Updates network table information from an object.
*
* @param sendable object
*/
public static synchronized void update(Sendable sendable) {
Component comp = components.get(sendable);
if (comp != null) {
comp.m_builder.updateTable();
}
}
/**
* Data passed to foreachLiveWindow() callback function.
*/
public static class CallbackData {
/** /**
* Callback. * Sendable object.
*
* @param sendable sendable object
* @param name name
* @param subsystem subsystem
* @param parent parent sendable object
* @param data data stored in object with setData()
* @return data to be stored back into object, or null if none/don't modify
*/ */
Object call(Sendable sendable, String name, String subsystem, Sendable parent, Object data); @SuppressWarnings("MemberName")
public Sendable sendable;
/**
* Name.
*/
@SuppressWarnings("MemberName")
public String name;
/**
* Subsystem.
*/
@SuppressWarnings("MemberName")
public String subsystem;
/**
* Parent sendable object.
*/
@SuppressWarnings("MemberName")
public Sendable parent;
/**
* Data stored in object with setData(). Update this to change the data.
*/
@SuppressWarnings("MemberName")
public Object data;
/**
* Sendable builder for the sendable.
*/
@SuppressWarnings("MemberName")
public SendableBuilderImpl builder;
} }
/** /**
@@ -406,26 +459,32 @@ public class SendableRegistry {
* It is *not* safe to call other SendableRegistry functions from the * It is *not* safe to call other SendableRegistry functions from the
* callback. * callback.
* *
* @param dataHandle data handle to get data pointer passed to callback * @param dataHandle data handle to get data object passed to callback
* @param callback function to call for each object * @param callback function to call for each object
*/ */
@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.AvoidInstantiatingObjectsInLoops", @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.AvoidInstantiatingObjectsInLoops",
"PMD.AvoidCatchingThrowable"}) "PMD.AvoidCatchingThrowable"})
public static synchronized void foreachLiveWindow(int dataHandle, public static synchronized void foreachLiveWindow(int dataHandle,
LiveWindowForeachCallback callback) { Consumer<CallbackData> callback) {
CallbackData cbdata = new CallbackData();
for (Component comp : components.values()) { for (Component comp : components.values()) {
Sendable sendable = comp.m_sendable.get(); cbdata.sendable = comp.m_sendable.get();
if (sendable != null && comp.m_liveWindow) { if (cbdata.sendable != null && comp.m_liveWindow) {
Sendable parent = null; cbdata.name = comp.m_name;
cbdata.subsystem = comp.m_subsystem;
if (comp.m_parent != null) { if (comp.m_parent != null) {
parent = comp.m_parent.get(); cbdata.parent = comp.m_parent.get();
} else {
cbdata.parent = null;
} }
Object data = null;
if (comp.m_data != null && dataHandle < comp.m_data.length) { if (comp.m_data != null && dataHandle < comp.m_data.length) {
data = comp.m_data[dataHandle]; cbdata.data = comp.m_data[dataHandle];
} else {
cbdata.data = null;
} }
cbdata.builder = comp.m_builder;
try { try {
data = callback.call(sendable, comp.m_name, comp.m_subsystem, parent, data); callback.accept(cbdata);
} catch (Throwable throwable) { } catch (Throwable throwable) {
Throwable cause = throwable.getCause(); Throwable cause = throwable.getCause();
if (cause != null) { if (cause != null) {
@@ -436,13 +495,13 @@ public class SendableRegistry {
+ throwable.toString(), false); + throwable.toString(), false);
comp.m_liveWindow = false; comp.m_liveWindow = false;
} }
if (data != null) { if (cbdata.data != null) {
if (comp.m_data == null) { if (comp.m_data == null) {
comp.m_data = new Object[dataHandle + 1]; comp.m_data = new Object[dataHandle + 1];
} else if (dataHandle >= comp.m_data.length) { } else if (dataHandle >= comp.m_data.length) {
comp.m_data = Arrays.copyOf(comp.m_data, dataHandle + 1); comp.m_data = Arrays.copyOf(comp.m_data, dataHandle + 1);
} }
comp.m_data[dataHandle] = data; comp.m_data[dataHandle] = cbdata.data;
} }
} }
} }

View File

@@ -27,28 +27,19 @@ import edu.wpi.first.wpilibj.Sendable;
* laptop. Users can put values into and get values from the SmartDashboard. * laptop. Users can put values into and get values from the SmartDashboard.
*/ */
@SuppressWarnings({"PMD.GodClass", "PMD.TooManyMethods"}) @SuppressWarnings({"PMD.GodClass", "PMD.TooManyMethods"})
public class SmartDashboard { public final class SmartDashboard {
/** /**
* The {@link NetworkTable} used by {@link SmartDashboard}. * The {@link NetworkTable} used by {@link SmartDashboard}.
*/ */
private static final NetworkTable table = private static final NetworkTable table =
NetworkTableInstance.getDefault().getTable("SmartDashboard"); NetworkTableInstance.getDefault().getTable("SmartDashboard");
private static class Data {
Data(Sendable sendable) {
m_sendable = sendable;
}
final Sendable m_sendable;
final SendableBuilderImpl m_builder = new SendableBuilderImpl();
}
/** /**
* A table linking tables in the SmartDashboard to the {@link Sendable} objects they * A table linking tables in the SmartDashboard to the {@link Sendable} objects they
* came from. * came from.
*/ */
@SuppressWarnings("PMD.UseConcurrentHashMap") @SuppressWarnings("PMD.UseConcurrentHashMap")
private static final Map<String, Data> tablesToData = new HashMap<>(); private static final Map<String, Sendable> tablesToData = new HashMap<>();
/** /**
* The executor for listener tasks; calls listener tasks synchronously from main thread. * The executor for listener tasks; calls listener tasks synchronously from main thread.
@@ -71,19 +62,13 @@ public class SmartDashboard {
* @param data the value * @param data the value
* @throws IllegalArgumentException If key is null * @throws IllegalArgumentException If key is null
*/ */
@SuppressWarnings("PMD.CompareObjectsWithEquals")
public static synchronized void putData(String key, Sendable data) { public static synchronized void putData(String key, Sendable data) {
Data sddata = tablesToData.get(key); Sendable sddata = tablesToData.get(key);
if (sddata == null || sddata.m_sendable != data) { if (sddata == null || sddata != data) {
if (sddata != null) { tablesToData.put(key, data);
sddata.m_builder.stopListeners();
}
sddata = new Data(data);
tablesToData.put(key, sddata);
NetworkTable dataTable = table.getSubTable(key); NetworkTable dataTable = table.getSubTable(key);
sddata.m_builder.setTable(dataTable); SendableRegistry.publish(data, dataTable);
data.initSendable(sddata.m_builder);
sddata.m_builder.updateTable();
sddata.m_builder.startListeners();
dataTable.getEntry(".name").setString(key); dataTable.getEntry(".name").setString(key);
} }
} }
@@ -111,11 +96,11 @@ public class SmartDashboard {
* @throws IllegalArgumentException if the key is null * @throws IllegalArgumentException if the key is null
*/ */
public static synchronized Sendable getData(String key) { public static synchronized Sendable getData(String key) {
Data data = tablesToData.get(key); Sendable data = tablesToData.get(key);
if (data == null) { if (data == null) {
throw new IllegalArgumentException("SmartDashboard data does not exist: " + key); throw new IllegalArgumentException("SmartDashboard data does not exist: " + key);
} else { } else {
return data.m_sendable; return data;
} }
} }
@@ -543,8 +528,8 @@ public class SmartDashboard {
* Puts all sendable data to the dashboard. * Puts all sendable data to the dashboard.
*/ */
public static synchronized void updateValues() { public static synchronized void updateValues() {
for (Data data : tablesToData.values()) { for (Sendable data : tablesToData.values()) {
data.m_builder.updateTable(); SendableRegistry.update(data);
} }
// Execute posted listener tasks // Execute posted listener tasks
listenerExecutor.runListenerTasks(); listenerExecutor.runListenerTasks();