From 8fa0e6c914db5cd38f2bec98fb9e59e239b054b9 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Thu, 23 Jul 2015 01:23:09 -0700 Subject: [PATCH] Add shims for old NetworkTable interface. This provides classes for virtual subtables using path delimiters within the global NetworkTable namespace. --- include/networktables/NetworkTable.h | 192 +++++++++++++++++++++++++++ include/tables/ITable.h | 158 ++++++++++++++++++++++ include/tables/ITableListener.h | 33 +++++ src/networktables/NetworkTable.cpp | 187 ++++++++++++++++++++++++++ 4 files changed, 570 insertions(+) create mode 100644 include/networktables/NetworkTable.h create mode 100644 include/tables/ITable.h create mode 100644 include/tables/ITableListener.h create mode 100644 src/networktables/NetworkTable.cpp diff --git a/include/networktables/NetworkTable.h b/include/networktables/NetworkTable.h new file mode 100644 index 0000000000..dbf6081137 --- /dev/null +++ b/include/networktables/NetworkTable.h @@ -0,0 +1,192 @@ +#ifndef _NETWORKTABLE_H_ +#define _NETWORKTABLE_H_ + +#include + +#include "tables/ITable.h" + +class NetworkTable : public ITable { + private: + struct private_init {}; + + std::string m_path; + std::vector> m_listeners; + + static std::string s_ip_address; + static bool s_client; + static bool s_running; + + public: + NetworkTable(llvm::StringRef path, const private_init&); + virtual ~NetworkTable(); + + /** + * The path separator for sub-tables and keys + * + */ + static const char PATH_SEPARATOR_CHAR; + + /** + * @throws IOException + */ + static void Initialize(); + static void Shutdown(); + + /** + * set that network tables should be a client + * This must be called before initalize or GetTable + */ + static void SetClientMode(); + + /** + * set that network tables should be a server + * This must be called before initalize or GetTable + */ + static void SetServerMode(); + + /** + * set the team the robot is configured for (this will set the ip address that + * network tables will connect to in client mode) + * This must be called before initalize or GetTable + * @param team the team number + */ + static void SetTeam(int team); + /** + * @param address the adress that network tables will connect to in client + * mode + */ + static void SetIPAddress(llvm::StringRef address); + + /** + * Gets the table with the specified key. If the table does not exist, a new + *table will be created.
+ * This will automatically initialize network tables if it has not been + *already + * + * @param key + * the key name + * @return the network table requested + */ + static std::shared_ptr GetTable(llvm::StringRef key); + + void AddTableListener(ITableListener* listener); + void AddTableListener(ITableListener* listener, bool immediateNotify); + void AddTableListener(llvm::StringRef key, ITableListener* listener, + bool immediateNotify); + void RemoveTableListener(ITableListener* listener); + + /** + * Returns the table at the specified key. If there is no table at the + * specified key, it will create a new table + * + * @param key + * the key name + * @return the networktable to be returned + */ + std::shared_ptr GetSubTable(llvm::StringRef key); + + /** + * Checks the table and tells if it contains the specified key + * + * @param key + * the key to be checked + */ + bool ContainsKey(llvm::StringRef key); + + bool ContainsSubTable(llvm::StringRef key); + + /** + * Maps the specified key to the specified value in this table. The key can + * not be null. The value can be retrieved by calling the get method with a + * key that is equal to the original key. + * + * @param key + * the key + * @param value + * the value + */ + void PutNumber(llvm::StringRef key, double value); + + /** + * Returns the key that the name maps to. If the key is null, it will return + * the default value + * + * @param key + * the key name + * @param defaultValue + * the default value if the key is null + * @return the key + */ + double GetNumber(llvm::StringRef key, double defaultValue); + + /** + * Maps the specified key to the specified value in this table. The key can + * not be null. The value can be retrieved by calling the get method with a + * key that is equal to the original key. + * + * @param key + * the key + * @param value + * the value + */ + void PutString(llvm::StringRef key, llvm::StringRef value); + + /** + * Returns the key that the name maps to. If the key is null, it will return + * the default value + * + * @param key + * the key name + * @param defaultValue + * the default value if the key is null + * @return the key + */ + std::string GetString(llvm::StringRef key, llvm::StringRef defaultValue); + + /** + * Maps the specified key to the specified value in this table. The key can + * not be null. The value can be retrieved by calling the get method with a + * key that is equal to the original key. + * + * @param key + * the key + * @param value + * the value + */ + void PutBoolean(llvm::StringRef key, bool value); + + /** + * Returns the key that the name maps to. If the key is null, it will return + * the default value + * + * @param key + * the key name + * @param defaultValue + * the default value if the key is null + * @return the key + */ + bool GetBoolean(llvm::StringRef key, bool defaultValue); + + /** + * Maps the specified key to the specified value in this table. The key can + * not be null. The value can be retrieved by calling the get method with a + * key that is equal to the original key. + * + * @param key the key name + * @param value the value to be put + */ + void PutValue(llvm::StringRef key, std::shared_ptr value); + + /** + * Returns the key that the name maps to. + * NOTE: If the value is a double, it will return a Double object, + * not a primitive. To get the primitive, use GetDouble + * + * @param key + * the key name + * @return the key, or nullptr if the key is not defined + */ + std::shared_ptr GetValue(llvm::StringRef key); +}; + +#endif diff --git a/include/tables/ITable.h b/include/tables/ITable.h new file mode 100644 index 0000000000..754ff12c9a --- /dev/null +++ b/include/tables/ITable.h @@ -0,0 +1,158 @@ +/* + * ITable.h + * + * Created on: Sep 19, 2012 + * Author: Mitchell Wills + */ + +#ifndef ITABLE_H_ +#define ITABLE_H_ + +#include + +#include "llvm/StringRef.h" +#include "nt_Value.h" + +class ITableListener; + +class ITable { + public: + /** + * Determines whether the given key is in this table. + * + * @param key the key to search for + * @return true if the table as a value assigned to the given key + */ + virtual bool ContainsKey(llvm::StringRef key) = 0; + + /** + * Determines whether there exists a non-empty subtable for this key + * in this table. + * + * @param key the key to search for + * @return true if there is a subtable with the key which contains at least + * one key/subtable of its own + */ + virtual bool ContainsSubTable(llvm::StringRef key) = 0; + + /** + * Gets the subtable in this table for the given name. + * + * @param key the name of the table relative to this one + * @return a sub table relative to this one + */ + virtual std::shared_ptr GetSubTable(llvm::StringRef key) = 0; + + /** + * Gets the value associated with a key as an object + * + * @param key the key of the value to look up + * @return the value associated with the given key + * @throws TableKeyNotDefinedException if there is no value associated with + * the given key + */ + virtual std::shared_ptr GetValue(llvm::StringRef key) = 0; + + /** + * Put a value in the table + * + * @param key the key to be assigned to + * @param value the value that will be assigned + * @throws IllegalArgumentException when the value is not supported by the + * table + */ + virtual void PutValue(llvm::StringRef key, + std::shared_ptr value) = 0; + + /** + * Put a number in the table + * + * @param key the key to be assigned to + * @param value the value that will be assigned + */ + virtual void PutNumber(llvm::StringRef key, double value) = 0; + + /** + * Gets the number associated with the given name. + * + * @param key the key to look up + * @param defaultValue the value to be returned if no value is found + * @return the value associated with the given key or the given default value + * if there is no value associated with the key + */ + virtual double GetNumber(llvm::StringRef key, double defaultValue) = 0; + + /** + * Put a string in the table + * + * @param key the key to be assigned to + * @param value the value that will be assigned + */ + virtual void PutString(llvm::StringRef key, llvm::StringRef value) = 0; + + /** + * Gets the string associated with the given name. + * + * @param key the key to look up + * @param defaultValue the value to be returned if no value is found + * @return the value associated with the given key or the given default value + * if there is no value associated with the key + */ + virtual std::string GetString(llvm::StringRef key, + llvm::StringRef defaultValue) = 0; + + /** + * Put a boolean in the table + * + * @param key the key to be assigned to + * @param value the value that will be assigned + */ + virtual void PutBoolean(llvm::StringRef key, bool value) = 0; + + /** + * Gets the boolean associated with the given name. + * + * @param key the key to look up + * @param defaultValue the value to be returned if no value is found + * @return the value associated with the given key or the given default value + * if there is no value associated with the key + */ + virtual bool GetBoolean(llvm::StringRef key, bool defaultValue) = 0; + + /** + * Add a listener for changes to the table + * + * @param listener the listener to add + */ + virtual void AddTableListener(ITableListener* listener) = 0; + + /** + * Add a listener for changes to the table + * + * @param listener the listener to add + * @param immediateNotify if true then this listener will be notified of all + * current entries (marked as new) + */ + virtual void AddTableListener(ITableListener* listener, + bool immediateNotify) = 0; + + /** + * Add a listener for changes to a specific key the table + * + * @param key the key to listen for + * @param listener the listener to add + * @param immediateNotify if true then this listener will be notified of all + * current entries (marked as new) + */ + virtual void AddTableListener(llvm::StringRef key, ITableListener* listener, + bool immediateNotify) = 0; + + /** + * Remove a listener from receiving table events + * + * @param listener the listener to be removed + */ + virtual void RemoveTableListener(ITableListener* listener) = 0; +}; + +#endif /* ITABLE_H_ */ diff --git a/include/tables/ITableListener.h b/include/tables/ITableListener.h new file mode 100644 index 0000000000..ee5a8040b3 --- /dev/null +++ b/include/tables/ITableListener.h @@ -0,0 +1,33 @@ +/* + * ITableListener.h + */ + +#ifndef ITABLELISTENER_H_ +#define ITABLELISTENER_H_ + +#include + +#include "tables/ITable.h" + +/** + * A listener that listens to changes in values in a {@link ITable} + */ +class ITableListener { + public: + virtual ~ITableListener() = default; + /** + * Called when a key-value pair is changed in a {@link ITable} + * WARNING: If a new key-value is put in this method value changed will + * immediatly be called which could lead to recursive code + * @param source the table the key-value pair exists in + * @param key the key associated with the value that changed + * @param value the new value + * @param isNew true if the key did not previously exist in the table, + * otherwise it is false + */ + virtual void ValueChanged(ITable* source, + const llvm::StringRef key, + std::shared_ptr value, bool isNew) = 0; +}; + +#endif /* ITABLELISTENER_H_ */ diff --git a/src/networktables/NetworkTable.cpp b/src/networktables/NetworkTable.cpp new file mode 100644 index 0000000000..2cf19bd222 --- /dev/null +++ b/src/networktables/NetworkTable.cpp @@ -0,0 +1,187 @@ +#include "networktables/NetworkTable.h" + +#include + +#include "llvm/SmallString.h" +#include "tables/ITableListener.h" +#include "ntcore.h" + +using llvm::StringRef; + +const char NetworkTable::PATH_SEPARATOR_CHAR = '/'; +std::string NetworkTable::s_ip_address; +bool NetworkTable::s_client = false; +bool NetworkTable::s_running = false; + +void NetworkTable::Initialize() { + if (s_client) + nt::StartClient(s_ip_address.c_str(), NT_DEFAULT_PORT); + else + nt::StartServer("", "", NT_DEFAULT_PORT); + s_running = true; +} + +void NetworkTable::Shutdown() { + if (s_client) + nt::StopClient(); + else + nt::StopServer(); + s_running = false; +} + +void NetworkTable::SetClientMode() { s_client = true; } + +void NetworkTable::SetServerMode() { s_client = false; } + +void NetworkTable::SetTeam(int team) { + char tmp[30]; + sprintf(tmp, "%d.%d.%d.%d\n", 10, team / 100, team % 100, 2); + SetIPAddress(tmp); +} + +void NetworkTable::SetIPAddress(StringRef address) { + s_ip_address = address; +} + +std::shared_ptr NetworkTable::GetTable(StringRef key) { + if (!s_running) Initialize(); + llvm::SmallString<128> path; + path += PATH_SEPARATOR_CHAR; + path += key; + return std::make_shared(path, private_init()); +} + +NetworkTable::NetworkTable(StringRef path, const private_init&) + : m_path(path) {} + +NetworkTable::~NetworkTable() { + for (auto& i : m_listeners) + nt::RemoveEntryListener(i.second); +} + +void NetworkTable::AddTableListener(ITableListener* listener) { + AddTableListener(listener, false); +} + +void NetworkTable::AddTableListener(ITableListener* listener, + bool immediateNotify) { + llvm::SmallString<128> path(m_path); + path += PATH_SEPARATOR_CHAR; + unsigned int id = nt::AddEntryListener( + path, + [=](unsigned int uid, StringRef name, std::shared_ptr value, + bool is_new) { listener->ValueChanged(this, name, value, is_new); }, + immediateNotify); + m_listeners.emplace_back(listener, id); +} + +void NetworkTable::AddTableListener(StringRef key, + ITableListener* listener, + bool immediateNotify) { + llvm::SmallString<128> path(m_path); + path += PATH_SEPARATOR_CHAR; + path += key; + unsigned int id = nt::AddEntryListener( + path, + [=](unsigned int uid, StringRef name, std::shared_ptr value, + bool is_new) { listener->ValueChanged(this, name, value, is_new); }, + immediateNotify); + m_listeners.emplace_back(listener, id); +} + +void NetworkTable::RemoveTableListener(ITableListener* listener) { + auto matches_begin = + std::remove_if(m_listeners.begin(), m_listeners.end(), + [=](const auto& x) { return x.first == listener; }); + + for (auto i = matches_begin; i != m_listeners.end(); ++i) + nt::RemoveEntryListener(i->second); + m_listeners.erase(matches_begin, m_listeners.end()); +} + +std::shared_ptr NetworkTable::GetSubTable(StringRef key) { + llvm::SmallString<128> path(m_path); + path += PATH_SEPARATOR_CHAR; + path += key; + return std::make_shared(path, private_init()); +} + +bool NetworkTable::ContainsKey(StringRef key) { + llvm::SmallString<128> path(m_path); + path += PATH_SEPARATOR_CHAR; + path += key; + return !nt::GetEntryValue(path); +} + +bool NetworkTable::ContainsSubTable(StringRef key) { + llvm::SmallString<128> path(m_path); + path += PATH_SEPARATOR_CHAR; + path += key; + path += PATH_SEPARATOR_CHAR; + return !nt::GetEntryInfo(path, 0).empty(); +} + +void NetworkTable::PutNumber(StringRef key, double value) { + llvm::SmallString<128> path(m_path); + path += PATH_SEPARATOR_CHAR; + path += key; + nt::SetEntryValue(path, nt::Value::MakeDouble(value)); +} + +double NetworkTable::GetNumber(StringRef key, double defaultValue) { + llvm::SmallString<128> path(m_path); + path += PATH_SEPARATOR_CHAR; + path += key; + auto value = nt::GetEntryValue(path); + if (!value || value->type() != NT_DOUBLE) + return defaultValue; + return value->GetDouble(); +} + +void NetworkTable::PutString(StringRef key, StringRef value) { + llvm::SmallString<128> path(m_path); + path += PATH_SEPARATOR_CHAR; + path += key; + nt::SetEntryValue(path, nt::Value::MakeString(value)); +} + +std::string NetworkTable::GetString(StringRef key, StringRef defaultValue) { + llvm::SmallString<128> path(m_path); + path += PATH_SEPARATOR_CHAR; + path += key; + auto value = nt::GetEntryValue(path); + if (!value || value->type() != NT_STRING) + return defaultValue; + return value->GetString(); +} + +void NetworkTable::PutBoolean(StringRef key, bool value) { + llvm::SmallString<128> path(m_path); + path += PATH_SEPARATOR_CHAR; + path += key; + nt::SetEntryValue(path, nt::Value::MakeBoolean(value)); +} + +bool NetworkTable::GetBoolean(StringRef key, bool defaultValue) { + llvm::SmallString<128> path(m_path); + path += PATH_SEPARATOR_CHAR; + path += key; + auto value = nt::GetEntryValue(path); + if (!value || value->type() != NT_BOOLEAN) + return defaultValue; + return value->GetBoolean(); +} + +void NetworkTable::PutValue(StringRef key, std::shared_ptr value) { + llvm::SmallString<128> path(m_path); + path += PATH_SEPARATOR_CHAR; + path += key; + nt::SetEntryValue(path, value); +} + +std::shared_ptr NetworkTable::GetValue(StringRef key) { + llvm::SmallString<128> path(m_path); + path += PATH_SEPARATOR_CHAR; + path += key; + return nt::GetEntryValue(path); +}