[ntcore] Add NetworkTable table-specific listeners (#4640)

These are similar, but not quite identical to, the NT3 NetworkTable
table listeners.

Also add table topic-only multi-subscriber to ensure functions like
getKeys() work properly regardless of other subscriptions.
This commit is contained in:
Peter Johnson
2022-11-27 19:46:34 -08:00
committed by GitHub
parent 8958b2a4da
commit ffbfc61532
7 changed files with 381 additions and 4 deletions

View File

@@ -25,6 +25,7 @@
#include "networktables/StringArrayTopic.h"
#include "networktables/StringTopic.h"
#include "ntcore.h"
#include "ntcore_cpp.h"
using namespace nt;
@@ -87,9 +88,14 @@ std::vector<std::string> NetworkTable::GetHierarchy(std::string_view key) {
NetworkTable::NetworkTable(NT_Inst inst, std::string_view path,
const private_init&)
: m_inst(inst), m_path(path) {}
: m_inst(inst),
m_path(path),
m_topicSub{::nt::SubscribeMultiple(inst, {{fmt::format("{}/", path)}},
{{PubSubOption::TopicsOnly(true)}})} {}
NetworkTable::~NetworkTable() {}
NetworkTable::~NetworkTable() {
::nt::UnsubscribeMultiple(m_topicSub);
}
NetworkTableInstance NetworkTable::GetInstance() const {
return NetworkTableInstance{m_inst};
@@ -362,3 +368,64 @@ Value NetworkTable::GetValue(std::string_view key) const {
std::string_view NetworkTable::GetPath() const {
return m_path;
}
NT_Listener NetworkTable::AddListener(int eventMask,
TableEventListener listener) {
return NetworkTableInstance{m_inst}.AddListener(
{{fmt::format("{}/", m_path)}}, eventMask,
[this, cb = std::move(listener)](const Event& event) {
std::string topicNameStr;
std::string_view topicName;
if (auto topicInfo = event.GetTopicInfo()) {
topicName = topicInfo->name;
} else if (auto valueData = event.GetValueEventData()) {
topicNameStr = Topic{valueData->topic}.GetName();
topicName = topicNameStr;
} else {
return;
}
auto relative_key = wpi::substr(topicName, m_path.size() + 1);
if (relative_key.find(PATH_SEPARATOR_CHAR) != std::string_view::npos) {
return;
}
cb(this, relative_key, event);
});
}
NT_Listener NetworkTable::AddListener(std::string_view key, int eventMask,
TableEventListener listener) {
return NetworkTableInstance{m_inst}.AddListener(
GetEntry(key), eventMask,
[this, cb = std::move(listener),
key = std::string{key}](const Event& event) { cb(this, key, event); });
}
NT_Listener NetworkTable::AddSubTableListener(SubTableListener listener) {
// The lambda needs to be copyable, but StringMap is not, so use
// a shared_ptr to it.
auto notified_tables = std::make_shared<wpi::StringMap<char>>();
return ::nt::AddListener(
m_topicSub, NT_EVENT_PUBLISH | NT_EVENT_IMMEDIATE,
[this, cb = std::move(listener), notified_tables](const Event& event) {
auto topicInfo = event.GetTopicInfo();
if (!topicInfo) {
return;
}
auto relative_key = wpi::substr(topicInfo->name, m_path.size() + 1);
auto end_sub_table = relative_key.find(PATH_SEPARATOR_CHAR);
if (end_sub_table == std::string_view::npos) {
return;
}
auto sub_table_key = relative_key.substr(0, end_sub_table);
if (notified_tables->find(sub_table_key) != notified_tables->end()) {
return;
}
notified_tables->insert(std::make_pair(sub_table_key, '\0'));
cb(this, sub_table_key, this->GetSubTable(sub_table_key));
});
}
void NetworkTable::RemoveListener(NT_Listener listener) {
NetworkTableInstance{m_inst}.RemoveListener(listener);
}

View File

@@ -122,7 +122,7 @@ NT_Listener NetworkTableInstance::AddListener(Subscriber& subscriber,
std::move(listener));
}
NT_Listener NetworkTableInstance::AddListener(NetworkTableEntry& entry,
NT_Listener NetworkTableInstance::AddListener(const NetworkTableEntry& entry,
int eventMask,
ListenerCallback listener) {
if (::nt::GetInstanceFromHandle(entry.GetHandle()) != m_handle) {

View File

@@ -48,6 +48,7 @@ class NetworkTable final {
private:
NT_Inst m_inst;
std::string m_path;
NT_MultiSubscriber m_topicSub;
mutable wpi::mutex m_mutex;
mutable wpi::StringMap<NT_Entry> m_entries;
@@ -566,6 +567,64 @@ class NetworkTable final {
* @return The path (e.g "", "/foo").
*/
std::string_view GetPath() const;
/**
* Called when an event occurs on a topic in a {@link NetworkTable}.
*
* @param table the table the topic exists in
* @param key the key associated with the topic that changed
* @param event the event
*/
using TableEventListener = std::function<void(
NetworkTable* table, std::string_view key, const Event& event)>;
/**
* Listen to topics only within this table.
*
* @param eventMask Bitmask of EventFlags values
* @param listener listener to add
* @return Listener handle
*/
NT_Listener AddListener(int eventMask, TableEventListener listener);
/**
* Listen to a single key.
*
* @param key the key name
* @param eventMask Bitmask of EventFlags values
* @param listener listener to add
* @return Listener handle
*/
NT_Listener AddListener(std::string_view key, int eventMask,
TableEventListener listener);
/**
* Called when a new table is created within a NetworkTable.
*
* @param parent the parent of the table
* @param name the name of the new table
* @param table the new table
*/
using SubTableListener =
std::function<void(NetworkTable* parent, std::string_view name,
std::shared_ptr<NetworkTable> table)>;
/**
* Listen for sub-table creation. This calls the listener once for each newly
* created sub-table. It immediately calls the listener for any existing
* sub-tables.
*
* @param listener listener to add
* @return Listener handle
*/
NT_Listener AddSubTableListener(SubTableListener listener);
/**
* Remove a listener.
*
* @param listener listener handle
*/
void RemoveListener(NT_Listener listener);
};
} // namespace nt

View File

@@ -447,7 +447,7 @@ class NetworkTableInstance final {
* @param listener Listener function
* @return Listener handle
*/
NT_Listener AddListener(NetworkTableEntry& entry, int eventMask,
NT_Listener AddListener(const NetworkTableEntry& entry, int eventMask,
ListenerCallback listener);
/**