summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Johnson <johnson.peter@gmail.com>2015-10-07 22:00:08 -0700
committerPeter Johnson <johnson.peter@gmail.com>2015-12-06 08:24:21 -0800
commitd27dbc49b420e0a86904e67ec2efbaf5bb67e3fb (patch)
tree676bb5816c749baa34ba314657c825af17e4cea3
parent057adc6df4b04bfbb2ef651c379a48e551a6d17d (diff)
Update to use ntcore and new Preferences.
This significantly upgrades the Preferences GUI to: - Show type information - Allow editing of all data types including arrays - Implement "real" deletion - Support local saves and loads Change-Id: Ic43281de9c1aea52e65d173328a8b10ec4f61259
-rw-r--r--smartdashboard/build.gradle64
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/LogToCSV.java2
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardMenu.java4
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPanel.java12
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ConnectionIndicator.java13
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/RobotPreferences.java511
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Scheduler.java2
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractTableWidget.java4
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LWSubsystem.java4
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/robot/Robot.java49
10 files changed, 539 insertions, 126 deletions
diff --git a/smartdashboard/build.gradle b/smartdashboard/build.gradle
index c85fee5..b1fcaf6 100644
--- a/smartdashboard/build.gradle
+++ b/smartdashboard/build.gradle
@@ -1,53 +1,59 @@
apply plugin: 'java'
-apply plugin: 'maven-publish'
apply plugin: 'application'
+apply plugin: 'com.github.johnrengelman.shadow'
+apply plugin: 'maven-publish'
-mainClassName = "edu.wpi.first.smartdashboard.main"
-
-sourceSets {
- main {
- java.srcDir "src/"
+buildscript {
+ repositories { jcenter() }
+ dependencies {
+ classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.1'
}
}
-dependencies {
- compile 'edu.wpi.first.wpilib.networktables.java:NetworkTables:0.1.0-SNAPSHOT'
- compile 'junit:junit:4.12'
- compile 'jcommon:jcommon:0.9.5'
- compile 'jfreechart:jfreechart:1.0.0'
+if (!hasProperty('repo')) {
+ ext.repo = 'development'
}
-repositories {
- maven {
- url "http://first.wpi.edu/FRC/roborio/maven/"
- }
- mavenCentral()
-}
+mainClassName = "edu.wpi.first.smartdashboard.main"
publishing {
publications {
maven(MavenPublication) {
- from components.java
-
- artifact (jar) {
- classifier = 'jar'
+ artifact(shadowJar) {
+ classifier null
}
-
groupId 'edu.wpi.first.wpilib'
artifactId 'SmartDashboard'
version '1.0.0-SNAPSHOT'
}
}
- repositories {
- maven {
- url "file://${System.properties['user.home']}/releases/maven"
+ repositories.maven {
+ url = "${System.getProperty('user.home')}/releases/maven/$repo"
+ }
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs = ["src"]
}
}
}
-jar {
- manifest {
- attributes 'Main-Class': 'edu.wpi.first.smartdashboard.main'
+repositories {
+ mavenCentral()
+ maven {
+ url = "${System.getProperty('user.home')}/releases/maven/$repo"
}
- from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
+ mavenLocal()
+ maven {
+ url = "http://first.wpi.edu/FRC/roborio/maven/$repo"
+ }
+}
+
+dependencies {
+ compile "edu.wpi.first.wpilib.networktables.java:NetworkTables:3.0.0-SNAPSHOT:desktop"
+ compile 'junit:junit:4.12'
+ compile 'jcommon:jcommon:0.9.5'
+ compile 'jfreechart:jfreechart:1.0.0'
}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/LogToCSV.java b/smartdashboard/src/edu/wpi/first/smartdashboard/LogToCSV.java
index 9c5e08b..68a103a 100644
--- a/smartdashboard/src/edu/wpi/first/smartdashboard/LogToCSV.java
+++ b/smartdashboard/src/edu/wpi/first/smartdashboard/LogToCSV.java
@@ -38,7 +38,7 @@ public class LogToCSV implements ITableListener {
m_fw = new FileWriter(path);
m_fw.write("Time (ms),Name,Value" + s_lineSeparator);
m_fw.flush();
- Robot.getTable().addTableListener(this, true);
+ Robot.getTable().addTableListenerEx(this, ITable.NOTIFY_IMMEDIATE | ITable.NOTIFY_LOCAL | ITable.NOTIFY_NEW | ITable.NOTIFY_UPDATE);
} catch (IOException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(null, "An error occurred when attempting to " + "open the output CSV file for writing. "
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardMenu.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardMenu.java
index 28ea979..18a4f01 100644
--- a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardMenu.java
+++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardMenu.java
@@ -150,7 +150,7 @@ public class DashboardMenu extends JMenuBar {
}
});
resetLW.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.CTRL_DOWN_MASK));
- Robot.getLiveWindow().getSubTable("~STATUS~").addTableListener("LW Enabled", new ITableListener() {
+ Robot.getLiveWindow().getSubTable("~STATUS~").addTableListenerEx("LW Enabled", new ITableListener() {
public void valueChanged(ITable itable, String string, Object o, boolean bln) {
final boolean isInLW = Robot.getLiveWindow().getSubTable("~STATUS~").getBoolean("LW Enabled", false);
@@ -170,7 +170,7 @@ public class DashboardMenu extends JMenuBar {
}
});
}
- }, true);
+ }, ITable.NOTIFY_IMMEDIATE | ITable.NOTIFY_LOCAL | ITable.NOTIFY_NEW | ITable.NOTIFY_UPDATE);
viewMenu.add(resetLW);
JMenu addMenu = new JMenu("Add...");
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPanel.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPanel.java
index 386a134..948f9c1 100644
--- a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPanel.java
+++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPanel.java
@@ -69,8 +69,8 @@ public class DashboardPanel extends JPanel {
setEditable(editable);
- table.addTableListener(listener, true);
- table.addSubTableListener(listener);
+ table.addTableListenerEx(listener, ITable.NOTIFY_IMMEDIATE | ITable.NOTIFY_LOCAL | ITable.NOTIFY_NEW | ITable.NOTIFY_UPDATE);
+ table.addSubTableListener(listener, true);
}
public ITable getTable() {
@@ -179,8 +179,8 @@ public class DashboardPanel extends JPanel {
table.removeTableListener(listener);
- table.addTableListener(listener, true);
- table.addSubTableListener(listener);
+ table.addTableListenerEx(listener, ITable.NOTIFY_IMMEDIATE | ITable.NOTIFY_LOCAL | ITable.NOTIFY_NEW | ITable.NOTIFY_UPDATE);
+ table.addSubTableListener(listener, true);
repaint();
}
@@ -514,7 +514,7 @@ public class DashboardPanel extends JPanel {
if (!hiddenFields.contains(key)) {
if (value instanceof ITable) {
final ITable table = (ITable) value;
- table.addTableListener("~TYPE~", new ITableListener() {
+ table.addTableListenerEx("~TYPE~", new ITableListener() {
public void valueChanged(final ITable typeSource, final String typeKey, final Object typeValue, final boolean typeIsNew) {
table.removeTableListener(this);
SwingUtilities.invokeLater(new Runnable() {
@@ -523,7 +523,7 @@ public class DashboardPanel extends JPanel {
}
});
}
- }, true);
+ }, ITable.NOTIFY_IMMEDIATE | ITable.NOTIFY_LOCAL | ITable.NOTIFY_NEW | ITable.NOTIFY_UPDATE);
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ConnectionIndicator.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ConnectionIndicator.java
index da18f20..ccfb89c 100644
--- a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ConnectionIndicator.java
+++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ConnectionIndicator.java
@@ -23,7 +23,6 @@ public class ConnectionIndicator extends StaticWidget implements IRemoteConnecti
public final ColorProperty positive = new ColorProperty(this, "Connection Color", Color.GREEN);
public final ColorProperty negative = new ColorProperty(this, "No Connection Color", Color.RED);
public final MultiProperty display = new MultiProperty(this, "Graphics");
- private boolean firstRun = true;
private boolean connected = false;
private Runnable repainter = new Runnable() {
@@ -91,23 +90,19 @@ public class ConnectionIndicator extends StaticWidget implements IRemoteConnecti
@Override
public void connected(IRemote remote) {
+ System.out.println("ConnectionIndicator CONNECTED");
if (!connected) {
connected = true;
- if (!firstRun) {
- SwingUtilities.invokeLater(repainter);
- }
+ SwingUtilities.invokeLater(repainter);
}
- firstRun = false;
}
@Override
public void disconnected(IRemote remote) {
+ System.out.println("ConnectionIndicator DISCONNECTED");
if (connected) {
connected = false;
- if (!firstRun) {
- SwingUtilities.invokeLater(repainter);
- }
+ SwingUtilities.invokeLater(repainter);
}
- firstRun = false;
}
}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/RobotPreferences.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/RobotPreferences.java
index 9cb35b2..cf640e8 100644
--- a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/RobotPreferences.java
+++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/RobotPreferences.java
@@ -6,10 +6,13 @@ import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
+import org.jfree.ui.ExtensionFileFilter;
import edu.wpi.first.smartdashboard.gui.*;
import edu.wpi.first.smartdashboard.properties.*;
import edu.wpi.first.smartdashboard.robot.Robot;
+import edu.wpi.first.wpilibj.networktables.NetworkTable;
+import edu.wpi.first.wpilibj.networktables.PersistentException;
import edu.wpi.first.wpilibj.tables.*;
/**
@@ -18,14 +21,14 @@ import edu.wpi.first.wpilibj.tables.*;
*/
public class RobotPreferences extends StaticWidget implements ITableListener {
- public static final String DELETED_VALUE = "\"";
public static final String NAME = "Robot Preferences";
private JTable table;
private PreferenceTableModel model;
- private Map<String, String> values;
- private JButton save;
+ private Map<String, Object> values;
private JButton add;
private JButton remove;
+ private JButton save;
+ private JButton load;
@Override
public void init() {
@@ -36,7 +39,7 @@ public class RobotPreferences extends StaticWidget implements ITableListener {
NewPreferenceEntryDialog dialog = new NewPreferenceEntryDialog();
dialog.show(remove.getLocationOnScreen());
if (!dialog.isCanceled()) {
- model.put(dialog.getKey(), dialog.getValue());
+ model.putString(dialog.getKey(), dialog.getValue(), dialog.getValueType());
}
}
});
@@ -48,7 +51,7 @@ public class RobotPreferences extends StaticWidget implements ITableListener {
if (table.isEditing()) {
table.getCellEditor().cancelCellEditing();
}
- Map.Entry<String, String> entry = model.getRow(table.getSelectedRow());
+ Map.Entry<String, Object> entry = model.getRow(table.getSelectedRow());
if (entry != null) {
model.delete(entry.getKey());
}
@@ -59,13 +62,50 @@ public class RobotPreferences extends StaticWidget implements ITableListener {
save.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
- Robot.getPreferences().putBoolean(Robot.PREF_SAVE_FIELD, true);
+ if (table.isEditing()) {
+ table.getCellEditor().cancelCellEditing();
+ }
+ JFileChooser fc = new JFileChooser(".");
+ fc.addChoosableFileFilter(new ExtensionFileFilter("INI File", ".ini"));
+ fc.setApproveButtonText("Save");
+ fc.setDialogTitle("Save Preferences As...");
+
+ fc.setMultiSelectionEnabled(false);
+ fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
+
+ if (fc.showOpenDialog(RobotPreferences.this) == JFileChooser.APPROVE_OPTION) {
+ String filepath = fc.getSelectedFile().getAbsolutePath();
+ if (!filepath.endsWith(".ini")) {
+ filepath = filepath + ".ini";
+ }
+ model.save(filepath);
+ }
}
});
- values = new LinkedHashMap<String, String>();
+ load = new JButton("Load");
+ load.addActionListener(new ActionListener() {
- Robot.getPreferences().addTableListener(this, true);
+ public void actionPerformed(ActionEvent e) {
+ if (table.isEditing()) {
+ table.getCellEditor().cancelCellEditing();
+ }
+ JFileChooser fc = new JFileChooser(".");
+ fc.addChoosableFileFilter(new ExtensionFileFilter("INI File", ".ini"));
+ fc.setMultiSelectionEnabled(false);
+ fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
+ fc.setApproveButtonText("Load");
+ fc.setDialogTitle("Load Preferences");
+
+ if (fc.showOpenDialog(RobotPreferences.this) == JFileChooser.APPROVE_OPTION) {
+ model.load(fc.getSelectedFile().getAbsolutePath());
+ }
+ }
+ });
+
+ values = new LinkedHashMap<String, Object>();
+
+ Robot.getPreferences().addTableListenerEx(this, ITable.NOTIFY_IMMEDIATE | ITable.NOTIFY_LOCAL | ITable.NOTIFY_NEW | ITable.NOTIFY_DELETE | ITable.NOTIFY_UPDATE);
model = new PreferenceTableModel();
@@ -75,14 +115,15 @@ public class RobotPreferences extends StaticWidget implements ITableListener {
JPanel buttonPanel = new JPanel();
- buttonPanel.setLayout(new GridLayout(0, 2));
+ buttonPanel.setLayout(new GridLayout(0, 4));
buttonPanel.add(add);
buttonPanel.add(remove);
+ buttonPanel.add(save);
+ buttonPanel.add(load);
JPanel controlPanel = new JPanel();
controlPanel.setLayout(new BorderLayout());
controlPanel.add(buttonPanel, BorderLayout.NORTH);
- controlPanel.add(save, BorderLayout.SOUTH);
setLayout(new BorderLayout());
JScrollPane tableScrollPane = new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
@@ -101,16 +142,15 @@ public class RobotPreferences extends StaticWidget implements ITableListener {
public void propertyChanged(Property property) {
}
- @Override
- public void valueChanged(ITable source, String key, Object value, boolean isNew) {
- if (key.equals(Robot.PREF_SAVE_FIELD)) {
- save.setEnabled(!(Boolean) value);
+ @Override
+ public void valueChanged(ITable source, String key, Object value, boolean isNew) {}
+
+ @Override
+ public void valueChangedEx(ITable source, String key, Object value, int flags) {
+ if ((flags & ITable.NOTIFY_DELETE) != 0) {
+ values.remove(key);
} else {
- if (DELETED_VALUE.equals(value.toString())) {
- values.remove(key);
- } else {
- values.put(key, value.toString());
- }
+ values.put(key, value);
}
if (model != null) {
@@ -118,6 +158,169 @@ public class RobotPreferences extends StaticWidget implements ITableListener {
}
}
+ private static final String[] typeNames = new String[] {"Boolean", "Number", "String", "Raw", "Boolean[]", "Number[]", "String[]"};
+
+ private static String getTypeName(Object value) {
+ if (value instanceof Boolean) return typeNames[0];
+ if (value instanceof Double) return typeNames[1];
+ if (value instanceof String) return typeNames[2];
+ if (value instanceof byte[]) return typeNames[3];
+ if (value instanceof boolean[]) return typeNames[4].substring(0, 8) + ((boolean[])value).length + "]";
+ if (value instanceof double[]) return typeNames[5].substring(0, 7) + ((double[])value).length + "]";
+ if (value instanceof String[]) return typeNames[6].substring(0, 7) + ((String[])value).length + "]";
+ return "ERROR";
+ }
+
+ private static int getTypeIndex(String name) {
+ if (name.equals("Boolean")) return 0;
+ if (name.equals("Number")) return 1;
+ if (name.equals("String")) return 2;
+ if (name.equals("Raw")) return 3;
+ if (name.startsWith("Boolean[")) return 4;
+ if (name.startsWith("Number[")) return 5;
+ if (name.startsWith("String[")) return 6;
+ return -1;
+ }
+
+ private static String hex(char ch) {
+ return Integer.toHexString(ch).toLowerCase(Locale.ENGLISH);
+ }
+
+ private static void escapeString(StringBuilder out, String str, boolean inArray) {
+ int sz = str.length();
+ for (int i = 0; i < sz; i++) {
+ char ch = str.charAt(i);
+ // handle unicode
+ /*if (ch > 0xfff) {
+ out.append("\\u" + hex(ch));
+ } else if (ch > 0xff) {
+ out.append("\\u0" + hex(ch));
+ } else if (ch > 0x7f) {
+ out.append("\\u00" + hex(ch));
+ } else*/ if (ch < 32) {
+ switch (ch) {
+ case '\b':
+ out.append("\\b");
+ break;
+ case '\n':
+ out.append("\\n");
+ break;
+ case '\t':
+ out.append("\\t");
+ break;
+ case '\f':
+ out.append("\\f");
+ break;
+ case '\r':
+ out.append("\\r");
+ break;
+ default:
+ if (ch > 0xf) {
+ out.append("\\u00" + hex(ch));
+ } else {
+ out.append("\\u000" + hex(ch));
+ }
+ break;
+ }
+ } else {
+ switch (ch) {
+ case ',': case ']':
+ if (inArray) {
+ out.append('\\');
+ }
+ out.append(ch);
+ break;
+ case '\\' :
+ out.append("\\\\");
+ break;
+ default:
+ out.append(ch);
+ break;
+ }
+ }
+ }
+ }
+
+ private static String escapeString(String str, boolean inArray) {
+ StringBuilder out = new StringBuilder();
+ escapeString(out, str, inArray);
+ return out.toString();
+ }
+
+ private static void unescapeString(StringBuilder out, String str) {
+ int sz = str.length();
+ StringBuilder unicode = new StringBuilder(4);
+ boolean hadSlash = false;
+ boolean inUnicode = false;
+ for (int i = 0; i < sz; i++) {
+ char ch = str.charAt(i);
+ if (inUnicode) {
+ // if in unicode, then we're reading unicode
+ // values in somehow
+ unicode.append(ch);
+ if (unicode.length() == 4) {
+ // unicode now contains the four hex digits
+ // which represents our unicode character
+ try {
+ int value = Integer.parseInt(unicode.toString(), 16);
+ out.append((char) value);
+ unicode.setLength(0);
+ inUnicode = false;
+ hadSlash = false;
+ } catch (NumberFormatException nfe) {
+ throw new NumberFormatException("Unable to parse unicode value: " + unicode);
+ }
+ }
+ continue;
+ }
+ if (hadSlash) {
+ // handle an escaped value
+ hadSlash = false;
+ switch (ch) {
+ case 'r':
+ out.append('\r');
+ break;
+ case 'f':
+ out.append('\f');
+ break;
+ case 't':
+ out.append('\t');
+ break;
+ case 'n':
+ out.append('\n');
+ break;
+ case 'b':
+ out.append('\b');
+ break;
+ case 'u':
+ {
+ // uh-oh, we're in unicode country....
+ inUnicode = true;
+ break;
+ }
+ default:
+ out.append(ch);
+ break;
+ }
+ continue;
+ } else if (ch == '\\') {
+ hadSlash = true;
+ continue;
+ }
+ out.append(ch);
+ }
+ if (hadSlash) {
+ // then we're in the weird case of a \ at the end of the
+ // string, let's output it anyway.
+ out.append('\\');
+ }
+ }
+
+ private static String unescapeString(String str) {
+ StringBuilder out = new StringBuilder();
+ unescapeString(out, str);
+ return out.toString();
+ }
private class PreferenceTableModel extends AbstractTableModel {
@@ -126,23 +329,22 @@ public class RobotPreferences extends StaticWidget implements ITableListener {
}
public int getColumnCount() {
- return 2;
+ return 3;
}
@Override
public String getColumnName(int i) {
- if (i == 0) {
- return "Key";
- } else if (i == 1) {
- return "Value";
- } else {
- return "ERROR";
+ switch (i) {
+ case 0: return "Key";
+ case 1: return "Value";
+ case 2: return "Type";
+ default: return "ERROR";
}
}
- public Map.Entry<String, String> getRow(int rowIndex) {
+ public Map.Entry<String, Object> getRow(int rowIndex) {
int row = 0;
- for (Map.Entry<String, String> entry : values.entrySet()) {
+ for (Map.Entry<String, Object> entry : values.entrySet()) {
if (row++ == rowIndex) {
return entry;
}
@@ -150,16 +352,29 @@ public class RobotPreferences extends StaticWidget implements ITableListener {
return null;
}
- public boolean put(String key, String value) {
- if (validateKey(key) && validateValue(value)) {
- Robot.getPreferences().putString(key, value);
+ public boolean put(String key, Object value) {
+ if (validateKey(key)) {
+ Robot.getPreferences().putValue(key, value);
+ Robot.getPreferences().setPersistent(key);
return true;
}
return false;
}
+ public boolean putString(String key, String value, String type) {
+ int typeIndex = getTypeIndex(type);
+ if (typeIndex < 0 || !validateKey(key))
+ return false;
+ Object valueObj = validateValue(value, typeIndex);
+ if (valueObj == null)
+ return false;
+ Robot.getPreferences().putValue(key, valueObj);
+ Robot.getPreferences().setPersistent(key);
+ return true;
+ }
+
public void delete(String key) {
- Robot.getPreferences().putString(key, DELETED_VALUE);
+ Robot.getPreferences().delete(key);
}
public boolean validateKey(String key) {
@@ -167,28 +382,159 @@ public class RobotPreferences extends StaticWidget implements ITableListener {
JOptionPane.showMessageDialog(RobotPreferences.this, "The key cannot be empty", "Bad Key", JOptionPane.ERROR_MESSAGE);
return false;
}
- if (key.contains(" ") || key.contains("=") || key.contains("\t") || key.contains("\r") || key.contains("\n")) {
- JOptionPane.showMessageDialog(RobotPreferences.this, "The key cannot containt ' ', '=', tabs or newlines", "Bad Key", JOptionPane.ERROR_MESSAGE);
- return false;
- }
return true;
}
- public boolean validateValue(String value) {
- if (value.contains("\"")) {
- JOptionPane.showMessageDialog(RobotPreferences.this, "The value cannot contain '\"'", "Bad Value", JOptionPane.ERROR_MESSAGE);
- return false;
+ public Object validateValue(String value, int typeIndex) {
+ switch (typeIndex) {
+ case 0: // Boolean
+ {
+ String lower = value.toLowerCase(Locale.ENGLISH);
+ if (lower.equals("y") || lower.equals("yes") || lower.equals("t") || lower.equals("true") || lower.equals("on") || lower.equals("1"))
+ return new Boolean(true);
+ else if (lower.equals("n") || lower.equals("no") || lower.equals("f") || lower.equals("false") || lower.equals("off") || lower.equals("0"))
+ return new Boolean(false);
+ else
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Invalid boolean value; expected one of yes, true, 1, no, false, 0", "Bad Value", JOptionPane.ERROR_MESSAGE);
+ break;
+ }
+ case 1: // Number
+ try {
+ return Double.parseDouble(value);
+ } catch (NumberFormatException e) {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Invalid number value", "Bad Value", JOptionPane.ERROR_MESSAGE);
+ }
+ break;
+ case 2: // String
+ try {
+ return unescapeString(value);
+ } catch (NumberFormatException e) {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Invalid string: " + e.getMessage(), "Bad Value", JOptionPane.ERROR_MESSAGE);
+ }
+ case 3: // Raw
+ {
+ value = value.trim();
+ if (value.equals("[]"))
+ return new byte[0];
+ if (!value.startsWith("[")) {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Invalid array: missing [", "Bad Value", JOptionPane.ERROR_MESSAGE);
+ break;
+ }
+ if (!value.endsWith("]")) {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Invalid array: missing ]", "Bad Value", JOptionPane.ERROR_MESSAGE);
+ break;
+ }
+
+ String[] arr = value.substring(1, value.length() - 1).split(",");
+ byte[] barr = new byte[arr.length];
+ for (int i=0; i < arr.length; i++) {
+ try {
+ barr[i] = Byte.parseByte(arr[i].trim());
+ } catch (NumberFormatException e) {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Invalid number value at index " + i + ": '" + arr[i].trim() + "'", "Bad Value", JOptionPane.ERROR_MESSAGE);
+ return null;
+ }
+ }
+ return barr;
+ }
+ case 4: // Boolean[]
+ {
+ value = value.trim();
+ if (value.equals("[]"))
+ return new boolean[0];
+ if (!value.startsWith("[")) {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Invalid array: missing [", "Bad Value", JOptionPane.ERROR_MESSAGE);
+ break;
+ }
+ if (!value.endsWith("]")) {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Invalid array: missing ]", "Bad Value", JOptionPane.ERROR_MESSAGE);
+ break;
+ }
+
+ String[] arr = value.substring(1, value.length() - 1).split(",");
+ boolean[] barr = new boolean[arr.length];
+ for (int i=0; i < arr.length; i++) {
+ String lower = arr[i].trim().toLowerCase(Locale.ENGLISH);
+ if (lower.equals("y") || lower.equals("yes") || lower.equals("t") || lower.equals("true") || lower.equals("on") || lower.equals("1"))
+ barr[i] = true;
+ else if (lower.equals("n") || lower.equals("no") || lower.equals("f") || lower.equals("false") || lower.equals("off") || lower.equals("0"))
+ barr[i] = false;
+ else {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Invalid boolean value at index " + i + ": '" + arr[i].trim() + "'; expected one of yes, true, 1, no, false, 0", "Bad Value", JOptionPane.ERROR_MESSAGE);
+ return null;
+ }
+ }
+ return barr;
+ }
+ case 5: // Number[]
+ {
+ value = value.trim();
+ if (value.equals("[]"))
+ return new double[0];
+ if (!value.startsWith("[")) {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Invalid array: missing [", "Bad Value", JOptionPane.ERROR_MESSAGE);
+ break;
+ }
+ if (!value.endsWith("]")) {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Invalid array: missing ]", "Bad Value", JOptionPane.ERROR_MESSAGE);
+ break;
+ }
+
+ String[] arr = value.substring(1, value.length() - 1).split(",");
+ double[] darr = new double[arr.length];
+ for (int i=0; i < arr.length; i++) {
+ try {
+ darr[i] = Double.parseDouble(arr[i].trim());
+ } catch (NumberFormatException e) {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Invalid number value at index " + i + ": '" + arr[i].trim() + "'", "Bad Value", JOptionPane.ERROR_MESSAGE);
+ return null;
+ }
+ }
+ return darr;
+ }
+ case 6: // String[]
+ {
+ value = value.trim();
+ if (value.equals("[]"))
+ return new String[0];
+ if (!value.startsWith("[")) {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Invalid array: missing [", "Bad Value", JOptionPane.ERROR_MESSAGE);
+ break;
+ }
+ if (!value.endsWith("]") || (value.endsWith("\\]") && !value.endsWith("\\\\]"))) {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Invalid array: missing ]", "Bad Value", JOptionPane.ERROR_MESSAGE);
+ break;
+ }
+ if (value.contains("]]]")) {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Invalid array: unescaped ]", "Bad Value", JOptionPane.ERROR_MESSAGE);
+ break;
+ }
+ // need to replace with unique string to make "\\" at end of value work
+ value = value.replaceAll("\\\\\\\\", "]]]");
+ // use look-behind assertion to avoid matching "\,"
+ String[] arr = value.substring(1, value.length() - 1).split("(?<!\\\\),");
+ for (int i=0; i < arr.length; i++) {
+ if (arr[i].startsWith(" "))
+ arr[i] = arr[i].substring(1);
+ try {
+ arr[i] = unescapeString(arr[i].replaceAll("]]]", "\\\\\\\\"));
+ } catch (NumberFormatException e) {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Invalid string at index " + i + ": " + e.getMessage(), "Bad Value", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ return arr;
+ }
}
- return true;
+ return null;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (columnIndex == 0) {//Key
- Map.Entry<String, String> entry = getRow(rowIndex);
+ Map.Entry<String, Object> entry = getRow(rowIndex);
if (entry != null) {
String oldName = entry.getKey();
- String value = entry.getValue();
+ Object value = entry.getValue();
if (!oldName.equals(aValue.toString())) {
if (!values.containsKey(aValue.toString())) {
if(put(aValue.toString(), value))
@@ -198,31 +544,84 @@ public class RobotPreferences extends StaticWidget implements ITableListener {
}
}
}
- } else {//Value
- Map.Entry<String, String> entry = getRow(rowIndex);
+ } else if (columnIndex == 1) {//Value
+ Map.Entry<String, Object> entry = getRow(rowIndex);
if (entry != null) {
- put(entry.getKey(), aValue.toString());
+ putString(entry.getKey(), aValue.toString(), getTypeName(entry.getValue()));
}
}
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
- return rowIndex >= 0;
+ return rowIndex >= 0 && columnIndex < 2;
}
public Object getValueAt(int rowIndex, int columnIndex) {
- Map.Entry<String, String> entry = getRow(rowIndex);
+ Map.Entry<String, Object> entry = getRow(rowIndex);
if (entry != null) {
- return columnIndex == 0 ? entry.getKey() : entry.getValue();
+ switch (columnIndex) {
+ case 0:
+ return entry.getKey();
+ case 1:
+ {
+ Object value = entry.getValue();
+ if (value instanceof Boolean) return ((Boolean)value).toString();
+ if (value instanceof Double) return ((Double)value).toString();
+ if (value instanceof String) return escapeString((String)value, false);
+ if (value instanceof byte[]) return Arrays.toString((byte[])value);
+ if (value instanceof boolean[]) return Arrays.toString((boolean[])value);
+ if (value instanceof double[]) return Arrays.toString((double[])value);
+ if (value instanceof String[]) {
+ String[] a = (String[])value;
+ int imax = a.length - 1;
+ if (imax == -1)
+ return "[]";
+
+ StringBuilder b = new StringBuilder();
+ b.append('[');
+ for (int i = 0; ; i++) {
+ escapeString(b, a[i], true);
+ if (i == imax)
+ return b.append(']').toString();
+ b.append(", ");
+ }
+ }
+ break;
+ }
+ case 2:
+ return getTypeName(entry.getValue());
+ default: break;
+ }
}
return "ERROR";
}
+
+ public void save(String filename) {
+ try {
+ NetworkTable.savePersistent(filename);
+ } catch (PersistentException e) {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Error saving file:" + e.getMessage(), "Save Preferences", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ public void load(String filename) {
+ String[] warnings;
+ try {
+ warnings = NetworkTable.loadPersistent(filename);
+ if (warnings.length > 0) {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Warning loading file:" + Arrays.toString(warnings), "Load Preferences", JOptionPane.WARNING_MESSAGE);
+ }
+ } catch (PersistentException e) {
+ JOptionPane.showMessageDialog(RobotPreferences.this, "Error loading file:" + e.getMessage(), "Load Preferences", JOptionPane.ERROR_MESSAGE);
+ }
+ }
}
private class NewPreferenceEntryDialog extends JDialog {
private JTextField keyField;
+ private JComboBox<String> typeComboBox;
private JTextField valueField;
private JButton addButton;
private JButton cancelButton;
@@ -243,6 +642,12 @@ public class RobotPreferences extends StaticWidget implements ITableListener {
c.gridx = 0;
c.gridy = 1;
+ add(new JLabel("Type: "), c);
+ c.gridx = 1;
+ add(typeComboBox = new JComboBox<String>(typeNames), c);
+
+ c.gridx = 0;
+ c.gridy = 2;
add(new JLabel("Value: "), c);
c.gridx = 1;
add(valueField = new JTextField(10), c);
@@ -254,7 +659,7 @@ public class RobotPreferences extends StaticWidget implements ITableListener {
public void actionPerformed(ActionEvent e) {
if (!values.containsKey(getKey())) {
- if (model.validateKey(getKey()) && model.validateValue(getValue())) {
+ if (model.validateKey(getKey()) && model.validateValue(getValue(), getTypeIndex(getValueType())) != null) {
canceled = false;
dispose();
}
@@ -274,7 +679,7 @@ public class RobotPreferences extends StaticWidget implements ITableListener {
});
c.gridx = 0;
- c.gridy = 2;
+ c.gridy = 3;
c.gridwidth = 2;
add(buttonPanel, c);
@@ -294,6 +699,10 @@ public class RobotPreferences extends StaticWidget implements ITableListener {
return keyField.getText();
}
+ public String getValueType() {
+ return (String)typeComboBox.getSelectedItem();
+ }
+
public String getValue() {
return valueField.getText();
}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Scheduler.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Scheduler.java
index d61e052..cde41c2 100644
--- a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Scheduler.java
+++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Scheduler.java
@@ -112,7 +112,7 @@ public class Scheduler extends Widget {
table.removeTableListener(listener);
}
table = (ITable) value;
- table.addTableListener(listener, true);
+ table.addTableListenerEx(listener, ITable.NOTIFY_IMMEDIATE | ITable.NOTIFY_LOCAL | ITable.NOTIFY_NEW | ITable.NOTIFY_UPDATE);
revalidate();
repaint();
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractTableWidget.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractTableWidget.java
index 99f232a..53fdccb 100644
--- a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractTableWidget.java
+++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractTableWidget.java
@@ -41,9 +41,9 @@ public abstract class AbstractTableWidget extends Widget implements ITableListen
this.table = table;
if(table!=null){
- table.addTableListener(this, true);
+ table.addTableListenerEx(this, ITable.NOTIFY_IMMEDIATE | ITable.NOTIFY_LOCAL | ITable.NOTIFY_NEW | ITable.NOTIFY_UPDATE);
if(listenSubtables)
- table.addSubTableListener(this);
+ table.addSubTableListener(this, true);
}
}
}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LWSubsystem.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LWSubsystem.java
index a09a60d..a42226c 100644
--- a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LWSubsystem.java
+++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LWSubsystem.java
@@ -119,7 +119,7 @@ public class LWSubsystem extends AbstractTableWidget {
}
if (!alreadyHasWidget) {
- table.addTableListener("~TYPE~", new ITableListener() {
+ table.addTableListenerEx("~TYPE~", new ITableListener() {
public void valueChanged(final ITable typeSource, final String typeKey, final Object typeValue, final boolean typeIsNew) {
table.removeTableListener(this);
SwingUtilities.invokeLater(new Runnable() {
@@ -128,7 +128,7 @@ public class LWSubsystem extends AbstractTableWidget {
}
});
}
- }, true);
+ }, ITable.NOTIFY_IMMEDIATE | ITable.NOTIFY_LOCAL | ITable.NOTIFY_NEW | ITable.NOTIFY_UPDATE);
}
}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/robot/Robot.java b/smartdashboard/src/edu/wpi/first/smartdashboard/robot/Robot.java
index 3d2a6c8..cb7a67b 100644
--- a/smartdashboard/src/edu/wpi/first/smartdashboard/robot/Robot.java
+++ b/smartdashboard/src/edu/wpi/first/smartdashboard/robot/Robot.java
@@ -3,8 +3,6 @@ package edu.wpi.first.smartdashboard.robot;
import java.io.*;
import edu.wpi.first.wpilibj.networktables.*;
-import edu.wpi.first.wpilibj.networktables2.client.*;
-import edu.wpi.first.wpilibj.networktables2.stream.*;
import edu.wpi.first.wpilibj.tables.*;
/**
@@ -17,23 +15,14 @@ public class Robot {
public static final String TABLE_NAME = "SmartDashboard";
public static final String LIVE_WINDOW_NAME = "LiveWindow";
public static final String PREFERENCES_NAME = "Preferences";
+ public static final String identity = "SmartDashboard";
- private static volatile String _host = null;
+ private static volatile String _host = "";
private static volatile int _port = NetworkTable.DEFAULT_PORT;
private static int _team;
private static boolean _usemDNS = true;
- private static final IOStreamFactory configurableFactory = new IOStreamFactory() {
- @Override
- public IOStream createStream() throws IOException {
- if(_host==null)
- return null;
- return new SocketStream(_host, _port);
- }
- };
- public static final NetworkTableClient client = new NetworkTableClient(configurableFactory);
- private static final NetworkTableProvider provider = new NetworkTableProvider(client);
static{
- NetworkTable.setTableProvider(provider);
+ NetworkTable.setClientMode();
}
public static void setTeam(int team) {
@@ -59,10 +48,16 @@ public class Robot {
} else {
host = "10." + (_team / 100) + "." + (_team % 100) + ".2";
}
-
+ if (_host.equals(host)) return;
_host = host;
System.out.println("Host: "+host);
- client.close();
+ try {
+ NetworkTable.shutdown();
+ } catch (IllegalStateException ex) {
+ }
+ NetworkTable.setIPAddress(host);
+ NetworkTable.setNetworkIdentity(identity);
+ NetworkTable.initialize();
}
public static String getHost() {
@@ -70,30 +65,38 @@ public class Robot {
}
public static void setPort(int port){
+ if (_port == port) return;
_port = port;
- client.close();
+ try {
+ NetworkTable.shutdown();
+ } catch (IllegalStateException ex) {
+ }
+ NetworkTable.setPort(port);
+ NetworkTable.initialize();
}
public static ITable getTable(String tableName) {
- return provider.getRootTable().getSubTable(tableName);
+ return NetworkTable.getTable(tableName);
}
public static ITable getTable() {
- return provider.getRootTable().getSubTable(TABLE_NAME);
+ return NetworkTable.getTable(TABLE_NAME);
}
public static ITable getPreferences() {
- return provider.getRootTable().getSubTable(PREFERENCES_NAME);
+ return NetworkTable.getTable(PREFERENCES_NAME);
}
public static ITable getLiveWindow() {
- return provider.getRootTable().getSubTable(LIVE_WINDOW_NAME);
+ return NetworkTable.getTable(LIVE_WINDOW_NAME);
}
public static void addConnectionListener(IRemoteConnectionListener listener, boolean immediateNotify) {
- client.addConnectionListener(listener, immediateNotify);
+ System.out.println("Adding connection listener");
+ NetworkTable.getTable(TABLE_NAME).addConnectionListener(listener, immediateNotify);
}
public static void removeConnectionListener(IRemoteConnectionListener listener) {
- client.removeConnectionListener(listener);
+ System.out.println("Removing connection listener");
+ NetworkTable.getTable(TABLE_NAME).removeConnectionListener(listener);
}
}