mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
Use new NetworkTables across WPILib (C++ and Java).
Also make sure table listeners stop listening in their destructors. This might be better handled by moving the table itself into ITableListener and providing cleanup functionality there. A submodule is used to pull in ntcore. Change-Id: I3031c1a768595cf0f8754c47e15cd423e2dbcce5
This commit is contained in:
committed by
Brad Miller (WPI)
parent
f65e697107
commit
f89c5e150f
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "networktables/ntcore"]
|
||||
path = networktables/ntcore
|
||||
url = ../ntcore
|
||||
@@ -57,3 +57,4 @@ include_directories("build")
|
||||
add_subdirectory(simulation/gz_msgs)
|
||||
add_subdirectory(wpilibc/wpilibC++Sim)
|
||||
add_subdirectory(simulation/frc_gazebo_plugins)
|
||||
add_subdirectory(networktables/ntcore)
|
||||
|
||||
@@ -34,6 +34,6 @@ sourceSets {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(":networktables:java")
|
||||
compile project(":networktables:ntcore")
|
||||
compile 'uk.gov.nationalarchives.thirdparty.netbeans:org-netbeans-swing-outline:7.2'
|
||||
}
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
package edu.wpi.first.tableviewer;
|
||||
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import edu.wpi.first.wpilibj.networktables.NetworkTableProvider;
|
||||
import edu.wpi.first.wpilibj.networktables2.NetworkTableNode;
|
||||
import edu.wpi.first.wpilibj.networktables.NetworkTable;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
import org.netbeans.swing.outline.Outline;
|
||||
@@ -30,13 +29,13 @@ public abstract class AbstractTreeNode extends DefaultMutableTreeNode {
|
||||
AbstractTreeNode.outline = outline;
|
||||
}
|
||||
|
||||
public AbstractTreeNode(NetworkTableNode node, String key, TableEntryData data) {
|
||||
public AbstractTreeNode(String key, TableEntryData data) {
|
||||
super(data);
|
||||
this.data = data;
|
||||
if (treeModel != null) {
|
||||
treeModel.reload(this);
|
||||
}
|
||||
table = new NetworkTableProvider(node).getTable(key);
|
||||
table = NetworkTable.getTable(key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,7 +8,6 @@ import edu.wpi.first.tableviewer.dialog.AddArrayDialog;
|
||||
import edu.wpi.first.tableviewer.dialog.AddBooleanDialog;
|
||||
import edu.wpi.first.tableviewer.dialog.AddNumberDialog;
|
||||
import edu.wpi.first.tableviewer.dialog.AddStringDialog;
|
||||
import edu.wpi.first.wpilibj.networktables2.NetworkTableNode;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import javax.swing.JMenuItem;
|
||||
@@ -25,8 +24,8 @@ public class BranchNode extends AbstractTreeNode {
|
||||
|
||||
private final String name;
|
||||
|
||||
public BranchNode(NetworkTableNode node, String key, String name) {
|
||||
super(node, key, new TableEntryData(name, null));
|
||||
public BranchNode(String key, String name) {
|
||||
super(key, new TableEntryData(name, null));
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
*/
|
||||
package edu.wpi.first.tableviewer;
|
||||
|
||||
import edu.wpi.first.wpilibj.networktables.NetworkTableProvider;
|
||||
import edu.wpi.first.wpilibj.networktables2.NetworkTableNode;
|
||||
import edu.wpi.first.wpilibj.networktables.NetworkTable;
|
||||
import javax.swing.event.TableModelEvent;
|
||||
|
||||
/**
|
||||
@@ -16,9 +15,9 @@ import javax.swing.event.TableModelEvent;
|
||||
*/
|
||||
public class LeafNode extends AbstractTreeNode {
|
||||
|
||||
public LeafNode(NetworkTableNode node, String key, TableEntryData data) {
|
||||
super(node, key, data);
|
||||
table = new NetworkTableProvider(node).getTable(key.substring(0, key.lastIndexOf('/')));
|
||||
public LeafNode(String key, TableEntryData data) {
|
||||
super(key, data);
|
||||
table = NetworkTable.getTable(key.substring(0, key.lastIndexOf('/')));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@ package edu.wpi.first.tableviewer;
|
||||
|
||||
import edu.wpi.first.tableviewer.TableEntryData.EntryType;
|
||||
import edu.wpi.first.tableviewer.dialog.AbstractAddDialog;
|
||||
import edu.wpi.first.wpilibj.networktables2.NetworkTableNode;
|
||||
import edu.wpi.first.wpilibj.networktables.NetworkTable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import edu.wpi.first.wpilibj.tables.ITableListener;
|
||||
import java.awt.BorderLayout;
|
||||
@@ -29,18 +29,16 @@ public class OutlineFrame extends JFrame implements ITableListener {
|
||||
|
||||
private final Outline outline;
|
||||
private final BranchNode rootBranch;
|
||||
private final NetworkTableNode node;
|
||||
private final boolean showMetadata;
|
||||
private final Preferences prefs = Preferences.userNodeForPackage(getClass());
|
||||
|
||||
public OutlineFrame(String title, NetworkTableNode node, boolean showMetadata) {
|
||||
this.node = node;
|
||||
public OutlineFrame(String title, boolean showMetadata) {
|
||||
this.showMetadata = showMetadata;
|
||||
|
||||
setTitle(title);
|
||||
setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||
|
||||
rootBranch = new BranchNode(node, "", "Root");
|
||||
rootBranch = new BranchNode("", "Root");
|
||||
DefaultTreeModel outlineTreeModel = new DefaultTreeModel(rootBranch);
|
||||
|
||||
OutlineModel outlineModel = DefaultOutlineModel.createOutlineModel(
|
||||
@@ -99,7 +97,7 @@ public class OutlineFrame extends JFrame implements ITableListener {
|
||||
scrollPane.setViewportView(outline);
|
||||
add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
node.addTableListener(this, true);
|
||||
NetworkTable.getTable("").addTableListener(this, true);
|
||||
|
||||
}
|
||||
|
||||
@@ -125,7 +123,7 @@ public class OutlineFrame extends JFrame implements ITableListener {
|
||||
key += "/" + name;
|
||||
if (subTableNames.getLast() == name) { // leaf
|
||||
if (currentNode == null) {
|
||||
currentNode = new LeafNode(node, key, new TableEntryData(name, value));
|
||||
currentNode = new LeafNode(key, new TableEntryData(name, value));
|
||||
if (currentNode.data.isMetadata() && !showMetadata) {
|
||||
// don't show metadata directly
|
||||
// instead, show the value in the branch's "Type" field
|
||||
@@ -140,7 +138,7 @@ public class OutlineFrame extends JFrame implements ITableListener {
|
||||
}
|
||||
|
||||
} else if (currentNode == null) {
|
||||
currentNode = new BranchNode(node, key, name);
|
||||
currentNode = new BranchNode(key, name);
|
||||
parentNode.add(currentNode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,8 @@
|
||||
*/
|
||||
package edu.wpi.first.tableviewer.dialog;
|
||||
|
||||
import edu.wpi.first.wpilibj.networktables.NetworkTable;
|
||||
import edu.wpi.first.wpilibj.networktables2.type.ArrayData;
|
||||
import edu.wpi.first.wpilibj.networktables2.type.BooleanArray;
|
||||
import edu.wpi.first.wpilibj.networktables2.type.NumberArray;
|
||||
import edu.wpi.first.wpilibj.networktables2.type.StringArray;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import java.util.ArrayList;
|
||||
import javax.swing.DefaultCellEditor;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JOptionPane;
|
||||
@@ -219,60 +215,78 @@ public class AddArrayDialog extends AbstractAddDialog {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code ArrayData} object containing the values in the
|
||||
* table, which will then be put into the {@link NetworkTable} associated
|
||||
* Creates a new array object containing the values in the
|
||||
* table, which will then be put into the Network Table associated
|
||||
* with this dialog.
|
||||
*
|
||||
* @param arrayType The type of array: Boolean, Number, or String.
|
||||
* @return An {@link ArrayData} object containing the data in the table, or
|
||||
* @return An array object containing the data in the table, or
|
||||
* null if an error is present in the table (such as an invalid double
|
||||
* value).
|
||||
*/
|
||||
private ArrayData createArrayData(String arrayType) {
|
||||
ArrayData ad;
|
||||
private Object createArrayData(String arrayType) {
|
||||
int n = 0;
|
||||
for (int i = 0; i < valueTable.getRowCount(); i++) {
|
||||
Object data = valueTable.getValueAt(i, 0);
|
||||
if (data != null) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
switch (arrayType) {
|
||||
case "Boolean":
|
||||
ad = new BooleanArray();
|
||||
for (int i = 0; i < valueTable.getRowCount(); i++) {
|
||||
Object data = valueTable.getValueAt(i, 0);
|
||||
if (data != null) {
|
||||
((BooleanArray) ad).add(data.toString().equalsIgnoreCase("true"));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "Number":
|
||||
ad = new NumberArray();
|
||||
for (int i = 0; i < valueTable.getRowCount(); i++) {
|
||||
Object data = valueTable.getValueAt(i, 0);
|
||||
try {
|
||||
{
|
||||
boolean[] arr = new boolean[n];
|
||||
n = 0;
|
||||
for (int i = 0; i < valueTable.getRowCount(); i++) {
|
||||
Object data = valueTable.getValueAt(i, 0);
|
||||
if (data != null) {
|
||||
Double d = Double.parseDouble(data.toString());
|
||||
((NumberArray) ad).add(d);
|
||||
arr[n] = data.toString().equalsIgnoreCase("true");
|
||||
n++;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
JOptionPane.showMessageDialog(
|
||||
this,
|
||||
"Invalid double value \"" + data + "\" in row " + (i + 1),
|
||||
"Invalid number",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return null;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
case "Number":
|
||||
{
|
||||
double[] arr = new double[n];
|
||||
n = 0;
|
||||
for (int i = 0; i < valueTable.getRowCount(); i++) {
|
||||
Object data = valueTable.getValueAt(i, 0);
|
||||
try {
|
||||
if (data != null) {
|
||||
arr[n] = Double.parseDouble(data.toString());
|
||||
n++;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
JOptionPane.showMessageDialog(
|
||||
this,
|
||||
"Invalid double value \"" + data + "\" in row " + (i + 1),
|
||||
"Invalid number",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
break;
|
||||
|
||||
case "String":
|
||||
ad = new StringArray();
|
||||
for (int i = 0; i < valueTable.getRowCount(); i++) {
|
||||
Object data = valueTable.getValueAt(i, 0);
|
||||
if (data != null) {
|
||||
((StringArray) ad).add(data.toString());
|
||||
{
|
||||
String[] arr = new String[n];
|
||||
n = 0;
|
||||
for (int i = 0; i < valueTable.getRowCount(); i++) {
|
||||
Object data = valueTable.getValueAt(i, 0);
|
||||
if (data != null) {
|
||||
arr[n] = data.toString();
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException(arrayType + " is not a valid array type");
|
||||
}
|
||||
return ad;
|
||||
}
|
||||
// Variables declaration - do not modify
|
||||
private javax.swing.JButton addRowButton;
|
||||
|
||||
@@ -2,12 +2,8 @@ package edu.wpi.first.tableviewer.dialog;
|
||||
|
||||
|
||||
import edu.wpi.first.tableviewer.OutlineFrame;
|
||||
import edu.wpi.first.wpilibj.networktables2.NetworkTableNode;
|
||||
import edu.wpi.first.wpilibj.networktables2.client.NetworkTableClient;
|
||||
import edu.wpi.first.wpilibj.networktables2.server.NetworkTableServer;
|
||||
import edu.wpi.first.wpilibj.networktables2.stream.IOStreamFactory;
|
||||
import edu.wpi.first.wpilibj.networktables2.stream.IOStreamProvider;
|
||||
import edu.wpi.first.wpilibj.networktables2.stream.SocketStreams;
|
||||
import edu.wpi.first.wpilibj.networktables.NetworkTable;
|
||||
import edu.wpi.first.wpilibj.networktables.NetworkTablesJNI;
|
||||
import java.util.prefs.Preferences;
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
@@ -114,24 +110,28 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
||||
|
||||
private void startButtonPressed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_startButtonPressed
|
||||
try {
|
||||
NetworkTableNode node;
|
||||
NetworkTablesJNI.setLogger(new NetworkTablesJNI.LoggerFunction() {
|
||||
public void apply(int level, String file, int line, String msg) {
|
||||
System.err.println(msg);
|
||||
}
|
||||
}, 0);
|
||||
if (evt.getSource() == clientButton) { // start client
|
||||
String host = hostField.getText();
|
||||
if (host.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
IOStreamFactory streamFactory = SocketStreams.newStreamFactory(host, 1735);
|
||||
NetworkTableClient client = new NetworkTableClient(streamFactory);
|
||||
client.reconnect();
|
||||
node = client;
|
||||
NetworkTable.setIPAddress(host);
|
||||
NetworkTable.setClientMode();
|
||||
NetworkTable.initialize();
|
||||
prefs.put("host", host);
|
||||
} else { // start server
|
||||
IOStreamProvider streamProvider = SocketStreams.newStreamProvider(1735);
|
||||
node = new NetworkTableServer(streamProvider);
|
||||
NetworkTable.setIPAddress("");
|
||||
NetworkTable.setServerMode();
|
||||
NetworkTable.initialize();
|
||||
prefs.put("host", "");
|
||||
}
|
||||
prefs.putBoolean("metadata", metadataBox.isSelected());
|
||||
new OutlineFrame("Network Table Viewer", node, metadataBox.isSelected()).setVisible(true);
|
||||
new OutlineFrame("Network Table Viewer", metadataBox.isSelected()).setVisible(true);
|
||||
dispose();
|
||||
} catch (Exception e) {
|
||||
JOptionPane.showMessageDialog(null, e.getClass() + ": " + e.getMessage(), "Error creating table node", JOptionPane.ERROR_MESSAGE);
|
||||
|
||||
1
networktables/ntcore
Submodule
1
networktables/ntcore
Submodule
Submodule networktables/ntcore added at e42f9b0603
@@ -1,3 +1,3 @@
|
||||
/* GNU ld script */
|
||||
OUTPUT_FORMAT(elf32-littlearm)
|
||||
GROUP ( AS_NEEDED ( -lwpilib_nonshared -lHALAthena -lNetworkTables -lFRC_NetworkCommunication -li2c -lni_emb -lNiFpgaLv -lNiFpga -lnirio_emb_can -lNiRioSrv -lni_rtlog -lRoboRIO_FRC_ChipObject -lspi -lvisa -ldl -lpthread -lrt -lGCBase_gcc-4.4-arm_v2_3 -lGenApi_gcc-4.4-arm_v2_3 -lLog_gcc-4.4-arm_v2_3 -lMathParser_gcc-4.4-arm_v2_3 -llog4cpp_gcc-4.4-arm_v2_3 -lniimaqdx -lnivision -lnivissvc ) )
|
||||
GROUP ( AS_NEEDED ( -lwpilib_nonshared -lHALAthena -lntcore -lFRC_NetworkCommunication -li2c -lni_emb -lNiFpgaLv -lNiFpga -lnirio_emb_can -lNiRioSrv -lni_rtlog -lRoboRIO_FRC_ChipObject -lspi -lvisa -ldl -lpthread -lrt -lGCBase_gcc-4.4-arm_v2_3 -lGenApi_gcc-4.4-arm_v2_3 -lLog_gcc-4.4-arm_v2_3 -lMathParser_gcc-4.4-arm_v2_3 -llog4cpp_gcc-4.4-arm_v2_3 -lniimaqdx -lnivision -lnivissvc ) )
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
include 'networktables:java',
|
||||
'networktables:cpp',
|
||||
include 'networktables:ntcore',
|
||||
'networktables:OutlineViewer',
|
||||
'hal',
|
||||
'wpilibj',
|
||||
@@ -7,4 +6,4 @@ include 'networktables:java',
|
||||
'simulation:JavaGazebo',
|
||||
'simulation:SimDS',
|
||||
'jenkins',
|
||||
'driver-station'
|
||||
'driver-station'
|
||||
|
||||
@@ -18,7 +18,7 @@ def sim = 'wpilibC++Sim'
|
||||
// Ensure that both hal and networktables are evaluated, so that they have the binaries property. We need this to
|
||||
// properly copy their archives into the final zip
|
||||
evaluationDependsOn(':hal')
|
||||
evaluationDependsOn(':networktables:cpp')
|
||||
evaluationDependsOn(':networktables:ntcore')
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
@@ -57,7 +57,7 @@ model {
|
||||
includes = ['**/*.h']
|
||||
}
|
||||
lib project: ':hal', library: 'HALAthena', linkage: 'static'
|
||||
lib project: ':networktables:cpp', library: 'NetworkTables', linkage: 'static'
|
||||
lib project: ':networktables:ntcore', library: 'ntcore', linkage: 'static'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,12 +86,12 @@ model {
|
||||
include 'gtest-all.cc', 'gtest_main.cc'
|
||||
}
|
||||
exportedHeaders {
|
||||
srcDirs = ["${dir}/include", "${dir}/src/gtest", "${dir}/src/gtest/include", "${devices}/include", "${shared}/include", '../hal/include/HAL', '../networktables/cpp/include']
|
||||
srcDirs = ["${dir}/include", "${dir}/src/gtest", "${dir}/src/gtest/include", "${devices}/include", "${shared}/include", '../hal/include/HAL', '../networktables/ntcore/include']
|
||||
include '**/*.h'
|
||||
}
|
||||
|
||||
lib library: 'wpilib_nonshared', linkage: 'static'
|
||||
lib project: ':networktables:cpp', library: 'NetworkTables', linkage: 'static'
|
||||
lib project: ':networktables:ntcore', library: 'ntcore', linkage: 'static'
|
||||
lib project: ':hal', library: 'HALAthena', linkage: 'static'
|
||||
}
|
||||
}
|
||||
@@ -104,7 +104,7 @@ doxygen {
|
||||
source file("${shared}/include")
|
||||
source file("${devices}/src")
|
||||
source file("${devices}/include")
|
||||
def netTablesLoc = '../networktables/cpp'
|
||||
def netTablesLoc = '../networktables/ntcore'
|
||||
source file("${netTablesLoc}/include")
|
||||
source file("${netTablesLoc}/lib/Athena")
|
||||
source file("${netTablesLoc}/lib/share")
|
||||
@@ -139,7 +139,7 @@ task wpilibcZip(type: Zip) {
|
||||
}
|
||||
|
||||
// Include the static library file and header files from the networktables project
|
||||
def netTables = project(':networktables:cpp')
|
||||
def netTables = project(':networktables:ntcore')
|
||||
netTables.binaries.withType(StaticLibraryBinarySpec) { spec ->
|
||||
spec.headerDirs.each {
|
||||
from(it) {
|
||||
@@ -190,7 +190,7 @@ task wpilibcSimZip(type: Zip) {
|
||||
from "${sim}/include"
|
||||
from "${shared}/include"
|
||||
from "../build/simulation/gz_msgs/generated"
|
||||
from '../networktables/cpp/include'
|
||||
from '../networktables/ntcore/include'
|
||||
from '../hal/include'
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ tasks.whenTaskAdded { task ->
|
||||
}
|
||||
|
||||
// Add the networktables static library as a dependency
|
||||
project(':networktables:cpp').tasks.whenTaskAdded { task ->
|
||||
project(':networktables:ntcore').tasks.whenTaskAdded { task ->
|
||||
if (task.name == 'networkTablesStaticLibrary') {
|
||||
wpilibcZip.dependsOn task
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "ErrorBase.h"
|
||||
#include "SmartDashboard/NamedSendable.h"
|
||||
#include "tables/ITableListener.h"
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
@@ -179,8 +180,8 @@ class Command : public ErrorBase, public NamedSendable, public ITableListener {
|
||||
virtual void InitTable(std::shared_ptr<ITable> table);
|
||||
virtual std::shared_ptr<ITable> GetTable() const;
|
||||
virtual std::string GetSmartDashboardType() const;
|
||||
virtual void ValueChanged(std::shared_ptr<ITable> source, const std::string &key,
|
||||
EntryValue value, bool isNew);
|
||||
virtual void ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<ITable> m_table = nullptr;
|
||||
|
||||
@@ -12,13 +12,12 @@
|
||||
#include "ErrorBase.h"
|
||||
#include "SmartDashboard/NamedSendable.h"
|
||||
#include "networktables/NetworkTable.h"
|
||||
#include "networktables2/type/NumberArray.h"
|
||||
#include "networktables2/type/StringArray.h"
|
||||
#include "SmartDashboard/SmartDashboard.h"
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "HAL/cpp/priority_mutex.h"
|
||||
|
||||
@@ -62,9 +61,9 @@ class Scheduler : public ErrorBase, public NamedSendable {
|
||||
CommandSet m_commands;
|
||||
bool m_adding = false;
|
||||
bool m_enabled = true;
|
||||
StringArray *commands = nullptr;
|
||||
NumberArray *ids = nullptr;
|
||||
NumberArray *toCancel = nullptr;
|
||||
std::vector<std::string> commands;
|
||||
std::vector<double> ids;
|
||||
std::vector<double> toCancel;
|
||||
std::shared_ptr<ITable> m_table = nullptr;
|
||||
bool m_runningCommandsChanged = false;
|
||||
};
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
class LiveWindowStatusListener : public ITableListener {
|
||||
public:
|
||||
virtual void ValueChanged(std::shared_ptr<ITable> source, const std::string& key,
|
||||
EntryValue value, bool isNew);
|
||||
virtual void ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -36,7 +36,7 @@ class PIDController : public LiveWindowSendable,
|
||||
float period = 0.05);
|
||||
PIDController(float p, float i, float d, float f, PIDSource *source,
|
||||
PIDOutput *output, float period = 0.05);
|
||||
virtual ~PIDController() = default;
|
||||
virtual ~PIDController();
|
||||
|
||||
PIDController(const PIDController&) = delete;
|
||||
PIDController& operator=(const PIDController) = delete;
|
||||
@@ -124,8 +124,9 @@ class PIDController : public LiveWindowSendable,
|
||||
|
||||
virtual std::shared_ptr<ITable> GetTable() const override;
|
||||
virtual std::string GetSmartDashboardType() const override;
|
||||
virtual void ValueChanged(std::shared_ptr<ITable> source, const std::string &key,
|
||||
EntryValue value, bool isNew) override;
|
||||
virtual void ValueChanged(ITable *source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value,
|
||||
bool isNew) override;
|
||||
virtual void UpdateTable() override;
|
||||
virtual void StartLiveWindowMode() override;
|
||||
virtual void StopLiveWindowMode() override;
|
||||
|
||||
@@ -19,25 +19,23 @@ class SmartDashboard : public SensorBase {
|
||||
public:
|
||||
static void init();
|
||||
|
||||
static void PutData(std::string key, Sendable *data);
|
||||
static void PutData(llvm::StringRef key, Sendable *data);
|
||||
static void PutData(NamedSendable *value);
|
||||
static Sendable *GetData(std::string keyName);
|
||||
static Sendable *GetData(llvm::StringRef keyName);
|
||||
|
||||
static void PutBoolean(std::string keyName, bool value);
|
||||
static bool GetBoolean(std::string keyName);
|
||||
static bool GetBoolean(std::string keyName, bool defaultValue);
|
||||
static void PutBoolean(llvm::StringRef keyName, bool value);
|
||||
static bool GetBoolean(llvm::StringRef keyName, bool defaultValue);
|
||||
|
||||
static void PutNumber(std::string keyName, double value);
|
||||
static double GetNumber(std::string keyName);
|
||||
static double GetNumber(std::string keyName, double defaultValue);
|
||||
static void PutNumber(llvm::StringRef keyName, double value);
|
||||
static double GetNumber(llvm::StringRef keyName, double defaultValue);
|
||||
|
||||
static void PutString(std::string keyName, std::string value);
|
||||
static int GetString(std::string keyName, char *value, unsigned int valueLen);
|
||||
static std::string GetString(std::string keyName);
|
||||
static std::string GetString(std::string keyName, std::string defaultValue);
|
||||
static void PutString(llvm::StringRef keyName, llvm::StringRef value);
|
||||
static std::string GetString(llvm::StringRef keyName,
|
||||
llvm::StringRef defaultValue);
|
||||
|
||||
static void PutValue(std::string keyName, ComplexData &value);
|
||||
static void RetrieveValue(std::string keyName, ComplexData &value);
|
||||
static void PutValue(llvm::StringRef keyName,
|
||||
std::shared_ptr<nt::Value> value);
|
||||
static std::shared_ptr<nt::Value> GetValue(llvm::StringRef keyName);
|
||||
|
||||
private:
|
||||
virtual ~SmartDashboard() = default;
|
||||
|
||||
@@ -21,5 +21,5 @@ bool NetworkButton::Get() {
|
||||
return m_netTable->GetBoolean(m_field.c_str());
|
||||
else
|
||||
return false;*/
|
||||
return m_netTable->GetBoolean(m_field);
|
||||
return m_netTable->GetBoolean(m_field, false);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ bool Trigger::Grab() {
|
||||
return true;
|
||||
else if (m_table != nullptr) {
|
||||
// if (m_table->isConnected())//TODO is connected on button?
|
||||
return m_table->GetBoolean("pressed");
|
||||
return m_table->GetBoolean("pressed", false);
|
||||
/*else
|
||||
return false;*/
|
||||
} else
|
||||
|
||||
@@ -60,10 +60,8 @@ Command::Command(const std::string &name, double timeout) {
|
||||
}
|
||||
}
|
||||
|
||||
Command::~Command() { // TODO deal with cleaning up all listeners
|
||||
/*if (m_table != nullptr){
|
||||
m_table->RemoveChangeListener(kRunning, this);
|
||||
}*/
|
||||
Command::~Command() {
|
||||
if (m_table != nullptr) m_table->RemoveTableListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -383,9 +381,10 @@ void Command::InitTable(std::shared_ptr<ITable> table) {
|
||||
|
||||
std::shared_ptr<ITable> Command::GetTable() const { return m_table; }
|
||||
|
||||
void Command::ValueChanged(std::shared_ptr<ITable> source, const std::string &key,
|
||||
EntryValue value, bool isNew) {
|
||||
if (value.b) {
|
||||
void Command::ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) {
|
||||
if (!value->IsBoolean()) return;
|
||||
if (value->GetBoolean()) {
|
||||
if (!IsRunning()) Start();
|
||||
} else {
|
||||
if (IsRunning()) Cancel();
|
||||
|
||||
@@ -217,37 +217,41 @@ void Scheduler::UpdateTable() {
|
||||
CommandSet::iterator commandIter;
|
||||
if (m_table != nullptr) {
|
||||
// Get the list of possible commands to cancel
|
||||
m_table->RetrieveValue("Cancel", *toCancel);
|
||||
auto new_toCancel = m_table->GetValue("Cancel");
|
||||
if (new_toCancel)
|
||||
toCancel = new_toCancel->GetDoubleArray();
|
||||
else
|
||||
toCancel.resize(0);
|
||||
// m_table->RetrieveValue("Ids", *ids);
|
||||
|
||||
// cancel commands that have had the cancel buttons pressed
|
||||
// on the SmartDashboad
|
||||
if (toCancel->size() > 0) {
|
||||
if (!toCancel.empty()) {
|
||||
for (commandIter = m_commands.begin(); commandIter != m_commands.end();
|
||||
++commandIter) {
|
||||
for (unsigned i = 0; i < toCancel->size(); i++) {
|
||||
for (unsigned i = 0; i < toCancel.size(); i++) {
|
||||
Command *c = *commandIter;
|
||||
if (c->GetID() == toCancel->get(i)) {
|
||||
if (c->GetID() == toCancel[i]) {
|
||||
c->Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
toCancel->setSize(0);
|
||||
m_table->PutValue("Cancel", *toCancel);
|
||||
toCancel.resize(0);
|
||||
m_table->PutValue("Cancel", nt::Value::MakeDoubleArray(toCancel));
|
||||
}
|
||||
|
||||
// Set the running commands
|
||||
if (m_runningCommandsChanged) {
|
||||
commands->setSize(0);
|
||||
ids->setSize(0);
|
||||
commands.resize(0);
|
||||
ids.resize(0);
|
||||
for (commandIter = m_commands.begin(); commandIter != m_commands.end();
|
||||
++commandIter) {
|
||||
Command *c = *commandIter;
|
||||
commands->add(c->GetName());
|
||||
ids->add(c->GetID());
|
||||
commands.push_back(c->GetName());
|
||||
ids.push_back(c->GetID());
|
||||
}
|
||||
m_table->PutValue("Names", *commands);
|
||||
m_table->PutValue("Ids", *ids);
|
||||
m_table->PutValue("Names", nt::Value::MakeStringArray(commands));
|
||||
m_table->PutValue("Ids", nt::Value::MakeDoubleArray(ids));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,13 +264,10 @@ std::string Scheduler::GetSmartDashboardType() const { return "Scheduler"; }
|
||||
|
||||
void Scheduler::InitTable(std::shared_ptr<ITable> subTable) {
|
||||
m_table = subTable;
|
||||
commands = new StringArray();
|
||||
ids = new NumberArray();
|
||||
toCancel = new NumberArray();
|
||||
|
||||
m_table->PutValue("Names", *commands);
|
||||
m_table->PutValue("Ids", *ids);
|
||||
m_table->PutValue("Cancel", *toCancel);
|
||||
m_table->PutValue("Names", nt::Value::MakeStringArray(commands));
|
||||
m_table->PutValue("Ids", nt::Value::MakeDoubleArray(ids));
|
||||
m_table->PutValue("Cancel", nt::Value::MakeDoubleArray(toCancel));
|
||||
}
|
||||
|
||||
std::shared_ptr<ITable> Scheduler::GetTable() const { return m_table; }
|
||||
|
||||
@@ -19,8 +19,8 @@ LiveWindow *LiveWindow::GetInstance() {
|
||||
* Allocate the necessary tables.
|
||||
*/
|
||||
LiveWindow::LiveWindow() : m_scheduler(Scheduler::GetInstance()) {
|
||||
m_liveWindowTable.reset(NetworkTable::GetTable("LiveWindow"));
|
||||
m_statusTable.reset(m_liveWindowTable->GetSubTable("~STATUS~"));
|
||||
m_liveWindowTable = NetworkTable::GetTable("LiveWindow");
|
||||
m_statusTable = m_liveWindowTable->GetSubTable("~STATUS~");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "LiveWindow/LiveWindowStatusListener.h"
|
||||
#include "Commands/Scheduler.h"
|
||||
|
||||
void LiveWindowStatusListener::ValueChanged(std::shared_ptr<ITable> source,
|
||||
const std::string& key,
|
||||
EntryValue value, bool isNew) {}
|
||||
void LiveWindowStatusListener::ValueChanged(ITable *source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value,
|
||||
bool isNew) {}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "SmartDashboard/SendableChooser.h"
|
||||
#include "networktables2/type/StringArray.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -54,14 +53,14 @@ void *SendableChooser::GetSelected() {
|
||||
}
|
||||
|
||||
void SendableChooser::InitTable(std::shared_ptr<ITable> subtable) {
|
||||
StringArray keys;
|
||||
std::vector<std::string> keys;
|
||||
m_table = subtable;
|
||||
if (m_table != nullptr) {
|
||||
std::map<std::string, void *>::iterator iter;
|
||||
for (iter = m_choices.begin(); iter != m_choices.end(); iter++) {
|
||||
keys.add(iter->first);
|
||||
keys.push_back(iter->first);
|
||||
}
|
||||
m_table->PutValue(kOptions, keys);
|
||||
m_table->PutValue(kOptions, nt::Value::MakeStringArray(std::move(keys)));
|
||||
m_table->PutString(kDefault, m_defaultChoice);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ std::shared_ptr<ITable> SmartDashboard::m_table = nullptr;
|
||||
std::map<std::shared_ptr<ITable> , Sendable *> SmartDashboard::m_tablesToData;
|
||||
|
||||
void SmartDashboard::init() {
|
||||
m_table.reset(NetworkTable::GetTable("SmartDashboard"));
|
||||
m_table = NetworkTable::GetTable("SmartDashboard");
|
||||
|
||||
HLUsageReporting::ReportSmartDashboard();
|
||||
}
|
||||
@@ -30,7 +30,7 @@ void SmartDashboard::init() {
|
||||
* @param keyName the key
|
||||
* @param value the value
|
||||
*/
|
||||
void SmartDashboard::PutData(std::string key, Sendable *data) {
|
||||
void SmartDashboard::PutData(llvm::StringRef key, Sendable *data) {
|
||||
if (data == nullptr) {
|
||||
wpi_setGlobalWPIErrorWithContext(NullParameter, "value");
|
||||
return;
|
||||
@@ -62,11 +62,11 @@ void SmartDashboard::PutData(NamedSendable *value) {
|
||||
* @param keyName the key
|
||||
* @return the value
|
||||
*/
|
||||
Sendable *SmartDashboard::GetData(std::string key) {
|
||||
Sendable *SmartDashboard::GetData(llvm::StringRef key) {
|
||||
std::shared_ptr<ITable> subtable(m_table->GetSubTable(key));
|
||||
Sendable *data = m_tablesToData[subtable];
|
||||
if (data == nullptr) {
|
||||
wpi_setGlobalWPIErrorWithContext(SmartDashboardMissingKey, key.c_str());
|
||||
wpi_setGlobalWPIErrorWithContext(SmartDashboardMissingKey, key);
|
||||
return nullptr;
|
||||
}
|
||||
return data;
|
||||
@@ -81,7 +81,8 @@ Sendable *SmartDashboard::GetData(std::string key) {
|
||||
* @param keyName the key
|
||||
* @param value the value
|
||||
*/
|
||||
void SmartDashboard::PutValue(std::string keyName, ComplexData &value) {
|
||||
void SmartDashboard::PutValue(llvm::StringRef keyName,
|
||||
std::shared_ptr<nt::Value> value) {
|
||||
m_table->PutValue(keyName, value);
|
||||
}
|
||||
|
||||
@@ -92,8 +93,8 @@ void SmartDashboard::PutValue(std::string keyName, ComplexData &value) {
|
||||
* @param keyName the key
|
||||
* @param value the object to retrieve the value into
|
||||
*/
|
||||
void SmartDashboard::RetrieveValue(std::string keyName, ComplexData &value) {
|
||||
m_table->RetrieveValue(keyName, value);
|
||||
std::shared_ptr<nt::Value> SmartDashboard::GetValue(llvm::StringRef keyName) {
|
||||
return m_table->GetValue(keyName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,27 +105,17 @@ void SmartDashboard::RetrieveValue(std::string keyName, ComplexData &value) {
|
||||
* @param keyName the key
|
||||
* @param value the value
|
||||
*/
|
||||
void SmartDashboard::PutBoolean(std::string keyName, bool value) {
|
||||
void SmartDashboard::PutBoolean(llvm::StringRef keyName, bool value) {
|
||||
m_table->PutBoolean(keyName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at the specified key. Throws an exception if the key is not
|
||||
* found in the table
|
||||
* @param keyName the key
|
||||
* @return the value
|
||||
*/
|
||||
bool SmartDashboard::GetBoolean(std::string keyName) {
|
||||
return m_table->GetBoolean(keyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at the specified key. If the key is not found, returns the
|
||||
* default value.
|
||||
* @param keyName the key
|
||||
* @return the value
|
||||
*/
|
||||
bool SmartDashboard::GetBoolean(std::string keyName, bool defaultValue) {
|
||||
bool SmartDashboard::GetBoolean(llvm::StringRef keyName, bool defaultValue) {
|
||||
return m_table->GetBoolean(keyName, defaultValue);
|
||||
}
|
||||
|
||||
@@ -136,27 +127,17 @@ bool SmartDashboard::GetBoolean(std::string keyName, bool defaultValue) {
|
||||
* @param keyName the key
|
||||
* @param value the value
|
||||
*/
|
||||
void SmartDashboard::PutNumber(std::string keyName, double value) {
|
||||
void SmartDashboard::PutNumber(llvm::StringRef keyName, double value) {
|
||||
m_table->PutNumber(keyName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at the specified key. Throws an exception if the key is not
|
||||
* found in the table.
|
||||
* @param keyName the key
|
||||
* @return the value
|
||||
*/
|
||||
double SmartDashboard::GetNumber(std::string keyName) {
|
||||
return m_table->GetNumber(keyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at the specified key. If the key is not found, returns the
|
||||
* default value.
|
||||
* @param keyName the key
|
||||
* @return the value
|
||||
*/
|
||||
double SmartDashboard::GetNumber(std::string keyName, double defaultValue) {
|
||||
double SmartDashboard::GetNumber(llvm::StringRef keyName, double defaultValue) {
|
||||
return m_table->GetNumber(keyName, defaultValue);
|
||||
}
|
||||
|
||||
@@ -168,44 +149,17 @@ double SmartDashboard::GetNumber(std::string keyName, double defaultValue) {
|
||||
* @param keyName the key
|
||||
* @param value the value
|
||||
*/
|
||||
void SmartDashboard::PutString(std::string keyName, std::string value) {
|
||||
void SmartDashboard::PutString(llvm::StringRef keyName, llvm::StringRef value) {
|
||||
m_table->PutString(keyName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at the specified key.
|
||||
* @param keyName the key
|
||||
* @param value the buffer to fill with the value
|
||||
* @param valueLen the size of the buffer pointed to by value
|
||||
* @return the length of the string
|
||||
*/
|
||||
int SmartDashboard::GetString(std::string keyName, char *outBuffer,
|
||||
unsigned int bufferLen) {
|
||||
std::string value = m_table->GetString(keyName);
|
||||
unsigned int i;
|
||||
for (i = 0; i < bufferLen - 1 && i < value.length(); ++i)
|
||||
outBuffer[i] = (char)value.at(i);
|
||||
outBuffer[i] = '\0';
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at the specified key. Throws an exception if the key is not
|
||||
* found in the table
|
||||
* @param keyName the key
|
||||
* @return the value
|
||||
*/
|
||||
std::string SmartDashboard::GetString(std::string keyName) {
|
||||
return m_table->GetString(keyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at the specified key. If the key is not found, returns the
|
||||
* default value.
|
||||
* @param keyName the key
|
||||
* @return the value
|
||||
*/
|
||||
std::string SmartDashboard::GetString(std::string keyName,
|
||||
std::string defaultValue) {
|
||||
std::string SmartDashboard::GetString(llvm::StringRef keyName,
|
||||
llvm::StringRef defaultValue) {
|
||||
return m_table->GetString(keyName, defaultValue);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ project(WPILibC++Devices)
|
||||
file(GLOB_RECURSE SRC_FILES src/*.cpp)
|
||||
include_directories(include/ ${WPILIB_INCLUDES} ${HAL_API_INCLUDES} ${NWT_API_INCLUDES})
|
||||
add_library(wpilib_nonshared STATIC ${SRC_FILES} ${COM_SRC_FILES})
|
||||
target_link_libraries(wpilib_nonshared HALAthena NetworkTables ${NI_LIBS})
|
||||
target_link_libraries(wpilib_nonshared HALAthena ntcore ${NI_LIBS})
|
||||
INSTALL(TARGETS wpilib_nonshared ARCHIVE DESTINATION lib COMPONENT lib)
|
||||
INSTALL(DIRECTORY include DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT headers)
|
||||
# lib/ c m gcc_s ld-linux
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "HAL/cpp/Semaphore.hpp"
|
||||
#include "HAL/HAL.hpp"
|
||||
#include "LiveWindow/LiveWindowSendable.h"
|
||||
#include "tables/ITable.h"
|
||||
#include "tables/ITableListener.h"
|
||||
#include "NetworkCommunication/CANSessionMux.h"
|
||||
#include "CAN/can_proto.h"
|
||||
|
||||
@@ -232,8 +232,8 @@ class CANJaguar : public MotorSafety,
|
||||
|
||||
std::unique_ptr<MotorSafetyHelper> m_safetyHelper;
|
||||
|
||||
void ValueChanged(std::shared_ptr<ITable> source, const std::string &key, EntryValue value,
|
||||
bool isNew) override;
|
||||
void ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) override;
|
||||
void UpdateTable() override;
|
||||
void StartLiveWindowMode() override;
|
||||
void StopLiveWindowMode() override;
|
||||
|
||||
@@ -157,8 +157,8 @@ class CANTalon : public MotorSafety,
|
||||
double GetSetpoint() const override;
|
||||
|
||||
// LiveWindow stuff.
|
||||
void ValueChanged(std::shared_ptr<ITable> source, const std::string &key, EntryValue value,
|
||||
bool isNew) override;
|
||||
void ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) override;
|
||||
void UpdateTable() override;
|
||||
void StartLiveWindowMode() override;
|
||||
void StopLiveWindowMode() override;
|
||||
|
||||
@@ -50,8 +50,8 @@ class Compressor : public SensorBase,
|
||||
std::string GetSmartDashboardType() const override;
|
||||
void InitTable(std::shared_ptr<ITable> subTable) override;
|
||||
std::shared_ptr<ITable> GetTable() const override;
|
||||
void ValueChanged(std::shared_ptr<ITable> source, const std::string &key, EntryValue value,
|
||||
bool isNew) override;
|
||||
void ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) override;
|
||||
|
||||
protected:
|
||||
void *m_pcm_pointer;
|
||||
|
||||
@@ -38,8 +38,8 @@ class DigitalOutput : public DigitalSource,
|
||||
virtual uint32_t GetModuleForRouting() const;
|
||||
virtual bool GetAnalogTriggerForRouting() const;
|
||||
|
||||
virtual void ValueChanged(std::shared_ptr<ITable> source, const std::string &key,
|
||||
EntryValue value, bool isNew);
|
||||
virtual void ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew);
|
||||
void UpdateTable();
|
||||
void StartLiveWindowMode();
|
||||
void StopLiveWindowMode();
|
||||
|
||||
@@ -34,8 +34,8 @@ class DoubleSolenoid : public SolenoidBase,
|
||||
bool IsFwdSolenoidBlackListed() const;
|
||||
bool IsRevSolenoidBlackListed() const;
|
||||
|
||||
void ValueChanged(std::shared_ptr<ITable> source, const std::string& key, EntryValue value,
|
||||
bool isNew);
|
||||
void ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew);
|
||||
void UpdateTable();
|
||||
void StartLiveWindowMode();
|
||||
void StopLiveWindowMode();
|
||||
|
||||
@@ -99,8 +99,8 @@ class PWM : public SensorBase,
|
||||
int32_t m_deadbandMinPwm;
|
||||
int32_t m_minPwm;
|
||||
|
||||
void ValueChanged(std::shared_ptr<ITable> source, const std::string& key, EntryValue value,
|
||||
bool isNew) override;
|
||||
void ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) override;
|
||||
void UpdateTable() override;
|
||||
void StartLiveWindowMode() override;
|
||||
void StopLiveWindowMode() override;
|
||||
|
||||
@@ -23,72 +23,41 @@
|
||||
* <p>This class loads and saves from a file
|
||||
* inside the RoboRIO. The user can not access the file directly, but may
|
||||
* modify values at specific
|
||||
* fields which will then be saved to the file when {@link Preferences#Save()
|
||||
* Save()} is called.</p>
|
||||
* fields which will then be automatically periodically saved to the file
|
||||
* by the NetworkTable server.</p>
|
||||
*
|
||||
* <p>This class is thread safe.</p>
|
||||
*
|
||||
* <p>This will also interact with {@link NetworkTable} by creating a table
|
||||
* called "Preferences" with all the
|
||||
* key-value pairs. To save using {@link NetworkTable}, simply set the boolean
|
||||
* at position "~S A V E~" to true.
|
||||
* Also, if the value of any variable is " in the {@link NetworkTable}, then
|
||||
* that represents non-existence in the
|
||||
* {@link Preferences} table</p>
|
||||
* called "Preferences" with all the key-value pairs.</p>
|
||||
*/
|
||||
class Preferences : public ErrorBase, public ITableListener {
|
||||
class Preferences : public ErrorBase {
|
||||
public:
|
||||
static Preferences *GetInstance();
|
||||
|
||||
std::vector<std::string> GetKeys();
|
||||
std::string GetString(const char *key, const char *defaultValue = "");
|
||||
int GetString(const char *key, char *value, int valueSize,
|
||||
const char *defaultValue = "");
|
||||
int GetInt(const char *key, int defaultValue = 0);
|
||||
double GetDouble(const char *key, double defaultValue = 0.0);
|
||||
float GetFloat(const char *key, float defaultValue = 0.0);
|
||||
bool GetBoolean(const char *key, bool defaultValue = false);
|
||||
int64_t GetLong(const char *key, int64_t defaultValue = 0);
|
||||
void PutString(const char *key, const char *value);
|
||||
void PutInt(const char *key, int value);
|
||||
void PutDouble(const char *key, double value);
|
||||
void PutFloat(const char *key, float value);
|
||||
void PutBoolean(const char *key, bool value);
|
||||
void PutLong(const char *key, int64_t value);
|
||||
std::string GetString(llvm::StringRef key, llvm::StringRef defaultValue = "");
|
||||
int GetInt(llvm::StringRef key, int defaultValue = 0);
|
||||
double GetDouble(llvm::StringRef key, double defaultValue = 0.0);
|
||||
float GetFloat(llvm::StringRef key, float defaultValue = 0.0);
|
||||
bool GetBoolean(llvm::StringRef key, bool defaultValue = false);
|
||||
int64_t GetLong(llvm::StringRef key, int64_t defaultValue = 0);
|
||||
void PutString(llvm::StringRef key, llvm::StringRef value);
|
||||
void PutInt(llvm::StringRef key, int value);
|
||||
void PutDouble(llvm::StringRef key, double value);
|
||||
void PutFloat(llvm::StringRef key, float value);
|
||||
void PutBoolean(llvm::StringRef key, bool value);
|
||||
void PutLong(llvm::StringRef key, int64_t value);
|
||||
[[deprecated(
|
||||
"Saving is now automatically performed by the NetworkTables server.")]]
|
||||
void Save();
|
||||
bool ContainsKey(const char *key);
|
||||
void Remove(const char *key);
|
||||
|
||||
void ValueChanged(std::shared_ptr<ITable> source, const std::string &key, EntryValue value,
|
||||
bool isNew) override;
|
||||
bool ContainsKey(llvm::StringRef key);
|
||||
void Remove(llvm::StringRef key);
|
||||
|
||||
protected:
|
||||
Preferences();
|
||||
virtual ~Preferences() = default;
|
||||
|
||||
private:
|
||||
std::string Get(const char *key);
|
||||
void Put(const char *key, std::string value);
|
||||
|
||||
void ReadTaskRun();
|
||||
void WriteTaskRun();
|
||||
|
||||
/** The semaphore for accessing the file */
|
||||
priority_recursive_mutex m_fileLock;
|
||||
/** The semaphore for beginning reads and writes to the file */
|
||||
Semaphore m_fileOpStarted{Semaphore::kEmpty};
|
||||
/** The semaphore for reading from the table */
|
||||
priority_recursive_mutex m_tableLock;
|
||||
typedef std::map<std::string, std::string> StringMap;
|
||||
/** The actual values (String->String) */
|
||||
StringMap m_values;
|
||||
/** The keys in the order they were read from the file */
|
||||
std::vector<std::string> m_keys;
|
||||
/** The comments that were in the file sorted by which key they appeared over
|
||||
* (String->Comment) */
|
||||
StringMap m_comments;
|
||||
/** The comment at the end of the file */
|
||||
std::string m_endComment;
|
||||
Task m_readTask;
|
||||
Task m_writeTask;
|
||||
std::shared_ptr<ITable> m_table;
|
||||
};
|
||||
|
||||
@@ -40,8 +40,8 @@ class Relay : public SensorBase,
|
||||
void Set(Value value);
|
||||
Value Get() const;
|
||||
|
||||
void ValueChanged(std::shared_ptr<ITable> source, const std::string& key, EntryValue value,
|
||||
bool isNew) override;
|
||||
void ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) override;
|
||||
void UpdateTable() override;
|
||||
void StartLiveWindowMode() override;
|
||||
void StopLiveWindowMode() override;
|
||||
|
||||
@@ -30,8 +30,8 @@ class Servo : public SafePWM {
|
||||
static float GetMaxAngle() { return kMaxServoAngle; }
|
||||
static float GetMinAngle() { return kMinServoAngle; }
|
||||
|
||||
void ValueChanged(std::shared_ptr<ITable> source, const std::string& key,
|
||||
EntryValue value, bool isNew) override;
|
||||
void ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) override;
|
||||
void UpdateTable() override;
|
||||
void StartLiveWindowMode() override;
|
||||
void StopLiveWindowMode() override;
|
||||
|
||||
@@ -30,8 +30,8 @@ class Solenoid : public SolenoidBase,
|
||||
virtual bool Get() const;
|
||||
bool IsBlackListed() const;
|
||||
|
||||
void ValueChanged(std::shared_ptr<ITable> source, const std::string& key, EntryValue value,
|
||||
bool isNew);
|
||||
void ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew);
|
||||
void UpdateTable();
|
||||
void StartLiveWindowMode();
|
||||
void StopLiveWindowMode();
|
||||
|
||||
@@ -216,6 +216,8 @@ CANJaguar::~CANJaguar() {
|
||||
FRC_NetworkCommunication_CANSessionMux_sendMessage(
|
||||
m_deviceNumber | LM_API_VCOMP_T_SET, nullptr, 0,
|
||||
CAN_SEND_PERIOD_STOP_REPEATING, &status);
|
||||
|
||||
if (m_table != nullptr) m_table->RemoveTableListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1935,9 +1937,9 @@ uint8_t CANJaguar::GetDeviceID() const { return m_deviceNumber; }
|
||||
*/
|
||||
void CANJaguar::StopMotor() { DisableControl(); }
|
||||
|
||||
void CANJaguar::ValueChanged(std::shared_ptr<ITable> source, const std::string &key,
|
||||
EntryValue value, bool isNew) {
|
||||
Set(value.f);
|
||||
void CANJaguar::ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) {
|
||||
if (value->type() == NT_DOUBLE) Set(value->GetDouble());
|
||||
}
|
||||
|
||||
void CANJaguar::UpdateTable() {
|
||||
|
||||
@@ -44,6 +44,7 @@ CANTalon::CANTalon(int deviceNumber, int controlPeriodMs)
|
||||
}
|
||||
|
||||
CANTalon::~CANTalon() {
|
||||
if (m_table != nullptr) m_table->RemoveTableListener(this);
|
||||
if (m_hasBeenMoved) return;
|
||||
Disable();
|
||||
}
|
||||
@@ -1275,9 +1276,10 @@ bool CANTalon::GetInverted() const { return m_isInverted; }
|
||||
*/
|
||||
void CANTalon::StopMotor() { Disable(); }
|
||||
|
||||
void CANTalon::ValueChanged(std::shared_ptr<ITable> source, const std::string& key,
|
||||
EntryValue value, bool isNew) {
|
||||
Set(value.f);
|
||||
void CANTalon::ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) {
|
||||
if (!value->IsDouble()) return;
|
||||
Set(value->GetDouble());
|
||||
}
|
||||
|
||||
void CANTalon::UpdateTable() {
|
||||
|
||||
@@ -267,9 +267,10 @@ void Compressor::InitTable(std::shared_ptr<ITable> subTable) {
|
||||
|
||||
std::shared_ptr<ITable> Compressor::GetTable() const { return m_table; }
|
||||
|
||||
void Compressor::ValueChanged(std::shared_ptr<ITable> source, const std::string& key,
|
||||
EntryValue value, bool isNew) {
|
||||
if (value.b)
|
||||
void Compressor::ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) {
|
||||
if (!value->IsBoolean()) return;
|
||||
if (value->GetBoolean())
|
||||
Start();
|
||||
else
|
||||
Stop();
|
||||
|
||||
@@ -40,6 +40,7 @@ DigitalOutput::DigitalOutput(uint32_t channel) {
|
||||
* Free the resources associated with a digital output.
|
||||
*/
|
||||
DigitalOutput::~DigitalOutput() {
|
||||
if (m_table != nullptr) m_table->RemoveTableListener(this);
|
||||
if (StatusIsFatal()) return;
|
||||
// Disable the PWM in case it was running.
|
||||
DisablePWM();
|
||||
@@ -196,9 +197,10 @@ uint32_t DigitalOutput::GetModuleForRouting() const { return 0; }
|
||||
*/
|
||||
bool DigitalOutput::GetAnalogTriggerForRouting() const { return false; }
|
||||
|
||||
void DigitalOutput::ValueChanged(std::shared_ptr<ITable> source, const std::string &key,
|
||||
EntryValue value, bool isNew) {
|
||||
Set(value.b);
|
||||
void DigitalOutput::ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) {
|
||||
if (!value->IsBoolean()) return;
|
||||
Set(value->GetBoolean());
|
||||
}
|
||||
|
||||
void DigitalOutput::UpdateTable() {}
|
||||
|
||||
@@ -91,6 +91,7 @@ DoubleSolenoid::~DoubleSolenoid() {
|
||||
m_allocated->Free(m_moduleNumber * kSolenoidChannels + m_forwardChannel);
|
||||
m_allocated->Free(m_moduleNumber * kSolenoidChannels + m_reverseChannel);
|
||||
}
|
||||
if (m_table != nullptr) m_table->RemoveTableListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,13 +156,14 @@ bool DoubleSolenoid::IsRevSolenoidBlackListed() const {
|
||||
return (blackList & m_reverseMask) ? 1 : 0;
|
||||
}
|
||||
|
||||
void DoubleSolenoid::ValueChanged(std::shared_ptr<ITable> source, const std::string &key,
|
||||
EntryValue value, bool isNew) {
|
||||
void DoubleSolenoid::ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value,
|
||||
bool isNew) {
|
||||
if (!value->IsString()) return;
|
||||
Value lvalue = kOff;
|
||||
std::string *val = (std::string *)value.ptr;
|
||||
if (*val == "Forward")
|
||||
if (value->GetString() == "Forward")
|
||||
lvalue = kForward;
|
||||
else if (*val == "Reverse")
|
||||
else if (value->GetString() == "Reverse")
|
||||
lvalue = kReverse;
|
||||
Set(lvalue);
|
||||
}
|
||||
|
||||
@@ -73,6 +73,10 @@ void PIDController::Initialize(float Kp, float Ki, float Kd, float Kf,
|
||||
HALReport(HALUsageReporting::kResourceType_PIDController, instances);
|
||||
}
|
||||
|
||||
PIDController::~PIDController() {
|
||||
if (m_table != nullptr) m_table->RemoveTableListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the Calculate method as a non-static method. This avoids having to
|
||||
* prepend
|
||||
@@ -551,18 +555,22 @@ void PIDController::InitTable(std::shared_ptr<ITable> table) {
|
||||
|
||||
std::shared_ptr<ITable> PIDController::GetTable() const { return m_table; }
|
||||
|
||||
void PIDController::ValueChanged(std::shared_ptr<ITable> source, const std::string &key,
|
||||
EntryValue value, bool isNew) {
|
||||
void PIDController::ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) {
|
||||
if (key == kP || key == kI || key == kD || key == kF) {
|
||||
if (m_P != m_table->GetNumber(kP) || m_I != m_table->GetNumber(kI) ||
|
||||
m_D != m_table->GetNumber(kD) || m_F != m_table->GetNumber(kF)) {
|
||||
if (m_P != m_table->GetNumber(kP, 0.0) ||
|
||||
m_I != m_table->GetNumber(kI, 0.0) ||
|
||||
m_D != m_table->GetNumber(kD, 0.0) ||
|
||||
m_F != m_table->GetNumber(kF, 0.0)) {
|
||||
SetPID(m_table->GetNumber(kP, 0.0), m_table->GetNumber(kI, 0.0),
|
||||
m_table->GetNumber(kD, 0.0), m_table->GetNumber(kF, 0.0));
|
||||
}
|
||||
} else if (key == kSetpoint && m_setpoint != value.f) {
|
||||
SetSetpoint(value.f);
|
||||
} else if (key == kEnabled && m_enabled != value.b) {
|
||||
if (value.b) {
|
||||
} else if (key == kSetpoint && value->IsDouble() &&
|
||||
m_setpoint != value->GetDouble()) {
|
||||
SetSetpoint(value->GetDouble());
|
||||
} else if (key == kEnabled && value->IsBoolean() &&
|
||||
m_enabled != value->GetBoolean()) {
|
||||
if (value->GetBoolean()) {
|
||||
Enable();
|
||||
} else {
|
||||
Disable();
|
||||
|
||||
@@ -65,6 +65,8 @@ PWM::~PWM() {
|
||||
|
||||
freePWMChannel(m_pwm_ports[m_channel], &status);
|
||||
wpi_setErrorWithContext(status, getHALErrorMessage(status));
|
||||
|
||||
if (m_table != nullptr) m_table->RemoveTableListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,9 +339,10 @@ void PWM::SetZeroLatch() {
|
||||
wpi_setErrorWithContext(status, getHALErrorMessage(status));
|
||||
}
|
||||
|
||||
void PWM::ValueChanged(std::shared_ptr<ITable> source, const std::string& key, EntryValue value,
|
||||
bool isNew) {
|
||||
SetSpeed(value.f);
|
||||
void PWM::ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) {
|
||||
if (!value->IsDouble()) return;
|
||||
SetSpeed(value->GetDouble());
|
||||
}
|
||||
|
||||
void PWM::UpdateTable() {
|
||||
|
||||
@@ -15,25 +15,8 @@
|
||||
|
||||
/** The Preferences table name */
|
||||
static const char *kTableName = "Preferences";
|
||||
/** The value of the save field */
|
||||
static const char *kSaveField = "~S A V E~";
|
||||
/** The file to save to */
|
||||
static const char *kFileName = "/home/lvuser/wpilib-preferences.ini";
|
||||
/** The characters to put between a field and value */
|
||||
static const char *kValuePrefix = "=\"";
|
||||
/** The characters to put after the value */
|
||||
static const char *kValueSuffix = "\"\n";
|
||||
|
||||
Preferences::Preferences() {
|
||||
std::unique_lock<priority_recursive_mutex> sync(m_fileLock);
|
||||
m_readTask = Task("PreferencesReadTask", &Preferences::ReadTaskRun, this);
|
||||
|
||||
/* The main thread initially blocks on the semaphore. The read task signals
|
||||
* the main thread to continue after it has locked the table mutex (so the
|
||||
* table will be fully populated when the main thread can finally access it).
|
||||
*/
|
||||
m_fileOpStarted.take();
|
||||
|
||||
Preferences::Preferences() : m_table(NetworkTable::GetTable(kTableName)) {
|
||||
HALReport(HALUsageReporting::kResourceType_Preferences, 0);
|
||||
}
|
||||
|
||||
@@ -50,7 +33,7 @@ Preferences *Preferences::GetInstance() {
|
||||
* Returns a vector of all the keys
|
||||
* @return a vector of the keys
|
||||
*/
|
||||
std::vector<std::string> Preferences::GetKeys() { return m_keys; }
|
||||
std::vector<std::string> Preferences::GetKeys() { return m_table->GetKeys(); }
|
||||
|
||||
/**
|
||||
* Returns the string at the given key. If this table does not have a value
|
||||
@@ -59,25 +42,9 @@ std::vector<std::string> Preferences::GetKeys() { return m_keys; }
|
||||
* @param defaultValue the value to return if none exists in the table
|
||||
* @return either the value in the table, or the defaultValue
|
||||
*/
|
||||
std::string Preferences::GetString(const char *key, const char *defaultValue) {
|
||||
std::string value = Get(key);
|
||||
return value.empty() ? defaultValue : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string at the given key. If this table does not have a value
|
||||
* for that position, then the given defaultValue will be returned.
|
||||
* @param key the key
|
||||
* @param value the buffer to copy the value into
|
||||
* @param valueSize the size of value
|
||||
* @param defaultValue the value to return if none exists in the table
|
||||
* @return The size of the returned string
|
||||
*/
|
||||
int Preferences::GetString(const char *key, char *value, int valueSize,
|
||||
const char *defaultValue) {
|
||||
std::string stringValue = GetString(key, defaultValue);
|
||||
stringValue.copy(value, valueSize);
|
||||
return stringValue.size();
|
||||
std::string Preferences::GetString(llvm::StringRef key,
|
||||
llvm::StringRef defaultValue) {
|
||||
return m_table->GetString(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,11 +54,8 @@ int Preferences::GetString(const char *key, char *value, int valueSize,
|
||||
* @param defaultValue the value to return if none exists in the table
|
||||
* @return either the value in the table, or the defaultValue
|
||||
*/
|
||||
int Preferences::GetInt(const char *key, int defaultValue) {
|
||||
std::string value = Get(key);
|
||||
if (value.empty()) return defaultValue;
|
||||
|
||||
return strtol(value.c_str(), nullptr, 0);
|
||||
int Preferences::GetInt(llvm::StringRef key, int defaultValue) {
|
||||
return static_cast<int>(m_table->GetNumber(key, defaultValue));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,11 +65,8 @@ int Preferences::GetInt(const char *key, int defaultValue) {
|
||||
* @param defaultValue the value to return if none exists in the table
|
||||
* @return either the value in the table, or the defaultValue
|
||||
*/
|
||||
double Preferences::GetDouble(const char *key, double defaultValue) {
|
||||
std::string value = Get(key);
|
||||
if (value.empty()) return defaultValue;
|
||||
|
||||
return strtod(value.c_str(), nullptr);
|
||||
double Preferences::GetDouble(llvm::StringRef key, double defaultValue) {
|
||||
return m_table->GetNumber(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,11 +76,8 @@ double Preferences::GetDouble(const char *key, double defaultValue) {
|
||||
* @param defaultValue the value to return if none exists in the table
|
||||
* @return either the value in the table, or the defaultValue
|
||||
*/
|
||||
float Preferences::GetFloat(const char *key, float defaultValue) {
|
||||
std::string value = Get(key);
|
||||
if (value.empty()) return defaultValue;
|
||||
|
||||
return strtod(value.c_str(), nullptr);
|
||||
float Preferences::GetFloat(llvm::StringRef key, float defaultValue) {
|
||||
return static_cast<float>(m_table->GetNumber(key, defaultValue));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,19 +87,8 @@ float Preferences::GetFloat(const char *key, float defaultValue) {
|
||||
* @param defaultValue the value to return if none exists in the table
|
||||
* @return either the value in the table, or the defaultValue
|
||||
*/
|
||||
bool Preferences::GetBoolean(const char *key, bool defaultValue) {
|
||||
std::string value = Get(key);
|
||||
if (value.empty()) return defaultValue;
|
||||
|
||||
if (value.compare("true") == 0)
|
||||
return true;
|
||||
else if (value.compare("false") == 0)
|
||||
return false;
|
||||
|
||||
wpi_setWPIErrorWithContext(
|
||||
ParameterOutOfRange,
|
||||
"Boolean value does not contain \"true\" or \"false\"");
|
||||
return false;
|
||||
bool Preferences::GetBoolean(llvm::StringRef key, bool defaultValue) {
|
||||
return m_table->GetBoolean(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,15 +99,8 @@ bool Preferences::GetBoolean(const char *key, bool defaultValue) {
|
||||
* @param defaultValue the value to return if none exists in the table
|
||||
* @return either the value in the table, or the defaultValue
|
||||
*/
|
||||
int64_t Preferences::GetLong(const char *key, int64_t defaultValue) {
|
||||
std::string value = Get(key);
|
||||
if (value.empty()) return defaultValue;
|
||||
|
||||
// Ummm... not available in our VxWorks...
|
||||
// return strtoll(value.c_str(), nullptr, 0);
|
||||
int64_t intVal;
|
||||
sscanf(value.c_str(), "%lld", &intVal);
|
||||
return intVal;
|
||||
int64_t Preferences::GetLong(llvm::StringRef key, int64_t defaultValue) {
|
||||
return static_cast<int64_t>(m_table->GetNumber(key, defaultValue));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,24 +109,12 @@ int64_t Preferences::GetLong(const char *key, int64_t defaultValue) {
|
||||
* <p>The value may not have quotation marks, nor may the key
|
||||
* have any whitespace nor an equals sign</p>
|
||||
*
|
||||
* <p>This will <b>NOT</b> save the value to memory between power cycles,
|
||||
* to do that you must call {@link Preferences#Save() Save()} (which must be
|
||||
* used with care).
|
||||
* at some point after calling this.</p>
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
*/
|
||||
void Preferences::PutString(const char *key, const char *value) {
|
||||
if (value == nullptr) {
|
||||
wpi_setWPIErrorWithContext(NullParameter, "value");
|
||||
return;
|
||||
}
|
||||
if (std::string(value).find_first_of("\"") != std::string::npos) {
|
||||
wpi_setWPIErrorWithContext(ParameterOutOfRange,
|
||||
"value contains illegal characters");
|
||||
return;
|
||||
}
|
||||
Put(key, value);
|
||||
void Preferences::PutString(llvm::StringRef key, llvm::StringRef value) {
|
||||
m_table->PutString(key, value);
|
||||
m_table->SetPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,17 +122,12 @@ void Preferences::PutString(const char *key, const char *value) {
|
||||
*
|
||||
* <p>The key may not have any whitespace nor an equals sign</p>
|
||||
*
|
||||
* <p>This will <b>NOT</b> save the value to memory between power cycles,
|
||||
* to do that you must call {@link Preferences#Save() Save()} (which must be
|
||||
* used with care)
|
||||
* at some point after calling this.</p>
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
*/
|
||||
void Preferences::PutInt(const char *key, int value) {
|
||||
char buf[32];
|
||||
snprintf(buf, 32, "%d", value);
|
||||
Put(key, buf);
|
||||
void Preferences::PutInt(llvm::StringRef key, int value) {
|
||||
m_table->PutNumber(key, value);
|
||||
m_table->SetPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,17 +135,12 @@ void Preferences::PutInt(const char *key, int value) {
|
||||
*
|
||||
* <p>The key may not have any whitespace nor an equals sign</p>
|
||||
*
|
||||
* <p>This will <b>NOT</b> save the value to memory between power cycles,
|
||||
* to do that you must call {@link Preferences#Save() Save()} (which must be
|
||||
* used with care)
|
||||
* at some point after calling this.</p>
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
*/
|
||||
void Preferences::PutDouble(const char *key, double value) {
|
||||
char buf[32];
|
||||
snprintf(buf, 32, "%f", value);
|
||||
Put(key, buf);
|
||||
void Preferences::PutDouble(llvm::StringRef key, double value) {
|
||||
m_table->PutNumber(key, value);
|
||||
m_table->SetPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,17 +148,12 @@ void Preferences::PutDouble(const char *key, double value) {
|
||||
*
|
||||
* <p>The key may not have any whitespace nor an equals sign</p>
|
||||
*
|
||||
* <p>This will <b>NOT</b> save the value to memory between power cycles,
|
||||
* to do that you must call {@link Preferences#Save() Save()} (which must be
|
||||
* used with care)
|
||||
* at some point after calling this.</p>
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
*/
|
||||
void Preferences::PutFloat(const char *key, float value) {
|
||||
char buf[32];
|
||||
snprintf(buf, 32, "%f", value);
|
||||
Put(key, buf);
|
||||
void Preferences::PutFloat(llvm::StringRef key, float value) {
|
||||
m_table->PutNumber(key, value);
|
||||
m_table->SetPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -248,15 +161,12 @@ void Preferences::PutFloat(const char *key, float value) {
|
||||
*
|
||||
* <p>The key may not have any whitespace nor an equals sign</p>
|
||||
*
|
||||
* <p>This will <b>NOT</b> save the value to memory between power cycles,
|
||||
* to do that you must call {@link Preferences#Save() Save()} (which must be
|
||||
* used with care)
|
||||
* at some point after calling this.</p>
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
*/
|
||||
void Preferences::PutBoolean(const char *key, bool value) {
|
||||
Put(key, value ? "true" : "false");
|
||||
void Preferences::PutBoolean(llvm::StringRef key, bool value) {
|
||||
m_table->PutBoolean(key, value);
|
||||
m_table->SetPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,282 +174,35 @@ void Preferences::PutBoolean(const char *key, bool value) {
|
||||
*
|
||||
* <p>The key may not have any whitespace nor an equals sign</p>
|
||||
*
|
||||
* <p>This will <b>NOT</b> save the value to memory between power cycles,
|
||||
* to do that you must call {@link Preferences#Save() Save()} (which must be
|
||||
* used with care)
|
||||
* at some point after calling this.</p>
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
*/
|
||||
void Preferences::PutLong(const char *key, int64_t value) {
|
||||
char buf[32];
|
||||
snprintf(buf, 32, "%lld", value);
|
||||
Put(key, buf);
|
||||
void Preferences::PutLong(llvm::StringRef key, int64_t value) {
|
||||
m_table->PutNumber(key, value);
|
||||
m_table->SetPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the preferences to a file on the RoboRIO.
|
||||
*
|
||||
* <p>This should <b>NOT</b> be called often.
|
||||
* Too many writes can damage the RoboRIO's flash memory.
|
||||
* While it is ok to save once or twice a match, this should never
|
||||
* be called every run of {@link IterativeRobot#TeleopPeriodic()}, etc.</p>
|
||||
*
|
||||
* <p>The actual writing of the file is done in a separate thread.
|
||||
* However, any call to a get or put method will wait until the table is fully
|
||||
* saved before continuing.</p>
|
||||
* This function is no longer required, as NetworkTables automatically
|
||||
* saves persistent values (which all Preferences values are) periodically
|
||||
* when running as a server.
|
||||
* @deprecated backwards compatibility shim
|
||||
*/
|
||||
void Preferences::Save() {
|
||||
std::unique_lock<priority_recursive_mutex> sync(m_fileLock);
|
||||
m_writeTask = Task("PreferencesWriteTask", &Preferences::WriteTaskRun, this);
|
||||
m_fileOpStarted.take();
|
||||
}
|
||||
void Preferences::Save() {}
|
||||
|
||||
/**
|
||||
* Returns whether or not there is a key with the given name.
|
||||
* @param key the key
|
||||
* @return if there is a value at the given key
|
||||
*/
|
||||
bool Preferences::ContainsKey(const char *key) { return !Get(key).empty(); }
|
||||
bool Preferences::ContainsKey(llvm::StringRef key) {
|
||||
return m_table->ContainsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a preference
|
||||
* @param key the key
|
||||
*/
|
||||
void Preferences::Remove(const char *key) {
|
||||
m_values.erase(std::string(key));
|
||||
auto it = m_keys.begin();
|
||||
for (; it != m_keys.end(); it++) {
|
||||
if (it->compare(key) == 0) {
|
||||
m_keys.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at the given key.
|
||||
* @param key the key
|
||||
* @return the value (or empty if none exists)
|
||||
*/
|
||||
std::string Preferences::Get(const char *key) {
|
||||
std::unique_lock<priority_recursive_mutex> sync(m_tableLock);
|
||||
if (key == nullptr) {
|
||||
wpi_setWPIErrorWithContext(NullParameter, "key");
|
||||
return "";
|
||||
}
|
||||
return m_values[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given value into the given key position
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
*/
|
||||
void Preferences::Put(const char *key, std::string value) {
|
||||
std::unique_lock<priority_recursive_mutex> sync(m_tableLock);
|
||||
if (key == nullptr) {
|
||||
wpi_setWPIErrorWithContext(NullParameter, "key");
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::string(key).find_first_of("=\n\r \t\"") != std::string::npos) {
|
||||
wpi_setWPIErrorWithContext(ParameterOutOfRange,
|
||||
"key contains illegal characters");
|
||||
return;
|
||||
}
|
||||
|
||||
std::pair<StringMap::iterator, bool> ret =
|
||||
m_values.insert(StringMap::value_type(key, value));
|
||||
if (ret.second)
|
||||
m_keys.push_back(key);
|
||||
else
|
||||
ret.first->second = value;
|
||||
|
||||
NetworkTable* table = NetworkTable::GetTable(kTableName);
|
||||
table->PutString(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* The internal method to read from a file.
|
||||
* This will be called in its own thread when the preferences singleton is
|
||||
* first created.
|
||||
*/
|
||||
void Preferences::ReadTaskRun() {
|
||||
std::unique_lock<priority_recursive_mutex> sync(m_tableLock);
|
||||
m_fileOpStarted.give();
|
||||
|
||||
std::string comment;
|
||||
|
||||
FILE *file = nullptr;
|
||||
file = fopen(kFileName, "r");
|
||||
|
||||
if (file != nullptr) {
|
||||
std::string buffer;
|
||||
while (true) {
|
||||
char value;
|
||||
do {
|
||||
value = fgetc(file);
|
||||
} while (value == ' ' || value == '\t');
|
||||
|
||||
if (value == '\n' || value == ';') {
|
||||
if (value == '\n') {
|
||||
comment += "\n";
|
||||
} else {
|
||||
buffer.clear();
|
||||
for (; value != '\n' && !feof(file); value = fgetc(file))
|
||||
buffer += value;
|
||||
buffer += '\n';
|
||||
comment += buffer;
|
||||
}
|
||||
} else if (value == '[') {
|
||||
// Find the end of the section and the new line after it and throw it
|
||||
// away
|
||||
for (; value != ']' && !feof(file); value = fgetc(file))
|
||||
;
|
||||
for (; value != '\n' && !feof(file); value = fgetc(file))
|
||||
;
|
||||
} else {
|
||||
buffer.clear();
|
||||
for (; value != '=' && !feof(file);) {
|
||||
buffer += value;
|
||||
do {
|
||||
value = fgetc(file);
|
||||
} while (value == ' ' || value == '\t');
|
||||
}
|
||||
std::string name = buffer;
|
||||
buffer.clear();
|
||||
|
||||
bool shouldBreak = false;
|
||||
|
||||
do {
|
||||
value = fgetc(file);
|
||||
} while (value == ' ' || value == '\t');
|
||||
|
||||
if (value == '"') {
|
||||
for (value = fgetc(file); value != '"' && !feof(file);
|
||||
value = fgetc(file))
|
||||
buffer += value;
|
||||
|
||||
// Clear the line
|
||||
while (fgetc(file) != '\n' && !feof(file))
|
||||
;
|
||||
} else {
|
||||
for (; value != '\n' && !feof(file);) {
|
||||
buffer += value;
|
||||
do {
|
||||
value = fgetc(file);
|
||||
} while (value == ' ' || value == '\t');
|
||||
}
|
||||
if (feof(file)) shouldBreak = true;
|
||||
}
|
||||
|
||||
std::string value = buffer;
|
||||
|
||||
if (!name.empty() && !value.empty()) {
|
||||
m_keys.push_back(name);
|
||||
m_values.insert(std::pair<std::string, std::string>(name, value));
|
||||
NetworkTable::GetTable(kTableName)->PutString(name, value);
|
||||
|
||||
if (!comment.empty()) {
|
||||
m_comments.insert(
|
||||
std::pair<std::string, std::string>(name, comment));
|
||||
comment.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldBreak) break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
wpi_setErrnoErrorWithContext("Opening preferences file");
|
||||
}
|
||||
|
||||
if (file != nullptr) fclose(file);
|
||||
|
||||
if (!comment.empty()) m_endComment = comment;
|
||||
|
||||
NetworkTable::GetTable(kTableName)->PutBoolean(kSaveField, false);
|
||||
NetworkTable::GetTable(kTableName)->AddTableListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method that actually writes the table to a file.
|
||||
* This is called in its own thread when {@link Preferences#Save() Save()} is
|
||||
* called.
|
||||
*/
|
||||
void Preferences::WriteTaskRun() {
|
||||
std::unique_lock<priority_recursive_mutex> sync(m_tableLock);
|
||||
m_fileOpStarted.give();
|
||||
|
||||
FILE *file = nullptr;
|
||||
file = fopen(kFileName, "w");
|
||||
|
||||
fputs("[Preferences]\n", file);
|
||||
auto it = m_keys.begin();
|
||||
for (; it != m_keys.end(); it++) {
|
||||
std::string key = *it;
|
||||
std::string value = m_values[key];
|
||||
std::string comment = m_comments[key];
|
||||
|
||||
if (!comment.empty()) fputs(comment.c_str(), file);
|
||||
|
||||
fputs(key.c_str(), file);
|
||||
fputs(kValuePrefix, file);
|
||||
fputs(value.c_str(), file);
|
||||
fputs(kValueSuffix, file);
|
||||
}
|
||||
|
||||
if (!m_endComment.empty()) fputs(m_endComment.c_str(), file);
|
||||
|
||||
if (file != nullptr) fclose(file);
|
||||
|
||||
NetworkTable::GetTable(kTableName)->PutBoolean(kSaveField, false);
|
||||
}
|
||||
|
||||
static bool isKeyAcceptable(const std::string &value) {
|
||||
for (auto letter : value) {
|
||||
switch (letter) {
|
||||
case '=':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '[':
|
||||
case ']':
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Preferences::ValueChanged(std::shared_ptr<ITable> table, const std::string &key,
|
||||
EntryValue value, bool isNew) {
|
||||
if (key == kSaveField) {
|
||||
if (table->GetBoolean(kSaveField, false)) Save();
|
||||
} else {
|
||||
std::unique_lock<priority_recursive_mutex> sync(m_tableLock);
|
||||
|
||||
if (!isKeyAcceptable(key) ||
|
||||
table->GetString(key, "").find('"') != std::string::npos) {
|
||||
if (m_values.find(key) != m_values.end()) {
|
||||
m_values.erase(key);
|
||||
auto it = m_keys.begin();
|
||||
for (; it != m_keys.end(); it++) {
|
||||
if (key == *it) {
|
||||
m_keys.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
table->PutString(key, "\"");
|
||||
}
|
||||
} else {
|
||||
std::pair<StringMap::iterator, bool> ret = m_values.insert(
|
||||
StringMap::value_type(key, table->GetString(key, "")));
|
||||
if (ret.second)
|
||||
m_keys.push_back(key);
|
||||
else
|
||||
ret.first->second = table->GetString(key, "");
|
||||
}
|
||||
}
|
||||
void Preferences::Remove(llvm::StringRef key) {
|
||||
m_table->Delete(key);
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ Relay::~Relay() {
|
||||
if (m_direction == kBothDirections || m_direction == kReverseOnly) {
|
||||
relayChannels->Free(m_channel * 2 + 1);
|
||||
}
|
||||
if (m_table != nullptr) m_table->RemoveTableListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,16 +190,16 @@ Relay::Value Relay::Get() const {
|
||||
wpi_setErrorWithContext(status, getHALErrorMessage(status));
|
||||
}
|
||||
|
||||
void Relay::ValueChanged(std::shared_ptr<ITable> source, const std::string &key,
|
||||
EntryValue value, bool isNew) {
|
||||
std::string *val = (std::string *)value.ptr;
|
||||
if (*val == "Off")
|
||||
void Relay::ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) {
|
||||
if (!value->IsString()) return;
|
||||
if (value->GetString() == "Off")
|
||||
Set(kOff);
|
||||
else if (*val == "On")
|
||||
else if (value->GetString() == "On")
|
||||
Set(kOn);
|
||||
else if (*val == "Forward")
|
||||
else if (value->GetString() == "Forward")
|
||||
Set(kForward);
|
||||
else if (*val == "Reverse")
|
||||
else if (value->GetString() == "Reverse")
|
||||
Set(kReverse);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "HLUsageReporting.h"
|
||||
#include "Internal/HardwareHLReporting.h"
|
||||
#include "Utility.h"
|
||||
#include "networktables/NetworkTable.h"
|
||||
#include <cstring>
|
||||
#include "HAL/HAL.hpp"
|
||||
#include <cstdio>
|
||||
@@ -51,6 +52,8 @@ RobotBase::RobotBase() : m_ds(DriverStation::GetInstance()) {
|
||||
|
||||
RobotBase::setInstance(this);
|
||||
|
||||
NetworkTable::SetNetworkIdentity("Robot");
|
||||
|
||||
FILE *file = nullptr;
|
||||
file = fopen("/tmp/frc_versions/FRC_Lib_Version.ini", "w");
|
||||
|
||||
|
||||
@@ -30,7 +30,11 @@ Servo::Servo(uint32_t channel) : SafePWM(channel) {
|
||||
// printf("Done initializing servo %d\n", channel);
|
||||
}
|
||||
|
||||
Servo::~Servo() {}
|
||||
Servo::~Servo() {
|
||||
if (m_table != nullptr) {
|
||||
m_table->RemoveTableListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the servo position.
|
||||
@@ -95,9 +99,10 @@ float Servo::GetAngle() const {
|
||||
return (float)GetPosition() * GetServoAngleRange() + kMinServoAngle;
|
||||
}
|
||||
|
||||
void Servo::ValueChanged(std::shared_ptr<ITable> source, const std::string& key,
|
||||
EntryValue value, bool isNew) {
|
||||
Set(value.f);
|
||||
void Servo::ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) {
|
||||
if (!value->IsDouble()) return;
|
||||
Set(value->GetDouble());
|
||||
}
|
||||
|
||||
void Servo::UpdateTable() {
|
||||
|
||||
@@ -61,6 +61,7 @@ Solenoid::~Solenoid() {
|
||||
if (CheckSolenoidModule(m_moduleNumber)) {
|
||||
m_allocated->Free(m_moduleNumber * kSolenoidChannels + m_channel);
|
||||
}
|
||||
if (m_table != nullptr) m_table->RemoveTableListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,9 +100,10 @@ bool Solenoid::IsBlackListed() const {
|
||||
return (value != 0);
|
||||
}
|
||||
|
||||
void Solenoid::ValueChanged(std::shared_ptr<ITable> source, const std::string& key,
|
||||
EntryValue value, bool isNew) {
|
||||
Set(value.b);
|
||||
void Solenoid::ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) {
|
||||
if (!value->IsBoolean()) return;
|
||||
Set(value->GetBoolean());
|
||||
}
|
||||
|
||||
void Solenoid::UpdateTable() {
|
||||
|
||||
@@ -7,4 +7,4 @@ file(GLOB_RECURSE SRC_FILES src/*.cpp src/gtest/src/gtest-all.cc src/gtest/src/g
|
||||
include_directories(include/ src/gtest/ src/gtest/include/ ../wpilibC++Devices/include/ ${WPILIB_INCLUDES} ${HAL_API_INCLUDES} ${NWT_API_INCLUDES})
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -Wno-unused-variable")
|
||||
add_executable(FRCUserProgram ${SRC_FILES})
|
||||
target_link_libraries(FRCUserProgram wpilib_nonshared HALAthena NetworkTables ${NI_LIBS})
|
||||
target_link_libraries(FRCUserProgram wpilib_nonshared HALAthena ntcore ${NI_LIBS})
|
||||
|
||||
@@ -11,25 +11,27 @@
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
|
||||
static const char *kFileName = "/home/lvuser/wpilib-preferences.ini";
|
||||
static const double kSaveTime = 0.2;
|
||||
static const char *kFileName = "networktables.ini";
|
||||
static const double kSaveTime = 1.2;
|
||||
|
||||
/**
|
||||
* If we write a new wpilib-preference.ini with some sample values, test that
|
||||
* If we write a new networktables.ini with some sample values, test that
|
||||
* we get those same values back using the Preference class.
|
||||
*/
|
||||
TEST(PreferencesTest, ReadPreferencesFromFile) {
|
||||
NetworkTable::Shutdown();
|
||||
std::remove(kFileName);
|
||||
std::ofstream preferencesFile(kFileName);
|
||||
preferencesFile << "[Preferences]" << std::endl;
|
||||
preferencesFile << "testFileGetString=\"Hello, preferences file\""
|
||||
preferencesFile << "[NetworkTables Storage 3.0]" << std::endl;
|
||||
preferencesFile << "string \"/Preferences/testFileGetString\"=\"Hello, preferences file\""
|
||||
<< std::endl;
|
||||
preferencesFile << "testFileGetInt=\"1\"" << std::endl;
|
||||
preferencesFile << "testFileGetDouble=\"0.5\"" << std::endl;
|
||||
preferencesFile << "testFileGetFloat=\"0.25\"" << std::endl;
|
||||
preferencesFile << "testFileGetBoolean=\"true\"" << std::endl;
|
||||
preferencesFile << "testFileGetLong=\"1000000000000000000\"" << std::endl;
|
||||
preferencesFile << "double \"/Preferences/testFileGetInt\"=1" << std::endl;
|
||||
preferencesFile << "double \"/Preferences/testFileGetDouble\"=0.5" << std::endl;
|
||||
preferencesFile << "double \"/Preferences/testFileGetFloat\"=0.25" << std::endl;
|
||||
preferencesFile << "boolean \"/Preferences/testFileGetBoolean\"=true" << std::endl;
|
||||
preferencesFile << "double \"/Preferences/testFileGetLong\"=1000000000000000000" << std::endl;
|
||||
preferencesFile.close();
|
||||
NetworkTable::Initialize();
|
||||
|
||||
Preferences *preferences = Preferences::GetInstance();
|
||||
EXPECT_EQ("Hello, preferences file",
|
||||
@@ -43,9 +45,13 @@ TEST(PreferencesTest, ReadPreferencesFromFile) {
|
||||
|
||||
/**
|
||||
* If we set some values using the Preferences class, test that they show up
|
||||
* in wpilib-preferences.ini
|
||||
* in networktables.ini
|
||||
*/
|
||||
TEST(PreferencesTest, WritePreferencesToFile) {
|
||||
NetworkTable::Shutdown();
|
||||
NetworkTable::GlobalDeleteAll();
|
||||
std::remove(kFileName);
|
||||
NetworkTable::Initialize();
|
||||
Preferences *preferences = Preferences::GetInstance();
|
||||
preferences->PutString("testFilePutString", "Hello, preferences file");
|
||||
preferences->PutInt("testFilePutInt", 1);
|
||||
@@ -58,10 +64,13 @@ TEST(PreferencesTest, WritePreferencesToFile) {
|
||||
Wait(kSaveTime);
|
||||
|
||||
static char const *kExpectedFileContents[] = {
|
||||
"[Preferences]", "testFileGetString=\"Hello, preferences file\"",
|
||||
"testFileGetInt=\"1\"", "testFileGetDouble=\"0.5\"",
|
||||
"testFileGetFloat=\"0.25\"", "testFileGetBoolean=\"true\"",
|
||||
"testFileGetLong=\"1000000000000000000\""};
|
||||
"[NetworkTables Storage 3.0]",
|
||||
"boolean \"/Preferences/testFilePutBoolean\"=true",
|
||||
"double \"/Preferences/testFilePutDouble\"=0.5",
|
||||
"double \"/Preferences/testFilePutFloat\"=0.25",
|
||||
"double \"/Preferences/testFilePutInt\"=1",
|
||||
"double \"/Preferences/testFilePutLong\"=1e+18",
|
||||
"string \"/Preferences/testFilePutString\"=\"Hello, preferences file\""};
|
||||
|
||||
std::ifstream preferencesFile(kFileName);
|
||||
for (auto& kExpectedFileContent : kExpectedFileContents) {
|
||||
@@ -72,6 +81,6 @@ TEST(PreferencesTest, WritePreferencesToFile) {
|
||||
std::getline(preferencesFile, line);
|
||||
|
||||
ASSERT_EQ(kExpectedFileContent, line)
|
||||
<< "A line in wpilib-preferences.ini was not correct";
|
||||
<< "A line in networktables.ini was not correct";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,22 +20,12 @@ if (WIN32)
|
||||
add_definitions(-Dsnprintf=sprintf_s)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
file(GLOB_RECURSE SRC_FILES src/*.cpp
|
||||
../../networktables/cpp/lib/share/*.cpp
|
||||
../../networktables/cpp/lib/WIN32/*.cpp)
|
||||
else()
|
||||
file(GLOB_RECURSE SRC_FILES src/*.cpp
|
||||
../../networktables/cpp/lib/share/*.cpp
|
||||
../../networktables/cpp/lib/Athena/*.cpp)
|
||||
endif()
|
||||
|
||||
file(GLOB_RECURSE COM_SRC_FILES ../wpilibC++/src/*.cpp)
|
||||
|
||||
|
||||
set (INCLUDE_FOLDERS include
|
||||
../wpilibC++/include
|
||||
../../networktables/cpp/include
|
||||
../../networktables/ntcore/include
|
||||
../../hal/include
|
||||
${GZ_MSGS_INCLUDE_DIR}
|
||||
${Boost_INCLUDE_DIR}
|
||||
@@ -61,7 +51,7 @@ else()
|
||||
add_library(WPILibSim SHARED ${SRC_FILES} ${COM_SRC_FILES})
|
||||
endif()
|
||||
|
||||
target_link_libraries(WPILibSim gz_msgs ${PTHREAD_LIBRARY} ${Boost_LIBRARIES} ${GAZEBO_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} -fPIC) # NetworkTables
|
||||
target_link_libraries(WPILibSim gz_msgs ntcore ${PTHREAD_LIBRARY} ${Boost_LIBRARIES} ${GAZEBO_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} -fPIC) # NetworkTables
|
||||
|
||||
if (WIN32)
|
||||
set_target_properties(${project} PROPERTIES LINK_FLAGS "/DEBUG")
|
||||
|
||||
@@ -30,11 +30,12 @@ public:
|
||||
|
||||
explicit DoubleSolenoid(uint32_t forwardChannel, uint32_t reverseChannel);
|
||||
DoubleSolenoid(uint8_t moduleNumber, uint32_t forwardChannel, uint32_t reverseChannel);
|
||||
virtual ~DoubleSolenoid() = default;
|
||||
virtual ~DoubleSolenoid();
|
||||
virtual void Set(Value value);
|
||||
virtual Value Get() const;
|
||||
|
||||
void ValueChanged(std::shared_ptr<ITable> source, const std::string& key, EntryValue value, bool isNew) override;
|
||||
void ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) override;
|
||||
void UpdateTable() override;
|
||||
void StartLiveWindowMode() override;
|
||||
void StopLiveWindowMode() override;
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
};
|
||||
|
||||
explicit PWM(uint32_t channel);
|
||||
virtual ~PWM() = default;
|
||||
virtual ~PWM();
|
||||
virtual void SetRaw(unsigned short value);
|
||||
void SetPeriodMultiplier(PeriodMultiplier mult);
|
||||
void EnableDeadbandElimination(bool eliminateDeadband);
|
||||
@@ -87,7 +87,8 @@ protected:
|
||||
bool m_eliminateDeadband;
|
||||
int32_t m_centerPwm;
|
||||
|
||||
void ValueChanged(std::shared_ptr<ITable> source, const std::string& key, EntryValue value, bool isNew) override;
|
||||
void ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) override;
|
||||
void UpdateTable() override;
|
||||
void StartLiveWindowMode() override;
|
||||
void StopLiveWindowMode() override;
|
||||
|
||||
@@ -47,7 +47,8 @@ public:
|
||||
void Set(Value value);
|
||||
Value Get() const;
|
||||
|
||||
void ValueChanged(std::shared_ptr<ITable> source, const std::string& key, EntryValue value, bool isNew) override;
|
||||
void ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) override;
|
||||
void UpdateTable() override;
|
||||
void StartLiveWindowMode() override;
|
||||
void StopLiveWindowMode() override;
|
||||
|
||||
@@ -22,11 +22,12 @@ class Solenoid : public LiveWindowSendable, public ITableListener
|
||||
public:
|
||||
explicit Solenoid(uint32_t channel);
|
||||
Solenoid(uint8_t moduleNumber, uint32_t channel);
|
||||
virtual ~Solenoid() = default;
|
||||
virtual ~Solenoid();
|
||||
virtual void Set(bool on);
|
||||
virtual bool Get() const;
|
||||
|
||||
void ValueChanged(std::shared_ptr<ITable> source, const std::string& key, EntryValue value, bool isNew) override;
|
||||
void ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) override;
|
||||
void UpdateTable() override;
|
||||
void StartLiveWindowMode() override;
|
||||
void StopLiveWindowMode() override;
|
||||
|
||||
@@ -43,6 +43,10 @@ DoubleSolenoid::DoubleSolenoid(uint8_t moduleNumber, uint32_t forwardChannel, ui
|
||||
forwardChannel, this);
|
||||
}
|
||||
|
||||
DoubleSolenoid::~DoubleSolenoid() {
|
||||
if (m_table != nullptr) m_table->RemoveTableListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a solenoid.
|
||||
*
|
||||
@@ -75,12 +79,14 @@ DoubleSolenoid::Value DoubleSolenoid::Get() const
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void DoubleSolenoid::ValueChanged(std::shared_ptr<ITable> source, const std::string& key, EntryValue value, bool isNew) {
|
||||
Value lvalue = kOff;
|
||||
std::string *val = (std::string *)value.ptr;
|
||||
if (*val == "Forward")
|
||||
void DoubleSolenoid::ValueChanged(ITable *source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value,
|
||||
bool isNew) {
|
||||
if (!value->IsString()) return;
|
||||
Value lvalue = kOff;
|
||||
if (value->GetString() == "Forward")
|
||||
lvalue = kForward;
|
||||
else if (*val == "Reverse")
|
||||
else if (value->GetString() == "Reverse")
|
||||
lvalue = kReverse;
|
||||
Set(lvalue);
|
||||
}
|
||||
|
||||
@@ -93,6 +93,10 @@ void PIDController::Initialize(float Kp, float Ki, float Kd, float Kf,
|
||||
m_toleranceType = kNoTolerance;
|
||||
}
|
||||
|
||||
PIDController::~PIDController() {
|
||||
if (m_table != nullptr) m_table->RemoveTableListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the Calculate method as a non-static method. This avoids having to prepend
|
||||
* all local variables in that method with the class pointer. This way the "this"
|
||||
@@ -579,20 +583,27 @@ std::shared_ptr<ITable> PIDController::GetTable() const {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
void PIDController::ValueChanged(std::shared_ptr<ITable> source, const std::string& key, EntryValue value, bool isNew){
|
||||
if (key==kP || key==kI || key==kD || key==kF) {
|
||||
if (m_P != m_table->GetNumber(kP) || m_I != m_table->GetNumber(kI) || m_D != m_table->GetNumber(kD) || m_F != m_table->GetNumber(kF) ) {
|
||||
SetPID(m_table->GetNumber(kP, 0.0), m_table->GetNumber(kI, 0.0), m_table->GetNumber(kD, 0.0), m_table->GetNumber(kF, 0.0));
|
||||
}
|
||||
} else if (key==kSetpoint && m_setpoint != value.f) {
|
||||
SetSetpoint(value.f);
|
||||
} else if (key==kEnabled && m_enabled != value.b) {
|
||||
if (value.b) {
|
||||
Enable();
|
||||
} else {
|
||||
Disable();
|
||||
}
|
||||
}
|
||||
void PIDController::ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) {
|
||||
if (key == kP || key == kI || key == kD || key == kF) {
|
||||
if (m_P != m_table->GetNumber(kP, 0.0) ||
|
||||
m_I != m_table->GetNumber(kI, 0.0) ||
|
||||
m_D != m_table->GetNumber(kD, 0.0) ||
|
||||
m_F != m_table->GetNumber(kF, 0.0)) {
|
||||
SetPID(m_table->GetNumber(kP, 0.0), m_table->GetNumber(kI, 0.0),
|
||||
m_table->GetNumber(kD, 0.0), m_table->GetNumber(kF, 0.0));
|
||||
}
|
||||
} else if (key == kSetpoint && value->IsDouble() &&
|
||||
m_setpoint != value->GetDouble()) {
|
||||
SetSetpoint(value->GetDouble());
|
||||
} else if (key == kEnabled && value->IsBoolean() &&
|
||||
m_enabled != value->GetBoolean()) {
|
||||
if (value->GetBoolean()) {
|
||||
Enable();
|
||||
} else {
|
||||
Disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PIDController::UpdateTable() {
|
||||
|
||||
@@ -43,6 +43,10 @@ PWM::PWM(uint32_t channel)
|
||||
m_centerPwm = kPwmDisabled; // In simulation, the same thing.
|
||||
}
|
||||
|
||||
PWM::~PWM() {
|
||||
if (m_table != nullptr) m_table->RemoveTableListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optionally eliminate the deadband from a speed controller.
|
||||
* @param eliminateDeadband If true, set the motor curve on the Jaguar to eliminate
|
||||
@@ -217,8 +221,10 @@ void PWM::SetPeriodMultiplier(PeriodMultiplier mult)
|
||||
}
|
||||
|
||||
|
||||
void PWM::ValueChanged(std::shared_ptr<ITable> source, const std::string& key, EntryValue value, bool isNew) {
|
||||
SetSpeed(value.f);
|
||||
void PWM::ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) {
|
||||
if (!value->IsDouble()) return;
|
||||
SetSpeed(value->GetDouble());
|
||||
}
|
||||
|
||||
void PWM::UpdateTable() {
|
||||
|
||||
@@ -43,6 +43,7 @@ Relay::Relay(uint32_t channel, Relay::Direction direction)
|
||||
Relay::~Relay()
|
||||
{
|
||||
impl->Set(0);
|
||||
if (m_table != nullptr) m_table->RemoveTableListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,11 +141,12 @@ Relay::Value Relay::Get() const {
|
||||
}
|
||||
}
|
||||
|
||||
void Relay::ValueChanged(std::shared_ptr<ITable> source, const std::string& key, EntryValue value, bool isNew) {
|
||||
std::string *val = (std::string *) value.ptr;
|
||||
if (*val == "Off") Set(kOff);
|
||||
else if (*val == "Forward") Set(kForward);
|
||||
else if (*val == "Reverse") Set(kReverse);
|
||||
void Relay::ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) {
|
||||
if (!value->IsString()) return;
|
||||
if (value->GetString() == "Off") Set(kOff);
|
||||
else if (value->GetString() == "Forward") Set(kForward);
|
||||
else if (value->GetString() == "Reverse") Set(kReverse);
|
||||
}
|
||||
|
||||
void Relay::UpdateTable() {
|
||||
|
||||
@@ -32,6 +32,10 @@ Solenoid::Solenoid(uint8_t moduleNumber, uint32_t channel)
|
||||
this);
|
||||
}
|
||||
|
||||
Solenoid::~Solenoid() {
|
||||
if (m_table != nullptr) m_table->RemoveTableListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a solenoid.
|
||||
*
|
||||
@@ -54,8 +58,10 @@ bool Solenoid::Get() const
|
||||
}
|
||||
|
||||
|
||||
void Solenoid::ValueChanged(std::shared_ptr<ITable> source, const std::string& key, EntryValue value, bool isNew) {
|
||||
Set(value.b);
|
||||
void Solenoid::ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) {
|
||||
if (!value->IsBoolean()) return;
|
||||
Set(value->GetBoolean());
|
||||
}
|
||||
|
||||
void Solenoid::UpdateTable() {
|
||||
|
||||
@@ -3,7 +3,7 @@ apply plugin: 'cpp'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
// We need this so we can get the networktables javadoc for the javadoc task
|
||||
evaluationDependsOn(':networktables:java')
|
||||
evaluationDependsOn(':networktables:ntcore')
|
||||
|
||||
def jdkDownloadSite = 'http://www.oracle.com/technetwork/java/javase/downloads/jdk8-arm-downloads-2187472.html'
|
||||
def jdkFolder = 'jdk-linux-arm-vfp-sflt'
|
||||
@@ -117,10 +117,10 @@ sourceSets {
|
||||
|
||||
dependencies {
|
||||
compile sourceSets.shared.output
|
||||
compile project(':networktables:java')
|
||||
compile project(':networktables:ntcore')
|
||||
testCompile 'junit:junit:4.11'
|
||||
sharedCompile project(':networktables:java')
|
||||
integrationTestCompile project(':networktables:java')
|
||||
sharedCompile project(':networktables:ntcore')
|
||||
integrationTestCompile project(':networktables:ntcore')
|
||||
integrationTestCompile 'junit:junit:4.11'
|
||||
integrationTestCompile sourceSets.main.output
|
||||
integrationTestCompile sourceSets.shared.output
|
||||
@@ -130,7 +130,7 @@ dependencies {
|
||||
simulationCompile sourceSets.main.output
|
||||
simulationCompile sourceSets.shared.output
|
||||
simulationCompile project(':simulation:JavaGazebo')
|
||||
simulationCompile project(':networktables:java')
|
||||
simulationCompile project(':networktables:ntcore')
|
||||
}
|
||||
|
||||
task wpilibjJar(type: Jar) {
|
||||
@@ -156,7 +156,7 @@ task wpilibjSources(type: Jar, dependsOn: classes) {
|
||||
}
|
||||
|
||||
task javadoc(type: Javadoc, overwrite: true) {
|
||||
def netTables = project(':networktables:java')
|
||||
def netTables = project(':networktables:ntcore')
|
||||
source sourceSets.main.allJava, sourceSets.shared.allJava, netTables.sourceSets.main.allJava
|
||||
classpath = files([sourceSets.main.compileClasspath, sourceSets.shared.compileClasspath, netTables.sourceSets.main.compileClasspath])
|
||||
}
|
||||
|
||||
@@ -13,8 +13,6 @@ import java.util.Vector;
|
||||
import edu.wpi.first.wpilibj.HLUsageReporting;
|
||||
import edu.wpi.first.wpilibj.NamedSendable;
|
||||
import edu.wpi.first.wpilibj.buttons.Trigger.ButtonScheduler;
|
||||
import edu.wpi.first.wpilibj.networktables2.type.NumberArray;
|
||||
import edu.wpi.first.wpilibj.networktables2.type.StringArray;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
|
||||
/**
|
||||
@@ -318,49 +316,48 @@ public class Scheduler implements NamedSendable {
|
||||
return "Scheduler";
|
||||
}
|
||||
|
||||
private StringArray commands;
|
||||
private NumberArray ids, toCancel;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
commands = new StringArray();
|
||||
ids = new NumberArray();
|
||||
toCancel = new NumberArray();
|
||||
|
||||
m_table.putValue("Names", commands);
|
||||
m_table.putValue("Ids", ids);
|
||||
m_table.putValue("Cancel", toCancel);
|
||||
m_table.putStringArray("Names", new String[0]);
|
||||
m_table.putNumberArray("Ids", new double[0]);
|
||||
m_table.putNumberArray("Cancel", new double[0]);
|
||||
}
|
||||
|
||||
private void updateTable() {
|
||||
if (m_table != null) {
|
||||
// Get the commands to cancel
|
||||
m_table.retrieveValue("Cancel", toCancel);
|
||||
if (toCancel.size() > 0) {
|
||||
double[] toCancel = m_table.getNumberArray("Cancel", new double[0]);
|
||||
if (toCancel.length > 0) {
|
||||
for (LinkedListElement e = firstCommand; e != null; e = e.getNext()) {
|
||||
for (int i = 0; i < toCancel.size(); i++) {
|
||||
if (e.getData().hashCode() == toCancel.get(i)) {
|
||||
for (int i = 0; i < toCancel.length; i++) {
|
||||
if (e.getData().hashCode() == toCancel[i]) {
|
||||
e.getData().cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
toCancel.setSize(0);
|
||||
m_table.putValue("Cancel", toCancel);
|
||||
m_table.putNumberArray("Cancel", new double[0]);
|
||||
}
|
||||
|
||||
if (m_runningCommandsChanged) {
|
||||
commands.setSize(0);
|
||||
ids.setSize(0);
|
||||
// Set the the running commands
|
||||
int n = 0;
|
||||
for (LinkedListElement e = firstCommand; e != null; e = e.getNext()) {
|
||||
commands.add(e.getData().getName());
|
||||
ids.add(e.getData().hashCode());
|
||||
n++;
|
||||
}
|
||||
m_table.putValue("Names", commands);
|
||||
m_table.putValue("Ids", ids);
|
||||
String[] commands = new String[n];
|
||||
double[] ids = new double[n];
|
||||
n = 0;
|
||||
for (LinkedListElement e = firstCommand; e != null; e = e.getNext()) {
|
||||
commands[n] = e.getData().getName();
|
||||
ids[n] = e.getData().hashCode();
|
||||
n++;
|
||||
}
|
||||
m_table.putStringArray("Names", commands);
|
||||
m_table.putNumberArray("Ids", ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ package edu.wpi.first.wpilibj.smartdashboard;
|
||||
|
||||
import edu.wpi.first.wpilibj.Sendable;
|
||||
import edu.wpi.first.wpilibj.command.Command;
|
||||
import edu.wpi.first.wpilibj.networktables2.type.StringArray;
|
||||
import edu.wpi.first.wpilibj.networktables2.util.List;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* The {@link SendableChooser} class is a useful tool for presenting a selection
|
||||
* of options to the {@link SmartDashboard}.
|
||||
@@ -44,8 +44,8 @@ public class SendableChooser implements Sendable {
|
||||
/**
|
||||
* A table linking strings to the objects the represent
|
||||
*/
|
||||
private StringArray choices = new StringArray();
|
||||
private List values = new List();
|
||||
private ArrayList<String> choices = new ArrayList<String>();
|
||||
private ArrayList<Object> values = new ArrayList<Object>();
|
||||
private String defaultChoice = null;
|
||||
private Object defaultValue = null;
|
||||
|
||||
@@ -77,8 +77,9 @@ public class SendableChooser implements Sendable {
|
||||
// not found
|
||||
choices.add(name);
|
||||
values.add(object);
|
||||
|
||||
if (table != null) {
|
||||
table.putValue(OPTIONS, choices);
|
||||
table.putStringArray(OPTIONS, choices.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +132,7 @@ public class SendableChooser implements Sendable {
|
||||
public void initTable(ITable table) {
|
||||
this.table = table;
|
||||
if (table != null) {
|
||||
table.putValue(OPTIONS, choices);
|
||||
table.putStringArray(OPTIONS, choices.toArray(new String[0]));
|
||||
if (defaultChoice != null) {
|
||||
table.putString(DEFAULT, defaultChoice);
|
||||
}
|
||||
|
||||
@@ -6,20 +6,12 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
package edu.wpi.first.wpilibj;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
|
||||
import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType;
|
||||
import edu.wpi.first.wpilibj.communication.UsageReporting;
|
||||
import edu.wpi.first.wpilibj.networktables.NetworkTable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import edu.wpi.first.wpilibj.tables.ITableListener;
|
||||
import edu.wpi.first.wpilibj.tables.TableKeyNotDefinedException;
|
||||
|
||||
/**
|
||||
* The preferences class provides a relatively simple way to save important
|
||||
@@ -28,7 +20,7 @@ import edu.wpi.first.wpilibj.tables.ITableListener;
|
||||
* <p>
|
||||
* This class loads and saves from a file inside the RoboRIO. The user can not
|
||||
* access the file directly, but may modify values at specific fields which will
|
||||
* then be saved to the file when {@link Preferences#save() save()} is called.
|
||||
* then be automatically saved to the file by the NetworkTables server.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
@@ -37,10 +29,7 @@ import edu.wpi.first.wpilibj.tables.ITableListener;
|
||||
*
|
||||
* <p>
|
||||
* This will also interact with {@link NetworkTable} by creating a table called
|
||||
* "Preferences" with all the key-value pairs. To save using
|
||||
* {@link NetworkTable}, simply set the boolean at position ~S A V E~ to true.
|
||||
* Also, if the value of any variable is " in the {@link NetworkTable}, then
|
||||
* that represents non-existence in the {@link Preferences} table
|
||||
* "Preferences" with all the key-value pairs.
|
||||
* </p>
|
||||
*
|
||||
* @author Joe Grinstead
|
||||
@@ -51,30 +40,14 @@ public class Preferences {
|
||||
* The Preferences table name
|
||||
*/
|
||||
private static final String TABLE_NAME = "Preferences";
|
||||
/**
|
||||
* The value of the save field
|
||||
*/
|
||||
private static final String SAVE_FIELD = "~S A V E~";
|
||||
/**
|
||||
* The file to save to
|
||||
*/
|
||||
private static final String FILE_NAME = "/home/lvuser/wpilib-preferences.ini";
|
||||
/**
|
||||
* The characters to put between a field and value
|
||||
*/
|
||||
private static final byte[] VALUE_PREFIX = {'=', '\"'};
|
||||
/**
|
||||
* The characters to put after the value
|
||||
*/
|
||||
private static final byte[] VALUE_SUFFIX = {'\"', '\n'};
|
||||
/**
|
||||
* The newline character
|
||||
*/
|
||||
private static final byte[] NEW_LINE = {'\n'};
|
||||
/**
|
||||
* The singleton instance
|
||||
*/
|
||||
private static Preferences instance;
|
||||
/**
|
||||
* The network table
|
||||
*/
|
||||
private NetworkTable table;
|
||||
|
||||
/**
|
||||
* Returns the preferences instance.
|
||||
@@ -89,54 +62,10 @@ public class Preferences {
|
||||
}
|
||||
|
||||
/**
|
||||
* The semaphore for beginning reads and writes to the file
|
||||
*/
|
||||
private final Object fileLock = new Object();
|
||||
/**
|
||||
* The semaphore for reading from the table
|
||||
*/
|
||||
private final Object lock = new Object();
|
||||
/**
|
||||
* The actual values (String->String)
|
||||
*/
|
||||
private Hashtable values;
|
||||
/**
|
||||
* The keys in the order they were read from the file
|
||||
*/
|
||||
private Vector keys;
|
||||
/**
|
||||
* The comments that were in the file sorted by which key they appeared over
|
||||
* (String->Comment)
|
||||
*/
|
||||
private Hashtable comments;
|
||||
/**
|
||||
* The comment at the end of the file
|
||||
*/
|
||||
private Comment endComment;
|
||||
|
||||
/**
|
||||
* Creates a preference class that will automatically read the file in a
|
||||
* different thread. Any call to its methods will be blocked until the thread
|
||||
* is finished reading.
|
||||
* Creates a preference class.
|
||||
*/
|
||||
private Preferences() {
|
||||
values = new Hashtable();
|
||||
keys = new Vector();
|
||||
|
||||
// We synchronized on fileLock and then wait
|
||||
// for it to know that the reading thread has started
|
||||
synchronized (fileLock) {
|
||||
new Thread() {
|
||||
public void run() {
|
||||
read();
|
||||
}
|
||||
}.start();
|
||||
try {
|
||||
fileLock.wait();
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
table = NetworkTable.getTable(TABLE_NAME);
|
||||
UsageReporting.report(tResourceType.kResourceType_Preferences, 0);
|
||||
}
|
||||
|
||||
@@ -144,188 +73,81 @@ public class Preferences {
|
||||
* @return a vector of the keys
|
||||
*/
|
||||
public Vector getKeys() {
|
||||
synchronized (lock) {
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given value into the given key position
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @throws ImproperPreferenceKeyException if the key contains an illegal
|
||||
* character
|
||||
*/
|
||||
private void put(String key, String value) {
|
||||
synchronized (lock) {
|
||||
if (key == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
ImproperPreferenceKeyException.confirmString(key);
|
||||
if (values.put(key, value) == null) {
|
||||
keys.addElement(key);
|
||||
}
|
||||
NetworkTable.getTable(TABLE_NAME).putString(key, value);
|
||||
Vector<String> keys = new Vector<String>();
|
||||
for (String key : table.getKeys()) {
|
||||
keys.add(key);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given string into the preferences table.
|
||||
*
|
||||
* <p>
|
||||
* The value may not have quotation marks, nor may the key have any whitespace
|
||||
* nor an equals sign
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This will <b>NOT</b> save the value to memory between power cycles, to do
|
||||
* that you must call {@link Preferences#save() save()} (which must be used
|
||||
* with care). at some point after calling this.
|
||||
* </p>
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @throws NullPointerException if value is null
|
||||
* @throws IllegalArgumentException if value contains a quotation mark
|
||||
* @throws ImproperPreferenceKeyException if the key contains any whitespace
|
||||
* or an equals sign
|
||||
*/
|
||||
public void putString(String key, String value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (value.indexOf('"') != -1) {
|
||||
throw new IllegalArgumentException("Can not put string:" + value
|
||||
+ " because it contains quotation marks");
|
||||
}
|
||||
put(key, value);
|
||||
table.putString(key, value);
|
||||
table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given int into the preferences table.
|
||||
*
|
||||
* <p>
|
||||
* The key may not have any whitespace nor an equals sign
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This will <b>NOT</b> save the value to memory between power cycles, to do
|
||||
* that you must call {@link Preferences#save() save()} (which must be used
|
||||
* with care) at some point after calling this.
|
||||
* </p>
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @throws ImproperPreferenceKeyException if the key contains any whitespace
|
||||
* or an equals sign
|
||||
*/
|
||||
public void putInt(String key, int value) {
|
||||
put(key, String.valueOf(value));
|
||||
table.putNumber(key, value);
|
||||
table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given double into the preferences table.
|
||||
*
|
||||
* <p>
|
||||
* The key may not have any whitespace nor an equals sign
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This will <b>NOT</b> save the value to memory between power cycles, to do
|
||||
* that you must call {@link Preferences#save() save()} (which must be used
|
||||
* with care) at some point after calling this.
|
||||
* </p>
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @throws ImproperPreferenceKeyException if the key contains any whitespace
|
||||
* or an equals sign
|
||||
*/
|
||||
public void putDouble(String key, double value) {
|
||||
put(key, String.valueOf(value));
|
||||
table.putNumber(key, value);
|
||||
table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given float into the preferences table.
|
||||
*
|
||||
* <p>
|
||||
* The key may not have any whitespace nor an equals sign
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This will <b>NOT</b> save the value to memory between power cycles, to do
|
||||
* that you must call {@link Preferences#save() save()} (which must be used
|
||||
* with care) at some point after calling this.
|
||||
* </p>
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @throws ImproperPreferenceKeyException if the key contains any whitespace
|
||||
* or an equals sign
|
||||
*/
|
||||
public void putFloat(String key, float value) {
|
||||
put(key, String.valueOf(value));
|
||||
table.putNumber(key, value);
|
||||
table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given boolean into the preferences table.
|
||||
*
|
||||
* <p>
|
||||
* The key may not have any whitespace nor an equals sign
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This will <b>NOT</b> save the value to memory between power cycles, to do
|
||||
* that you must call {@link Preferences#save() save()} (which must be used
|
||||
* with care) at some point after calling this.
|
||||
* </p>
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @throws ImproperPreferenceKeyException if the key contains any whitespace
|
||||
* or an equals sign
|
||||
*/
|
||||
public void putBoolean(String key, boolean value) {
|
||||
put(key, String.valueOf(value));
|
||||
table.putBoolean(key, value);
|
||||
table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given long into the preferences table.
|
||||
*
|
||||
* <p>
|
||||
* The key may not have any whitespace nor an equals sign
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This will <b>NOT</b> save the value to memory between power cycles, to do
|
||||
* that you must call {@link Preferences#save() save()} (which must be used
|
||||
* with care) at some point after calling this.
|
||||
* </p>
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @throws ImproperPreferenceKeyException if the key contains any whitespace
|
||||
* or an equals sign
|
||||
*/
|
||||
public void putLong(String key, long value) {
|
||||
put(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at the given key.
|
||||
*
|
||||
* @param key the key
|
||||
* @return the value (or null if none exists)
|
||||
* @throws NullPointerException if the key is null
|
||||
*/
|
||||
private String get(String key) {
|
||||
synchronized (lock) {
|
||||
if (key == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
return (String) values.get(key);
|
||||
}
|
||||
table.putNumber(key, value);
|
||||
table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -333,26 +155,18 @@ public class Preferences {
|
||||
*
|
||||
* @param key the key
|
||||
* @return if there is a value at the given key
|
||||
* @throws NullPointerException if key is null
|
||||
*/
|
||||
public boolean containsKey(String key) {
|
||||
return get(key) != null;
|
||||
return table.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a preference
|
||||
*
|
||||
* @param key the key
|
||||
* @throws NullPointerException if key is null
|
||||
*/
|
||||
public void remove(String key) {
|
||||
synchronized (lock) {
|
||||
if (key == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
values.remove(key);
|
||||
keys.removeElement(key);
|
||||
}
|
||||
table.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -362,11 +176,9 @@ public class Preferences {
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
* @throws NullPointerException if the key is null
|
||||
*/
|
||||
public String getString(String key, String backup) {
|
||||
String value = get(key);
|
||||
return value == null ? backup : value;
|
||||
return table.getString(key, backup);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -376,19 +188,12 @@ public class Preferences {
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
* @throws IncompatibleTypeException if the value in the table can not be
|
||||
* converted to an int
|
||||
*/
|
||||
public int getInt(String key, int backup) {
|
||||
String value = get(key);
|
||||
if (value == null) {
|
||||
try {
|
||||
return (int)table.getNumber(key);
|
||||
} catch (TableKeyNotDefinedException e) {
|
||||
return backup;
|
||||
} else {
|
||||
try {
|
||||
return Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IncompatibleTypeException(value, "int");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,20 +204,9 @@ public class Preferences {
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
* @throws IncompatibleTypeException if the value in the table can not be
|
||||
* converted to an double
|
||||
*/
|
||||
public double getDouble(String key, double backup) {
|
||||
String value = get(key);
|
||||
if (value == null) {
|
||||
return backup;
|
||||
} else {
|
||||
try {
|
||||
return Double.parseDouble(value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IncompatibleTypeException(value, "double");
|
||||
}
|
||||
}
|
||||
return table.getDouble(key, backup);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -422,22 +216,9 @@ public class Preferences {
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
* @throws IncompatibleTypeException if the value in the table can not be
|
||||
* converted to a boolean
|
||||
*/
|
||||
public boolean getBoolean(String key, boolean backup) {
|
||||
String value = get(key);
|
||||
if (value == null) {
|
||||
return backup;
|
||||
} else {
|
||||
if (value.equalsIgnoreCase("true")) {
|
||||
return true;
|
||||
} else if (value.equalsIgnoreCase("false")) {
|
||||
return false;
|
||||
} else {
|
||||
throw new IncompatibleTypeException(value, "boolean");
|
||||
}
|
||||
}
|
||||
return table.getBoolean(key, backup);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -447,19 +228,12 @@ public class Preferences {
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
* @throws IncompatibleTypeException if the value in the table can not be
|
||||
* converted to a float
|
||||
*/
|
||||
public float getFloat(String key, float backup) {
|
||||
String value = get(key);
|
||||
if (value == null) {
|
||||
try {
|
||||
return (float)table.getNumber(key);
|
||||
} catch (TableKeyNotDefinedException e) {
|
||||
return backup;
|
||||
} else {
|
||||
try {
|
||||
return Float.parseFloat(value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IncompatibleTypeException(value, "float");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,403 +244,21 @@ public class Preferences {
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
* @throws IncompatibleTypeException if the value in the table can not be
|
||||
* converted to a long
|
||||
*/
|
||||
public long getLong(String key, long backup) {
|
||||
String value = get(key);
|
||||
if (value == null) {
|
||||
put(key, String.valueOf(backup));
|
||||
try {
|
||||
return (long)table.getNumber(key);
|
||||
} catch (TableKeyNotDefinedException e) {
|
||||
return backup;
|
||||
} else {
|
||||
try {
|
||||
return Long.parseLong(value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IncompatibleTypeException(value, "long");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the preferences to a file on the RoboRIO.
|
||||
*
|
||||
* <p>
|
||||
* This should <b>NOT</b> be called often. Too many writes can damage the
|
||||
* RoboRIO's flash memory. While it is ok to save once or twice a match, this
|
||||
* should never be called every run of {@link IterativeRobot#teleopPeriodic()}
|
||||
* .
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The actual writing of the file is done in a separate thread. However, any
|
||||
* call to a get or put method will wait until the table is fully saved before
|
||||
* continuing.
|
||||
* </p>
|
||||
* This function is no longer required, as NetworkTables automatically
|
||||
* saves persistent values (which all Preferences values are) periodically
|
||||
* when running as a server.
|
||||
* @deprecated backwards compatibility shim
|
||||
*/
|
||||
public void save() {
|
||||
synchronized (fileLock) {
|
||||
new Thread() {
|
||||
public void run() {
|
||||
write();
|
||||
}
|
||||
}.start();
|
||||
try {
|
||||
fileLock.wait();
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method that actually writes the table to a file. This is called in
|
||||
* its own thread when {@link Preferences#save() save()} is called.
|
||||
*/
|
||||
private void write() {
|
||||
synchronized (lock) {
|
||||
synchronized (fileLock) {
|
||||
fileLock.notifyAll();
|
||||
}
|
||||
|
||||
File file = null;
|
||||
FileOutputStream output = null;
|
||||
try {
|
||||
file = new File(FILE_NAME);
|
||||
|
||||
if (file.exists())
|
||||
file.delete();
|
||||
|
||||
file.createNewFile();
|
||||
|
||||
output = new FileOutputStream(file);
|
||||
|
||||
output.write("[Preferences]\n".getBytes());
|
||||
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
String key = (String) keys.elementAt(i);
|
||||
String value = (String) values.get(key);
|
||||
|
||||
if (comments != null) {
|
||||
Comment comment = (Comment) comments.get(key);
|
||||
if (comment != null) {
|
||||
comment.write(output);
|
||||
}
|
||||
}
|
||||
|
||||
output.write(key.getBytes());
|
||||
output.write(VALUE_PREFIX);
|
||||
output.write(value.getBytes());
|
||||
output.write(VALUE_SUFFIX);
|
||||
}
|
||||
|
||||
if (endComment != null) {
|
||||
endComment.write(output);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
} finally {
|
||||
if (output != null) {
|
||||
try {
|
||||
output.close();
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
}
|
||||
NetworkTable.getTable(TABLE_NAME).putBoolean(SAVE_FIELD, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The internal method to read from a file. This will be called in its own
|
||||
* thread when the preferences singleton is first created.
|
||||
*/
|
||||
private void read() {
|
||||
class EndOfStreamException extends Exception {
|
||||
}
|
||||
|
||||
class Reader {
|
||||
|
||||
InputStream stream;
|
||||
|
||||
Reader(InputStream stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
public char read() throws IOException, EndOfStreamException {
|
||||
int input = stream.read();
|
||||
if (input == -1) {
|
||||
throw new EndOfStreamException();
|
||||
} else {
|
||||
// Check for carriage returns
|
||||
return input == '\r' ? '\n' : (char) input;
|
||||
}
|
||||
}
|
||||
|
||||
char readWithoutWhitespace() throws IOException, EndOfStreamException {
|
||||
while (true) {
|
||||
char value = read();
|
||||
switch (value) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
continue;
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (lock) {
|
||||
synchronized (fileLock) {
|
||||
fileLock.notifyAll();
|
||||
}
|
||||
|
||||
Comment comment = null;
|
||||
|
||||
|
||||
|
||||
File file = null;
|
||||
FileInputStream input = null;
|
||||
try {
|
||||
file = new File(FILE_NAME);
|
||||
|
||||
if (file.exists()) {
|
||||
input = new FileInputStream(file);
|
||||
Reader reader = new Reader(input);
|
||||
|
||||
StringBuffer buffer;
|
||||
|
||||
while (true) {
|
||||
char value = reader.readWithoutWhitespace();
|
||||
|
||||
if (value == '\n' || value == ';') {
|
||||
if (comment == null) {
|
||||
comment = new Comment();
|
||||
}
|
||||
|
||||
if (value == '\n') {
|
||||
comment.addBytes(NEW_LINE);
|
||||
} else {
|
||||
buffer = new StringBuffer(30);
|
||||
for (; value != '\n'; value = reader.read()) {
|
||||
buffer.append(value);
|
||||
}
|
||||
buffer.append('\n');
|
||||
comment.addBytes(buffer.toString().getBytes());
|
||||
}
|
||||
} else if (value == '[') {
|
||||
// Find the end of the section and the new line after it and throw
|
||||
// it away
|
||||
while (reader.read() != ']');
|
||||
while (reader.read() != '\n');
|
||||
} else {
|
||||
buffer = new StringBuffer(30);
|
||||
for (; value != '='; value = reader.readWithoutWhitespace()) {
|
||||
buffer.append(value);
|
||||
}
|
||||
String name = buffer.toString();
|
||||
buffer = new StringBuffer(30);
|
||||
|
||||
boolean shouldBreak = false;
|
||||
|
||||
value = reader.readWithoutWhitespace();
|
||||
if (value == '"') {
|
||||
for (value = reader.read(); value != '"'; value = reader.read()) {
|
||||
buffer.append(value);
|
||||
}
|
||||
// Clear the line
|
||||
while (reader.read() != '\n');
|
||||
} else {
|
||||
try {
|
||||
for (; value != '\n'; value = reader.readWithoutWhitespace()) {
|
||||
buffer.append(value);
|
||||
}
|
||||
} catch (EndOfStreamException e) {
|
||||
shouldBreak = true;
|
||||
}
|
||||
}
|
||||
|
||||
String result = buffer.toString();
|
||||
|
||||
keys.addElement(name);
|
||||
values.put(name, result);
|
||||
NetworkTable.getTable(TABLE_NAME).putString(name, result);
|
||||
|
||||
if (comment != null) {
|
||||
if (comments == null) {
|
||||
comments = new Hashtable();
|
||||
}
|
||||
comments.put(name, comment);
|
||||
comment = null;
|
||||
}
|
||||
|
||||
|
||||
if (shouldBreak) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
} catch (EndOfStreamException ex) {
|
||||
System.out.println("Done Reading");
|
||||
}
|
||||
|
||||
if (input != null) {
|
||||
try {
|
||||
input.close();
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
if (comment != null) {
|
||||
endComment = comment;
|
||||
}
|
||||
}
|
||||
|
||||
NetworkTable.getTable(TABLE_NAME).putBoolean(SAVE_FIELD, false);
|
||||
// TODO: Verify that this works even though it changes with subtables.
|
||||
// Should work since preferences shouldn't have subtables.
|
||||
NetworkTable.getTable(TABLE_NAME).addTableListener(new ITableListener() {
|
||||
public void valueChanged(ITable source, String key, Object value, boolean isNew) {
|
||||
if (key.equals(SAVE_FIELD)) {
|
||||
if (((Boolean) value).booleanValue()) {
|
||||
save();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
if (!ImproperPreferenceKeyException.isAcceptable(key)
|
||||
|| value.toString().indexOf('"') != -1) {
|
||||
if (values.contains(key) || keys.contains(key)) {
|
||||
values.remove(key);
|
||||
keys.removeElement(key);
|
||||
NetworkTable.getTable(TABLE_NAME).putString(key, "\"");
|
||||
}
|
||||
} else {
|
||||
if (values.put(key, value.toString()) == null) {
|
||||
keys.addElement(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A class representing some comment lines in the ini file. This is used so
|
||||
* that if a programmer ever directly modifies the ini file, then his/her
|
||||
* comments will still be there after {@link Preferences#save() save()} is
|
||||
* called.
|
||||
*/
|
||||
private static class Comment {
|
||||
|
||||
/**
|
||||
* A vector of byte arrays. Each array represents a line to write
|
||||
*/
|
||||
private Vector bytes = new Vector();
|
||||
|
||||
/**
|
||||
* Appends the given bytes to the comment.
|
||||
*
|
||||
* @param bytes the bytes to add
|
||||
*/
|
||||
private void addBytes(byte[] bytes) {
|
||||
this.bytes.addElement(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes this comment to the given stream
|
||||
*
|
||||
* @param stream the stream to write to
|
||||
* @throws IOException if the stream has a problem
|
||||
*/
|
||||
private void write(OutputStream stream) throws IOException {
|
||||
for (int i = 0; i < bytes.size(); i++) {
|
||||
stream.write((byte[]) bytes.elementAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This exception is thrown if the a value requested cannot be converted to
|
||||
* the requested type.
|
||||
*/
|
||||
public static class IncompatibleTypeException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Creates an exception with a description based on the input
|
||||
*
|
||||
* @param value the value that can not be converted
|
||||
* @param type the type that the value can not be converted to
|
||||
*/
|
||||
public IncompatibleTypeException(String value, String type) {
|
||||
super("Cannot convert \"" + value + "\" into " + type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be thrown if a string can not be used as a key in the preferences
|
||||
* file. This happens if the string contains a new line, a space, a tab, or an
|
||||
* equals sign.
|
||||
*/
|
||||
public static class ImproperPreferenceKeyException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Instantiates an exception with a descriptive message based on the input.
|
||||
*
|
||||
* @param value the illegal key
|
||||
* @param letter the specific character that made it illegal
|
||||
*/
|
||||
public ImproperPreferenceKeyException(String value, char letter) {
|
||||
super("Preference key \"" + value + "\" is not allowed to contain letter with ASCII code:"
|
||||
+ (byte) letter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the given string is ok to use as a key in the preference table.
|
||||
* If not, then a {@link ImproperPreferenceKeyException} will be thrown.
|
||||
*
|
||||
* @param value the value to test
|
||||
*/
|
||||
public static void confirmString(String value) {
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
char letter = value.charAt(i);
|
||||
switch (letter) {
|
||||
case '=':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '[':
|
||||
case ']':
|
||||
throw new ImproperPreferenceKeyException(value, letter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the given string is ok to use in the preference
|
||||
* table.
|
||||
*
|
||||
* @param value the string to check
|
||||
* @return true if the given string is ok to use in the preference table
|
||||
*/
|
||||
public static boolean isAcceptable(String value) {
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
char letter = value.charAt(i);
|
||||
switch (letter) {
|
||||
case '=':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '[':
|
||||
case ']':
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ public abstract class RobotBase {
|
||||
// TODO: See if the next line is necessary
|
||||
// Resource.RestartProgram();
|
||||
|
||||
NetworkTable.setNetworkIdentity("Robot");
|
||||
NetworkTable.setServerMode();// must be before b
|
||||
m_ds = DriverStation.getInstance();
|
||||
NetworkTable.getTable(""); // forces network tables to initialize
|
||||
|
||||
@@ -44,8 +44,9 @@ public class PrefrencesTest extends AbstractComsSetup {
|
||||
*/
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
NetworkTable.shutdown();
|
||||
try {
|
||||
File file = new File("/home/lvuser/wpilib-preferences.ini");
|
||||
File file = new File("networktables.ini");
|
||||
file.mkdirs();
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
@@ -53,12 +54,13 @@ public class PrefrencesTest extends AbstractComsSetup {
|
||||
file.createNewFile();
|
||||
OutputStream output = new FileOutputStream(file);
|
||||
output
|
||||
.write("checkedValueInt = 2\ncheckedValueDouble = .2\ncheckedValueFloat = 3.14\ncheckedValueLong = 172\ncheckedValueString =\"hello \nHow are you ?\"\ncheckedValueBoolean = false"
|
||||
.write("[NetworkTables Storage 3.0]\ndouble \"/Preferences/checkedValueInt\"=2\ndouble \"/Preferences/checkedValueDouble\"=.2\ndouble \"/Preferences/checkedValueFloat\"=3.14\ndouble \"/Preferences/checkedValueLong\"=172\nstring \"/Preferences/checkedValueString\"=\"hello \\nHow are you ?\"\nboolean \"/Preferences/checkedValueBoolean\"=false\n"
|
||||
.getBytes());
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
NetworkTable.initialize();
|
||||
|
||||
pref = Preferences.getInstance();
|
||||
prefTable = NetworkTable.getTable("Preferences");
|
||||
@@ -114,7 +116,7 @@ public class PrefrencesTest extends AbstractComsSetup {
|
||||
String networkedNumber = "networkCheckedValue";
|
||||
int networkNumberValue = 100;
|
||||
pref.putInt(networkedNumber, networkNumberValue);
|
||||
assertEquals((new Integer(networkNumberValue).toString()), prefTable.getString(networkedNumber));
|
||||
assertEquals(networkNumberValue, (int)(prefTable.getNumber(networkedNumber)));
|
||||
pref.remove(networkedNumber);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user