From f3ad927f45aa7a8e535313da2cb29ee8aa2cfb8e Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Fri, 18 Oct 2019 13:36:01 -0700 Subject: [PATCH] Update Java SmartDashboard and LiveWindow to match C++ --- .../first/wpilibj/livewindow/LiveWindow.java | 56 ++++------ .../smartdashboard/SendableBuilderImpl.java | 16 +++ .../smartdashboard/SendableRegistry.java | 105 ++++++++++++++---- .../smartdashboard/SmartDashboard.java | 37 ++---- 4 files changed, 133 insertions(+), 81 deletions(-) diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/livewindow/LiveWindow.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/livewindow/LiveWindow.java index b5972a9080..f545b7d610 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/livewindow/LiveWindow.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/livewindow/LiveWindow.java @@ -12,7 +12,6 @@ import edu.wpi.first.networktables.NetworkTableEntry; import edu.wpi.first.networktables.NetworkTableInstance; import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.command.Scheduler; -import edu.wpi.first.wpilibj.smartdashboard.SendableBuilderImpl; import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; @@ -22,7 +21,6 @@ import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; */ public class LiveWindow { private static class Component { - final SendableBuilderImpl m_builder = new SendableBuilderImpl(); boolean m_firstTime = true; boolean m_telemetryEnabled = true; } @@ -73,13 +71,9 @@ public class LiveWindow { scheduler.removeAll(); } else { System.out.println("stopping live window mode."); - SendableRegistry.foreachLiveWindow(dataHandle, - (sendable, name, subsystem, parent, data) -> { - if (data != null) { - ((Component) data).m_builder.stopLiveWindowMode(); - } - return data; - }); + SendableRegistry.foreachLiveWindow(dataHandle, cbdata -> { + cbdata.builder.stopLiveWindowMode(); + }); scheduler.enable(); } enabledEntry.setBoolean(enabled); @@ -111,12 +105,11 @@ public class LiveWindow { */ public static synchronized void disableAllTelemetry() { telemetryEnabled = false; - SendableRegistry.foreachLiveWindow(dataHandle, (sendable, name, subsystem, parent, data) -> { - if (data == null) { - data = new Component(); + SendableRegistry.foreachLiveWindow(dataHandle, cbdata -> { + if (cbdata.data == null) { + cbdata.data = new Component(); } - ((Component) data).m_telemetryEnabled = false; - return data; + ((Component) cbdata.data).m_telemetryEnabled = false; }); } @@ -133,19 +126,19 @@ public class LiveWindow { return; } - SendableRegistry.foreachLiveWindow(dataHandle, (sendable, name, subsystem, parent, data) -> { - if (sendable == null || parent != null) { - return data; + SendableRegistry.foreachLiveWindow(dataHandle, cbdata -> { + if (cbdata.sendable == null || cbdata.parent != null) { + return; } - if (data == null) { - data = new Component(); + if (cbdata.data == null) { + cbdata.data = new Component(); } - Component component = (Component) data; + Component component = (Component) cbdata.data; if (!liveWindowEnabled && !component.m_telemetryEnabled) { - return data; + return; } if (component.m_firstTime) { @@ -153,30 +146,29 @@ public class LiveWindow { // components to be redefined. This allows default sensor and actuator // values to be created that are replaced with the custom names from // users calling setName. - if (name.isEmpty()) { - return data; + if (cbdata.name.isEmpty()) { + return; } - NetworkTable ssTable = liveWindowTable.getSubTable(subsystem); + NetworkTable ssTable = liveWindowTable.getSubTable(cbdata.subsystem); NetworkTable table; // Treat name==subsystem as top level of subsystem - if (name.equals(subsystem)) { + if (cbdata.name.equals(cbdata.subsystem)) { table = ssTable; } else { - table = ssTable.getSubTable(name); + table = ssTable.getSubTable(cbdata.name); } - table.getEntry(".name").setString(name); - component.m_builder.setTable(table); - sendable.initSendable(component.m_builder); + table.getEntry(".name").setString(cbdata.name); + cbdata.builder.setTable(table); + cbdata.sendable.initSendable(cbdata.builder); ssTable.getEntry(".type").setString("LW Subsystem"); component.m_firstTime = false; } if (startLiveWindow) { - component.m_builder.startLiveWindowMode(); + cbdata.builder.startLiveWindowMode(); } - component.m_builder.updateTable(); - return data; + cbdata.builder.updateTable(); }); startLiveWindow = false; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableBuilderImpl.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableBuilderImpl.java index 01ddf27a6e..e0bb4f938e 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableBuilderImpl.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableBuilderImpl.java @@ -79,6 +79,14 @@ public class SendableBuilderImpl implements SendableBuilder { 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. * @@ -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 * for this sendable. diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableRegistry.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableRegistry.java index 00abd59472..c19747ee2a 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableRegistry.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableRegistry.java @@ -11,7 +11,9 @@ import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.Map; 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.Sendable; @@ -30,6 +32,7 @@ public class SendableRegistry { } WeakReference m_sendable; + SendableBuilderImpl m_builder = new SendableBuilderImpl(); String m_name; String m_subsystem = "Ungrouped"; WeakReference 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 interface LiveWindowForeachCallback { + public static synchronized void publish(Sendable sendable, NetworkTable table) { + 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. - * - * @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 + * Sendable object. */ - 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 * 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 */ @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.AvoidInstantiatingObjectsInLoops", "PMD.AvoidCatchingThrowable"}) public static synchronized void foreachLiveWindow(int dataHandle, - LiveWindowForeachCallback callback) { + Consumer callback) { + CallbackData cbdata = new CallbackData(); for (Component comp : components.values()) { - Sendable sendable = comp.m_sendable.get(); - if (sendable != null && comp.m_liveWindow) { - Sendable parent = null; + cbdata.sendable = comp.m_sendable.get(); + if (cbdata.sendable != null && comp.m_liveWindow) { + cbdata.name = comp.m_name; + cbdata.subsystem = comp.m_subsystem; 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) { - data = comp.m_data[dataHandle]; + cbdata.data = comp.m_data[dataHandle]; + } else { + cbdata.data = null; } + cbdata.builder = comp.m_builder; try { - data = callback.call(sendable, comp.m_name, comp.m_subsystem, parent, data); + callback.accept(cbdata); } catch (Throwable throwable) { Throwable cause = throwable.getCause(); if (cause != null) { @@ -436,13 +495,13 @@ public class SendableRegistry { + throwable.toString(), false); comp.m_liveWindow = false; } - if (data != null) { + if (cbdata.data != null) { if (comp.m_data == null) { comp.m_data = new Object[dataHandle + 1]; } else if (dataHandle >= comp.m_data.length) { comp.m_data = Arrays.copyOf(comp.m_data, dataHandle + 1); } - comp.m_data[dataHandle] = data; + comp.m_data[dataHandle] = cbdata.data; } } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SmartDashboard.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SmartDashboard.java index 50868f2818..457ae46e02 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SmartDashboard.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SmartDashboard.java @@ -27,28 +27,19 @@ import edu.wpi.first.wpilibj.Sendable; * laptop. Users can put values into and get values from the SmartDashboard. */ @SuppressWarnings({"PMD.GodClass", "PMD.TooManyMethods"}) -public class SmartDashboard { +public final class SmartDashboard { /** * The {@link NetworkTable} used by {@link SmartDashboard}. */ private static final NetworkTable table = 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 * came from. */ @SuppressWarnings("PMD.UseConcurrentHashMap") - private static final Map tablesToData = new HashMap<>(); + private static final Map tablesToData = new HashMap<>(); /** * The executor for listener tasks; calls listener tasks synchronously from main thread. @@ -71,19 +62,13 @@ public class SmartDashboard { * @param data the value * @throws IllegalArgumentException If key is null */ + @SuppressWarnings("PMD.CompareObjectsWithEquals") public static synchronized void putData(String key, Sendable data) { - Data sddata = tablesToData.get(key); - if (sddata == null || sddata.m_sendable != data) { - if (sddata != null) { - sddata.m_builder.stopListeners(); - } - sddata = new Data(data); - tablesToData.put(key, sddata); + Sendable sddata = tablesToData.get(key); + if (sddata == null || sddata != data) { + tablesToData.put(key, data); NetworkTable dataTable = table.getSubTable(key); - sddata.m_builder.setTable(dataTable); - data.initSendable(sddata.m_builder); - sddata.m_builder.updateTable(); - sddata.m_builder.startListeners(); + SendableRegistry.publish(data, dataTable); dataTable.getEntry(".name").setString(key); } } @@ -111,11 +96,11 @@ public class SmartDashboard { * @throws IllegalArgumentException if the key is null */ public static synchronized Sendable getData(String key) { - Data data = tablesToData.get(key); + Sendable data = tablesToData.get(key); if (data == null) { throw new IllegalArgumentException("SmartDashboard data does not exist: " + key); } else { - return data.m_sendable; + return data; } } @@ -543,8 +528,8 @@ public class SmartDashboard { * Puts all sendable data to the dashboard. */ public static synchronized void updateValues() { - for (Data data : tablesToData.values()) { - data.m_builder.updateTable(); + for (Sendable data : tablesToData.values()) { + SendableRegistry.update(data); } // Execute posted listener tasks listenerExecutor.runListenerTasks();