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:
Peter Johnson
2015-08-13 23:17:19 -07:00
committed by Brad Miller (WPI)
parent f65e697107
commit f89c5e150f
67 changed files with 512 additions and 1457 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "networktables/ntcore"]
path = networktables/ntcore
url = ../ntcore

View File

@@ -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)

View File

@@ -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'
}

View File

@@ -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);
}
/**

View File

@@ -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;
}

View File

@@ -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('/')));
}
/**

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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

Submodule networktables/ntcore added at e42f9b0603

View File

@@ -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 ) )

View File

@@ -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'

View File

@@ -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
}

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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();

View File

@@ -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; }

View File

@@ -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~");
}
/**

View File

@@ -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) {}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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();

View File

@@ -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() {}

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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() {

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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");

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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})

View File

@@ -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";
}
}

View File

@@ -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")

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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])
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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);
}