[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) {