mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-28 02:11:43 +00:00
[ntcore] Unify listeners (#4536)
This combines all 4 NT listener APIs (topic, value, connection, and logging) into a single unified listener API.
This commit is contained in:
@@ -70,44 +70,41 @@ static void RemapEnterKeyCallback(GLFWwindow* window, int key, int scancode,
|
||||
}
|
||||
|
||||
static void NtInitialize() {
|
||||
// update window title when connection status changes
|
||||
auto inst = nt::GetDefaultInstance();
|
||||
auto poller = nt::CreateConnectionListenerPoller(inst);
|
||||
nt::AddPolledConnectionListener(poller, true);
|
||||
auto poller = nt::CreateListenerPoller(inst);
|
||||
nt::AddPolledListener(
|
||||
poller, inst,
|
||||
NT_EVENT_CONNECTION | NT_EVENT_IMMEDIATE | NT_EVENT_LOGMESSAGE);
|
||||
gui::AddEarlyExecute([poller] {
|
||||
auto win = gui::GetSystemWindow();
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
for (auto&& event : nt::ReadConnectionListenerQueue(poller)) {
|
||||
if (event.connected) {
|
||||
glfwSetWindowTitle(
|
||||
win, fmt::format("Glass - Connected ({})", event.conn.remote_ip)
|
||||
.c_str());
|
||||
} else {
|
||||
glfwSetWindowTitle(win, "Glass - DISCONNECTED");
|
||||
for (auto&& event : nt::ReadListenerQueue(poller)) {
|
||||
if (auto connInfo = event.GetConnectionInfo()) {
|
||||
// update window title when connection status changes
|
||||
if ((event.flags & NT_EVENT_CONNECTED) != 0) {
|
||||
glfwSetWindowTitle(
|
||||
win, fmt::format("Glass - Connected ({})", connInfo->remote_ip)
|
||||
.c_str());
|
||||
} else {
|
||||
glfwSetWindowTitle(win, "Glass - DISCONNECTED");
|
||||
}
|
||||
} else if (auto msg = event.GetLogMessage()) {
|
||||
const char* level = "";
|
||||
if (msg->level >= NT_LOG_CRITICAL) {
|
||||
level = "CRITICAL: ";
|
||||
} else if (msg->level >= NT_LOG_ERROR) {
|
||||
level = "ERROR: ";
|
||||
} else if (msg->level >= NT_LOG_WARNING) {
|
||||
level = "WARNING: ";
|
||||
}
|
||||
gNetworkTablesLog.Append(fmt::format(
|
||||
"{}{} ({}:{})\n", level, msg->message, msg->filename, msg->line));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// handle NetworkTables log messages
|
||||
auto logPoller = nt::CreateLoggerPoller(inst);
|
||||
nt::AddPolledLogger(logPoller, NT_LOG_INFO, 100);
|
||||
gui::AddEarlyExecute([logPoller] {
|
||||
for (auto&& msg : nt::ReadLoggerQueue(logPoller)) {
|
||||
const char* level = "";
|
||||
if (msg.level >= NT_LOG_CRITICAL) {
|
||||
level = "CRITICAL: ";
|
||||
} else if (msg.level >= NT_LOG_ERROR) {
|
||||
level = "ERROR: ";
|
||||
} else if (msg.level >= NT_LOG_WARNING) {
|
||||
level = "WARNING: ";
|
||||
}
|
||||
gNetworkTablesLog.Append(fmt::format("{}{} ({}:{})\n", level, msg.message,
|
||||
msg.filename, msg.line));
|
||||
}
|
||||
});
|
||||
|
||||
gNetworkTablesLogWindow = std::make_unique<glass::Window>(
|
||||
glass::GetStorageRoot().GetChild("NetworkTables Log"),
|
||||
"NetworkTables Log", glass::Window::kHide);
|
||||
|
||||
@@ -117,58 +117,55 @@ NTField2DModel::NTField2DModel(nt::NetworkTableInstance inst,
|
||||
{{nt::PubSubOption::SendAll(true),
|
||||
nt::PubSubOption::Periodic(0.05)}}},
|
||||
m_nameTopic{inst.GetTopic(fmt::format("{}/.name", path))},
|
||||
m_topicListener{inst},
|
||||
m_valueListener{inst} {
|
||||
m_topicListener.Add(m_tableSub, NT_TOPIC_NOTIFY_PUBLISH |
|
||||
NT_TOPIC_NOTIFY_UNPUBLISH |
|
||||
NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
m_valueListener.Add(m_tableSub,
|
||||
NT_VALUE_NOTIFY_IMMEDIATE | NT_VALUE_NOTIFY_LOCAL);
|
||||
m_poller{inst} {
|
||||
m_poller.AddListener(m_tableSub, nt::EventFlags::kTopic |
|
||||
nt::EventFlags::kValueAll |
|
||||
nt::EventFlags::kImmediate);
|
||||
}
|
||||
|
||||
NTField2DModel::~NTField2DModel() = default;
|
||||
|
||||
void NTField2DModel::Update() {
|
||||
// handle publish/unpublish
|
||||
for (auto&& event : m_topicListener.ReadQueue()) {
|
||||
auto name = wpi::drop_front(event.info.name, m_path.size());
|
||||
if (name.empty() || name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
auto [it, match] = Find(event.info.name);
|
||||
if (event.flags & NT_TOPIC_NOTIFY_UNPUBLISH) {
|
||||
if (match) {
|
||||
m_objects.erase(it);
|
||||
for (auto&& event : m_poller.ReadQueue()) {
|
||||
if (auto info = event.GetTopicInfo()) {
|
||||
// handle publish/unpublish
|
||||
auto name = wpi::drop_front(info->name, m_path.size());
|
||||
if (name.empty() || name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
} else if (event.flags & NT_TOPIC_NOTIFY_PUBLISH) {
|
||||
if (!match) {
|
||||
it = m_objects.emplace(
|
||||
it, std::make_unique<ObjectModel>(
|
||||
event.info.name, nt::DoubleArrayTopic{event.info.topic}));
|
||||
auto [it, match] = Find(info->name);
|
||||
if (event.flags & nt::EventFlags::kUnpublish) {
|
||||
if (match) {
|
||||
m_objects.erase(it);
|
||||
}
|
||||
continue;
|
||||
} else if (event.flags & nt::EventFlags::kPublish) {
|
||||
if (!match) {
|
||||
it = m_objects.emplace(
|
||||
it, std::make_unique<ObjectModel>(
|
||||
info->name, nt::DoubleArrayTopic{info->topic}));
|
||||
}
|
||||
} else if (!match) {
|
||||
continue;
|
||||
}
|
||||
} else if (auto valueData = event.GetValueEventData()) {
|
||||
// update values
|
||||
// .name
|
||||
if (valueData->topic == m_nameTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsString()) {
|
||||
m_nameValue = valueData->value.GetString();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else if (!match) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// update values
|
||||
for (auto&& event : m_valueListener.ReadQueue()) {
|
||||
// .name
|
||||
if (event.topic == m_nameTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsString()) {
|
||||
m_nameValue = event.value.GetString();
|
||||
auto it =
|
||||
std::find_if(m_objects.begin(), m_objects.end(), [&](const auto& e) {
|
||||
return e->GetTopic().GetHandle() == valueData->topic;
|
||||
});
|
||||
if (it != m_objects.end()) {
|
||||
(*it)->NTUpdate(valueData->value);
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
auto it =
|
||||
std::find_if(m_objects.begin(), m_objects.end(), [&](const auto& e) {
|
||||
return e->GetTopic().GetHandle() == event.topic;
|
||||
});
|
||||
if (it != m_objects.end()) {
|
||||
(*it)->NTUpdate(event.value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,7 @@ class NTMechanismGroupImpl final {
|
||||
const char* GetName() const { return m_name.c_str(); }
|
||||
void ForEachObject(wpi::function_ref<void(MechanismObjectModel& model)> func);
|
||||
|
||||
void NTUpdate(const nt::TopicNotification& event, std::string_view name);
|
||||
void NTUpdate(const nt::ValueNotification& event, std::string_view name);
|
||||
void NTUpdate(const nt::Event& event, std::string_view name);
|
||||
|
||||
protected:
|
||||
nt::NetworkTableInstance m_inst;
|
||||
@@ -74,8 +73,7 @@ class NTMechanismObjectModel final : public MechanismObjectModel {
|
||||
frc::Rotation2d GetAngle() const final { return m_angleValue; }
|
||||
units::meter_t GetLength() const final { return m_lengthValue; }
|
||||
|
||||
bool NTUpdate(const nt::TopicNotification& event, std::string_view name);
|
||||
void NTUpdate(const nt::ValueNotification& event, std::string_view name);
|
||||
bool NTUpdate(const nt::Event& event, std::string_view name);
|
||||
|
||||
private:
|
||||
NTMechanismGroupImpl m_group;
|
||||
@@ -102,7 +100,7 @@ void NTMechanismGroupImpl::ForEachObject(
|
||||
}
|
||||
}
|
||||
|
||||
void NTMechanismGroupImpl::NTUpdate(const nt::TopicNotification& event,
|
||||
void NTMechanismGroupImpl::NTUpdate(const nt::Event& event,
|
||||
std::string_view name) {
|
||||
if (name.empty()) {
|
||||
return;
|
||||
@@ -118,83 +116,69 @@ void NTMechanismGroupImpl::NTUpdate(const nt::TopicNotification& event,
|
||||
[](const auto& e, std::string_view name) { return e->GetName() < name; });
|
||||
bool match = it != m_objects.end() && (*it)->GetName() == name;
|
||||
|
||||
if (event.flags & NT_TOPIC_NOTIFY_PUBLISH) {
|
||||
if (!match) {
|
||||
it = m_objects.emplace(
|
||||
it, std::make_unique<NTMechanismObjectModel>(
|
||||
m_inst, fmt::format("{}/{}", m_path, name), name));
|
||||
match = true;
|
||||
if (event.GetTopicInfo()) {
|
||||
if (event.flags & nt::EventFlags::kPublish) {
|
||||
if (!match) {
|
||||
it = m_objects.emplace(
|
||||
it, std::make_unique<NTMechanismObjectModel>(
|
||||
m_inst, fmt::format("{}/{}", m_path, name), name));
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
if ((*it)->NTUpdate(event, childName)) {
|
||||
m_objects.erase(it);
|
||||
if (match) {
|
||||
if ((*it)->NTUpdate(event, childName)) {
|
||||
m_objects.erase(it);
|
||||
}
|
||||
}
|
||||
} else if (event.GetValueEventData()) {
|
||||
if (match) {
|
||||
(*it)->NTUpdate(event, childName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NTMechanismGroupImpl::NTUpdate(const nt::ValueNotification& event,
|
||||
std::string_view name) {
|
||||
if (name.empty()) {
|
||||
return;
|
||||
}
|
||||
std::string_view childName;
|
||||
std::tie(name, childName) = wpi::split(name, '/');
|
||||
if (childName.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = std::lower_bound(
|
||||
m_objects.begin(), m_objects.end(), name,
|
||||
[](const auto& e, std::string_view name) { return e->GetName() < name; });
|
||||
if (it != m_objects.end() && (*it)->GetName() == name) {
|
||||
(*it)->NTUpdate(event, childName);
|
||||
}
|
||||
}
|
||||
|
||||
bool NTMechanismObjectModel::NTUpdate(const nt::TopicNotification& event,
|
||||
bool NTMechanismObjectModel::NTUpdate(const nt::Event& event,
|
||||
std::string_view childName) {
|
||||
if (event.info.topic == m_typeTopic.GetHandle()) {
|
||||
if (event.flags & NT_TOPIC_NOTIFY_UNPUBLISH) {
|
||||
return true;
|
||||
if (auto info = event.GetTopicInfo()) {
|
||||
if (info->topic == m_typeTopic.GetHandle()) {
|
||||
if (event.flags & nt::EventFlags::kUnpublish) {
|
||||
return true;
|
||||
}
|
||||
} else if (info->topic != m_colorTopic.GetHandle() &&
|
||||
info->topic != m_weightTopic.GetHandle() &&
|
||||
info->topic != m_angleTopic.GetHandle() &&
|
||||
info->topic != m_lengthTopic.GetHandle()) {
|
||||
m_group.NTUpdate(event, childName);
|
||||
}
|
||||
} else if (auto valueData = event.GetValueEventData()) {
|
||||
if (valueData->topic == m_typeTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsString()) {
|
||||
m_typeValue = valueData->value.GetString();
|
||||
}
|
||||
} else if (valueData->topic == m_colorTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsString()) {
|
||||
ConvertColor(valueData->value.GetString(), &m_colorValue);
|
||||
}
|
||||
} else if (valueData->topic == m_weightTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsDouble()) {
|
||||
m_weightValue = valueData->value.GetDouble();
|
||||
}
|
||||
} else if (valueData->topic == m_angleTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsDouble()) {
|
||||
m_angleValue = units::degree_t{valueData->value.GetDouble()};
|
||||
}
|
||||
} else if (valueData->topic == m_lengthTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsDouble()) {
|
||||
m_lengthValue = units::meter_t{valueData->value.GetDouble()};
|
||||
}
|
||||
} else {
|
||||
m_group.NTUpdate(event, childName);
|
||||
}
|
||||
} else if (event.info.topic != m_colorTopic.GetHandle() &&
|
||||
event.info.topic != m_weightTopic.GetHandle() &&
|
||||
event.info.topic != m_angleTopic.GetHandle() &&
|
||||
event.info.topic != m_lengthTopic.GetHandle()) {
|
||||
m_group.NTUpdate(event, childName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NTMechanismObjectModel::NTUpdate(const nt::ValueNotification& event,
|
||||
std::string_view childName) {
|
||||
if (event.topic == m_typeTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsString()) {
|
||||
m_typeValue = event.value.GetString();
|
||||
}
|
||||
} else if (event.topic == m_colorTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsString()) {
|
||||
ConvertColor(event.value.GetString(), &m_colorValue);
|
||||
}
|
||||
} else if (event.topic == m_weightTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsDouble()) {
|
||||
m_weightValue = event.value.GetDouble();
|
||||
}
|
||||
} else if (event.topic == m_angleTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsDouble()) {
|
||||
m_angleValue = units::degree_t{event.value.GetDouble()};
|
||||
}
|
||||
} else if (event.topic == m_lengthTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsDouble()) {
|
||||
m_lengthValue = units::meter_t{event.value.GetDouble()};
|
||||
}
|
||||
} else {
|
||||
m_group.NTUpdate(event, childName);
|
||||
}
|
||||
}
|
||||
|
||||
class NTMechanism2DModel::RootModel final : public MechanismRootModel {
|
||||
public:
|
||||
RootModel(nt::NetworkTableInstance inst, std::string_view path,
|
||||
@@ -209,8 +193,7 @@ class NTMechanism2DModel::RootModel final : public MechanismRootModel {
|
||||
m_group.ForEachObject(func);
|
||||
}
|
||||
|
||||
bool NTUpdate(const nt::TopicNotification& event, std::string_view childName);
|
||||
void NTUpdate(const nt::ValueNotification& event, std::string_view childName);
|
||||
bool NTUpdate(const nt::Event& event, std::string_view childName);
|
||||
|
||||
frc::Translation2d GetPosition() const override { return m_pos; };
|
||||
|
||||
@@ -221,36 +204,35 @@ class NTMechanism2DModel::RootModel final : public MechanismRootModel {
|
||||
frc::Translation2d m_pos;
|
||||
};
|
||||
|
||||
bool NTMechanism2DModel::RootModel::NTUpdate(const nt::TopicNotification& event,
|
||||
bool NTMechanism2DModel::RootModel::NTUpdate(const nt::Event& event,
|
||||
std::string_view childName) {
|
||||
if (event.info.topic == m_xTopic.GetHandle() ||
|
||||
event.info.topic == m_yTopic.GetHandle()) {
|
||||
if (event.flags & NT_TOPIC_NOTIFY_UNPUBLISH) {
|
||||
return true;
|
||||
if (auto info = event.GetTopicInfo()) {
|
||||
if (info->topic == m_xTopic.GetHandle() ||
|
||||
info->topic == m_yTopic.GetHandle()) {
|
||||
if (event.flags & nt::EventFlags::kUnpublish) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
m_group.NTUpdate(event, childName);
|
||||
}
|
||||
} else if (auto valueData = event.GetValueEventData()) {
|
||||
if (valueData->topic == m_xTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsDouble()) {
|
||||
m_pos = frc::Translation2d{units::meter_t{valueData->value.GetDouble()},
|
||||
m_pos.Y()};
|
||||
}
|
||||
} else if (valueData->topic == m_yTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsDouble()) {
|
||||
m_pos = frc::Translation2d{
|
||||
m_pos.X(), units::meter_t{valueData->value.GetDouble()}};
|
||||
}
|
||||
} else {
|
||||
m_group.NTUpdate(event, childName);
|
||||
}
|
||||
} else {
|
||||
m_group.NTUpdate(event, childName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NTMechanism2DModel::RootModel::NTUpdate(const nt::ValueNotification& event,
|
||||
std::string_view childName) {
|
||||
if (event.topic == m_xTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsDouble()) {
|
||||
m_pos = frc::Translation2d{units::meter_t{event.value.GetDouble()},
|
||||
m_pos.Y()};
|
||||
}
|
||||
} else if (event.topic == m_yTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsDouble()) {
|
||||
m_pos = frc::Translation2d{m_pos.X(),
|
||||
units::meter_t{event.value.GetDouble()}};
|
||||
}
|
||||
} else {
|
||||
m_group.NTUpdate(event, childName);
|
||||
}
|
||||
}
|
||||
|
||||
NTMechanism2DModel::NTMechanism2DModel(std::string_view path)
|
||||
: NTMechanism2DModel{nt::NetworkTableInstance::GetDefault(), path} {}
|
||||
|
||||
@@ -265,75 +247,72 @@ NTMechanism2DModel::NTMechanism2DModel(nt::NetworkTableInstance inst,
|
||||
m_nameTopic{m_inst.GetTopic(fmt::format("{}/.name", path))},
|
||||
m_dimensionsTopic{m_inst.GetTopic(fmt::format("{}/dims", path))},
|
||||
m_bgColorTopic{m_inst.GetTopic(fmt::format("{}/backgroundColor", path))},
|
||||
m_topicListener{m_inst},
|
||||
m_valueListener{m_inst},
|
||||
m_poller{m_inst},
|
||||
m_dimensionsValue{1_m, 1_m} {
|
||||
m_topicListener.Add(m_tableSub, NT_TOPIC_NOTIFY_PUBLISH |
|
||||
NT_TOPIC_NOTIFY_UNPUBLISH |
|
||||
NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
m_valueListener.Add(m_tableSub,
|
||||
NT_VALUE_NOTIFY_IMMEDIATE | NT_VALUE_NOTIFY_LOCAL);
|
||||
m_poller.AddListener(m_tableSub, nt::EventFlags::kTopic |
|
||||
nt::EventFlags::kValueAll |
|
||||
nt::EventFlags::kImmediate);
|
||||
}
|
||||
|
||||
NTMechanism2DModel::~NTMechanism2DModel() = default;
|
||||
|
||||
void NTMechanism2DModel::Update() {
|
||||
for (auto&& event : m_topicListener.ReadQueue()) {
|
||||
auto name = wpi::drop_front(event.info.name, m_path.size());
|
||||
if (name.empty() || name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
std::string_view childName;
|
||||
std::tie(name, childName) = wpi::split(name, '/');
|
||||
if (childName.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto it = std::lower_bound(m_roots.begin(), m_roots.end(), name,
|
||||
[](const auto& e, std::string_view name) {
|
||||
return e->GetName() < name;
|
||||
});
|
||||
bool match = it != m_roots.end() && (*it)->GetName() == name;
|
||||
|
||||
if (event.flags & NT_TOPIC_NOTIFY_PUBLISH) {
|
||||
if (!match) {
|
||||
it = m_roots.emplace(
|
||||
it, std::make_unique<RootModel>(
|
||||
m_inst, fmt::format("{}{}", m_path, name), name));
|
||||
match = true;
|
||||
for (auto&& event : m_poller.ReadQueue()) {
|
||||
if (auto info = event.GetTopicInfo()) {
|
||||
auto name = wpi::drop_front(info->name, m_path.size());
|
||||
if (name.empty() || name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
if ((*it)->NTUpdate(event, childName)) {
|
||||
m_roots.erase(it);
|
||||
std::string_view childName;
|
||||
std::tie(name, childName) = wpi::split(name, '/');
|
||||
if (childName.empty()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto&& event : m_valueListener.ReadQueue()) {
|
||||
// .name
|
||||
if (event.topic == m_nameTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsString()) {
|
||||
m_nameValue = event.value.GetString();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
auto it = std::lower_bound(m_roots.begin(), m_roots.end(), name,
|
||||
[](const auto& e, std::string_view name) {
|
||||
return e->GetName() < name;
|
||||
});
|
||||
bool match = it != m_roots.end() && (*it)->GetName() == name;
|
||||
|
||||
// dims
|
||||
if (event.topic == m_dimensionsTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsDoubleArray()) {
|
||||
auto arr = event.value.GetDoubleArray();
|
||||
if (arr.size() == 2) {
|
||||
m_dimensionsValue = frc::Translation2d{units::meter_t{arr[0]},
|
||||
units::meter_t{arr[1]}};
|
||||
if (event.flags & nt::EventFlags::kPublish) {
|
||||
if (!match) {
|
||||
it = m_roots.emplace(
|
||||
it, std::make_unique<RootModel>(
|
||||
m_inst, fmt::format("{}{}", m_path, name), name));
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
if ((*it)->NTUpdate(event, childName)) {
|
||||
m_roots.erase(it);
|
||||
}
|
||||
}
|
||||
} else if (auto valueData = event.GetValueEventData()) {
|
||||
// .name
|
||||
if (valueData->topic == m_nameTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsString()) {
|
||||
m_nameValue = valueData->value.GetString();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// backgroundColor
|
||||
if (event.topic == m_bgColorTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsString()) {
|
||||
ConvertColor(event.value.GetString(), &m_bgColorValue);
|
||||
// dims
|
||||
if (valueData->topic == m_dimensionsTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsDoubleArray()) {
|
||||
auto arr = valueData->value.GetDoubleArray();
|
||||
if (arr.size() == 2) {
|
||||
m_dimensionsValue = frc::Translation2d{units::meter_t{arr[0]},
|
||||
units::meter_t{arr[1]}};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// backgroundColor
|
||||
if (valueData->topic == m_bgColorTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsString()) {
|
||||
ConvertColor(valueData->value.GetString(), &m_bgColorValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,15 +110,10 @@ NetworkTablesModel::NetworkTablesModel()
|
||||
: NetworkTablesModel{nt::NetworkTableInstance::GetDefault()} {}
|
||||
|
||||
NetworkTablesModel::NetworkTablesModel(nt::NetworkTableInstance inst)
|
||||
: m_inst{inst},
|
||||
m_subscriber{inst, {{"", "$"}}},
|
||||
m_topicPoller{inst},
|
||||
m_valuePoller{inst} {
|
||||
m_topicPoller.Add(m_subscriber,
|
||||
NT_TOPIC_NOTIFY_IMMEDIATE | NT_TOPIC_NOTIFY_PROPERTIES |
|
||||
NT_TOPIC_NOTIFY_PUBLISH | NT_TOPIC_NOTIFY_UNPUBLISH);
|
||||
m_valuePoller.Add(m_subscriber,
|
||||
NT_VALUE_NOTIFY_IMMEDIATE | NT_VALUE_NOTIFY_LOCAL);
|
||||
: m_inst{inst}, m_poller{inst} {
|
||||
m_poller.AddListener({{"", "$"}}, nt::EventFlags::kTopic |
|
||||
nt::EventFlags::kValueAll |
|
||||
nt::EventFlags::kImmediate);
|
||||
}
|
||||
|
||||
NetworkTablesModel::Entry::~Entry() {
|
||||
@@ -426,56 +421,57 @@ void NetworkTablesModel::ValueSource::UpdateFromValue(
|
||||
|
||||
void NetworkTablesModel::Update() {
|
||||
bool updateTree = false;
|
||||
for (auto&& event : m_topicPoller.ReadQueue()) {
|
||||
auto& entry = m_entries[event.info.topic];
|
||||
if (event.flags & NT_TOPIC_NOTIFY_PUBLISH) {
|
||||
if (!entry) {
|
||||
entry = std::make_unique<Entry>();
|
||||
m_sortedEntries.emplace_back(entry.get());
|
||||
for (auto&& event : m_poller.ReadQueue()) {
|
||||
if (auto info = event.GetTopicInfo()) {
|
||||
auto& entry = m_entries[info->topic];
|
||||
if (event.flags & nt::EventFlags::kPublish) {
|
||||
if (!entry) {
|
||||
entry = std::make_unique<Entry>();
|
||||
m_sortedEntries.emplace_back(entry.get());
|
||||
updateTree = true;
|
||||
}
|
||||
}
|
||||
if (event.flags & nt::EventFlags::kUnpublish) {
|
||||
auto it = std::find(m_sortedEntries.begin(), m_sortedEntries.end(),
|
||||
entry.get());
|
||||
// will be removed completely below
|
||||
if (it != m_sortedEntries.end()) {
|
||||
*it = nullptr;
|
||||
}
|
||||
m_entries.erase(info->topic);
|
||||
updateTree = true;
|
||||
continue;
|
||||
}
|
||||
if (event.flags & nt::EventFlags::kProperties) {
|
||||
updateTree = true;
|
||||
}
|
||||
}
|
||||
if (event.flags & NT_TOPIC_NOTIFY_UNPUBLISH) {
|
||||
auto it = std::find(m_sortedEntries.begin(), m_sortedEntries.end(),
|
||||
entry.get());
|
||||
// will be removed completely below
|
||||
if (it != m_sortedEntries.end()) {
|
||||
*it = nullptr;
|
||||
if (entry) {
|
||||
entry->UpdateTopic(std::move(event));
|
||||
}
|
||||
m_entries.erase(event.info.topic);
|
||||
updateTree = true;
|
||||
continue;
|
||||
}
|
||||
if (event.flags & NT_TOPIC_NOTIFY_PROPERTIES) {
|
||||
updateTree = true;
|
||||
}
|
||||
if (entry) {
|
||||
entry->UpdateTopic(std::move(event));
|
||||
}
|
||||
}
|
||||
for (auto&& event : m_valuePoller.ReadQueue()) {
|
||||
auto& entry = m_entries[event.topic];
|
||||
if (entry) {
|
||||
entry->UpdateFromValue(std::move(event.value), entry->info.name,
|
||||
entry->info.type_str);
|
||||
if (wpi::starts_with(entry->info.name, '$') && entry->value.IsRaw() &&
|
||||
entry->info.type_str == "msgpack") {
|
||||
// meta topic handling
|
||||
if (entry->info.name == "$clients") {
|
||||
UpdateClients(entry->value.GetRaw());
|
||||
} else if (entry->info.name == "$serverpub") {
|
||||
m_server.UpdatePublishers(entry->value.GetRaw());
|
||||
} else if (entry->info.name == "$serversub") {
|
||||
m_server.UpdateSubscribers(entry->value.GetRaw());
|
||||
} else if (wpi::starts_with(entry->info.name, "$clientpub$")) {
|
||||
auto it = m_clients.find(wpi::drop_front(entry->info.name, 11));
|
||||
if (it != m_clients.end()) {
|
||||
it->second.UpdatePublishers(entry->value.GetRaw());
|
||||
}
|
||||
} else if (wpi::starts_with(entry->info.name, "$clientsub$")) {
|
||||
auto it = m_clients.find(wpi::drop_front(entry->info.name, 11));
|
||||
if (it != m_clients.end()) {
|
||||
it->second.UpdateSubscribers(entry->value.GetRaw());
|
||||
} else if (auto valueData = event.GetValueEventData()) {
|
||||
auto& entry = m_entries[valueData->topic];
|
||||
if (entry) {
|
||||
entry->UpdateFromValue(std::move(valueData->value), entry->info.name,
|
||||
entry->info.type_str);
|
||||
if (wpi::starts_with(entry->info.name, '$') && entry->value.IsRaw() &&
|
||||
entry->info.type_str == "msgpack") {
|
||||
// meta topic handling
|
||||
if (entry->info.name == "$clients") {
|
||||
UpdateClients(entry->value.GetRaw());
|
||||
} else if (entry->info.name == "$serverpub") {
|
||||
m_server.UpdatePublishers(entry->value.GetRaw());
|
||||
} else if (entry->info.name == "$serversub") {
|
||||
m_server.UpdateSubscribers(entry->value.GetRaw());
|
||||
} else if (wpi::starts_with(entry->info.name, "$clientpub$")) {
|
||||
auto it = m_clients.find(wpi::drop_front(entry->info.name, 11));
|
||||
if (it != m_clients.end()) {
|
||||
it->second.UpdatePublishers(entry->value.GetRaw());
|
||||
}
|
||||
} else if (wpi::starts_with(entry->info.name, "$clientsub$")) {
|
||||
auto it = m_clients.find(wpi::drop_front(entry->info.name, 11));
|
||||
if (it != m_clients.end()) {
|
||||
it->second.UpdateSubscribers(entry->value.GetRaw());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,10 @@ NetworkTablesProvider::NetworkTablesProvider(Storage& storage,
|
||||
nt::NetworkTableInstance inst)
|
||||
: Provider{storage.GetChild("windows")},
|
||||
m_inst{inst},
|
||||
m_topicPoller{inst},
|
||||
m_valuePoller{inst},
|
||||
m_poller{inst},
|
||||
m_typeCache{storage.GetChild("types")} {
|
||||
storage.SetCustomApply([this] {
|
||||
m_topicListener = m_topicPoller.Add({{""}}, NT_TOPIC_NOTIFY_PUBLISH |
|
||||
NT_TOPIC_NOTIFY_UNPUBLISH |
|
||||
NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
m_listener = m_poller.AddListener({{""}}, nt::EventFlags::kTopic);
|
||||
for (auto&& childIt : m_storage.GetChildren()) {
|
||||
auto id = childIt.key();
|
||||
auto typePtr = m_typeCache.FindValue(id);
|
||||
@@ -51,8 +48,8 @@ NetworkTablesProvider::NetworkTablesProvider(Storage& storage,
|
||||
}
|
||||
});
|
||||
storage.SetCustomClear([this, &storage] {
|
||||
m_topicPoller.Remove(m_topicListener);
|
||||
m_topicListener = 0;
|
||||
m_poller.RemoveListener(m_listener);
|
||||
m_listener = 0;
|
||||
for (auto&& modelEntry : m_modelEntries) {
|
||||
modelEntry->model.reset();
|
||||
}
|
||||
@@ -102,59 +99,60 @@ void NetworkTablesProvider::DisplayMenu() {
|
||||
void NetworkTablesProvider::Update() {
|
||||
Provider::Update();
|
||||
|
||||
// add/remove entries from NT changes
|
||||
for (auto&& event : m_topicPoller.ReadQueue()) {
|
||||
// look for .type fields
|
||||
if (!wpi::ends_with(event.info.name, "/.type") ||
|
||||
event.info.type != NT_STRING || event.info.type_str != "string") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (event.flags & NT_TOPIC_NOTIFY_UNPUBLISH) {
|
||||
auto it = m_topicMap.find(event.info.topic);
|
||||
if (it != m_topicMap.end()) {
|
||||
m_valuePoller.Remove(it->second.listener);
|
||||
m_topicMap.erase(it);
|
||||
for (auto&& event : m_poller.ReadQueue()) {
|
||||
if (auto info = event.GetTopicInfo()) {
|
||||
// add/remove entries from NT changes
|
||||
// look for .type fields
|
||||
if (!wpi::ends_with(info->name, "/.type") || info->type != NT_STRING ||
|
||||
info->type_str != "string") {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto it2 = std::find_if(
|
||||
m_viewEntries.begin(), m_viewEntries.end(), [&](const auto& elem) {
|
||||
return static_cast<Entry*>(elem->modelEntry)
|
||||
->typeTopic.GetHandle() == event.info.topic;
|
||||
});
|
||||
if (it2 != m_viewEntries.end()) {
|
||||
m_viewEntries.erase(it2);
|
||||
if (event.flags & nt::EventFlags::kUnpublish) {
|
||||
auto it = m_topicMap.find(info->topic);
|
||||
if (it != m_topicMap.end()) {
|
||||
m_poller.RemoveListener(it->second.listener);
|
||||
m_topicMap.erase(it);
|
||||
}
|
||||
|
||||
auto it2 = std::find_if(
|
||||
m_viewEntries.begin(), m_viewEntries.end(), [&](const auto& elem) {
|
||||
return static_cast<Entry*>(elem->modelEntry)
|
||||
->typeTopic.GetHandle() == info->topic;
|
||||
});
|
||||
if (it2 != m_viewEntries.end()) {
|
||||
m_viewEntries.erase(it2);
|
||||
}
|
||||
} else if (event.flags & nt::EventFlags::kPublish) {
|
||||
// subscribe to it; use a subscriber so we only get string values
|
||||
SubListener sublistener;
|
||||
sublistener.subscriber = nt::StringTopic{info->topic}.Subscribe("");
|
||||
sublistener.listener = m_poller.AddListener(
|
||||
sublistener.subscriber,
|
||||
nt::EventFlags::kValueAll | nt::EventFlags::kImmediate);
|
||||
m_topicMap.try_emplace(info->topic, std::move(sublistener));
|
||||
}
|
||||
} else if (event.flags & NT_TOPIC_NOTIFY_PUBLISH) {
|
||||
// subscribe to it
|
||||
SubListener sublistener;
|
||||
sublistener.subscriber = nt::StringTopic{event.info.topic}.Subscribe("");
|
||||
sublistener.listener =
|
||||
m_valuePoller.Add(sublistener.subscriber,
|
||||
NT_VALUE_NOTIFY_LOCAL | NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
m_topicMap.try_emplace(event.info.topic, std::move(sublistener));
|
||||
} else if (auto valueData = event.GetValueEventData()) {
|
||||
// handle actual .type strings
|
||||
if (!valueData->value.IsString()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// only handle ones where we have a builder
|
||||
auto builderIt = m_typeMap.find(valueData->value.GetString());
|
||||
if (builderIt == m_typeMap.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto topicName = nt::GetTopicName(valueData->topic);
|
||||
auto tableName = wpi::drop_back(topicName, 6);
|
||||
|
||||
GetOrCreateView(builderIt->second, nt::Topic{valueData->topic},
|
||||
tableName);
|
||||
// cache the type
|
||||
m_typeCache.SetString(tableName, valueData->value.GetString());
|
||||
}
|
||||
}
|
||||
|
||||
// handle actual .type strings
|
||||
for (auto&& event : m_valuePoller.ReadQueue()) {
|
||||
if (!event.value.IsString()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// only handle ones where we have a builder
|
||||
auto builderIt = m_typeMap.find(event.value.GetString());
|
||||
if (builderIt == m_typeMap.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto topicName = nt::GetTopicName(event.topic);
|
||||
auto tableName = wpi::drop_back(topicName, 6);
|
||||
|
||||
GetOrCreateView(builderIt->second, nt::Topic{event.topic}, tableName);
|
||||
// cache the type
|
||||
m_typeCache.SetString(tableName, event.value.GetString());
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkTablesProvider::Register(std::string_view typeName,
|
||||
|
||||
@@ -12,9 +12,8 @@
|
||||
|
||||
#include <networktables/MultiSubscriber.h>
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#include <networktables/NetworkTableListener.h>
|
||||
#include <networktables/StringTopic.h>
|
||||
#include <networktables/TopicListener.h>
|
||||
#include <networktables/ValueListener.h>
|
||||
#include <ntcore_cpp.h>
|
||||
|
||||
#include "glass/other/Field2D.h"
|
||||
@@ -48,8 +47,7 @@ class NTField2DModel : public Field2DModel {
|
||||
nt::NetworkTableInstance m_inst;
|
||||
nt::MultiSubscriber m_tableSub;
|
||||
nt::StringTopic m_nameTopic;
|
||||
nt::TopicListenerPoller m_topicListener;
|
||||
nt::ValueListenerPoller m_valueListener;
|
||||
nt::NetworkTableListenerPoller m_poller;
|
||||
std::string m_nameValue;
|
||||
|
||||
class ObjectModel;
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
#include <frc/geometry/Translation2d.h>
|
||||
#include <networktables/MultiSubscriber.h>
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#include <networktables/TopicListener.h>
|
||||
#include <networktables/ValueListener.h>
|
||||
#include <networktables/NetworkTableListener.h>
|
||||
|
||||
#include "glass/other/Mechanism2D.h"
|
||||
|
||||
@@ -50,8 +49,7 @@ class NTMechanism2DModel : public Mechanism2DModel {
|
||||
nt::Topic m_nameTopic;
|
||||
nt::Topic m_dimensionsTopic;
|
||||
nt::Topic m_bgColorTopic;
|
||||
nt::TopicListenerPoller m_topicListener;
|
||||
nt::ValueListenerPoller m_valueListener;
|
||||
nt::NetworkTableListenerPoller m_poller;
|
||||
|
||||
std::string m_nameValue;
|
||||
frc::Translation2d m_dimensionsValue;
|
||||
|
||||
@@ -13,10 +13,8 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <networktables/MultiSubscriber.h>
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#include <networktables/TopicListener.h>
|
||||
#include <networktables/ValueListener.h>
|
||||
#include <networktables/NetworkTableListener.h>
|
||||
#include <ntcore_cpp.h>
|
||||
#include <wpi/DenseMap.h>
|
||||
#include <wpi/json.h>
|
||||
@@ -85,8 +83,10 @@ class NetworkTablesModel : public Model {
|
||||
Entry& operator=(const Entry&) = delete;
|
||||
~Entry();
|
||||
|
||||
void UpdateTopic(nt::TopicNotification&& event) {
|
||||
UpdateInfo(std::move(event.info));
|
||||
void UpdateTopic(nt::Event&& event) {
|
||||
if (std::holds_alternative<nt::TopicInfo>(event.data)) {
|
||||
UpdateInfo(std::get<nt::TopicInfo>(std::move(event.data)));
|
||||
}
|
||||
}
|
||||
void UpdateInfo(nt::TopicInfo&& info_);
|
||||
|
||||
@@ -180,9 +180,7 @@ class NetworkTablesModel : public Model {
|
||||
void UpdateClients(std::span<const uint8_t> data);
|
||||
|
||||
nt::NetworkTableInstance m_inst;
|
||||
nt::MultiSubscriber m_subscriber;
|
||||
nt::TopicListenerPoller m_topicPoller;
|
||||
nt::ValueListenerPoller m_valuePoller;
|
||||
nt::NetworkTableListenerPoller m_poller;
|
||||
wpi::DenseMap<NT_Topic, std::unique_ptr<Entry>> m_entries;
|
||||
|
||||
// sorted by name
|
||||
|
||||
@@ -10,10 +10,9 @@
|
||||
#include <vector>
|
||||
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#include <networktables/NetworkTableListener.h>
|
||||
#include <networktables/StringTopic.h>
|
||||
#include <networktables/Topic.h>
|
||||
#include <networktables/TopicListener.h>
|
||||
#include <networktables/ValueListener.h>
|
||||
#include <wpi/DenseMap.h>
|
||||
#include <wpi/StringMap.h>
|
||||
|
||||
@@ -79,9 +78,8 @@ class NetworkTablesProvider : private Provider<detail::NTProviderFunctions> {
|
||||
void Update() override;
|
||||
|
||||
nt::NetworkTableInstance m_inst;
|
||||
nt::TopicListenerPoller m_topicPoller;
|
||||
NT_TopicListener m_topicListener{0};
|
||||
nt::ValueListenerPoller m_valuePoller;
|
||||
nt::NetworkTableListenerPoller m_poller;
|
||||
NT_Listener m_listener{0};
|
||||
|
||||
// cached mapping from table name to type string
|
||||
Storage& m_typeCache;
|
||||
@@ -96,7 +94,7 @@ class NetworkTablesProvider : private Provider<detail::NTProviderFunctions> {
|
||||
|
||||
struct SubListener {
|
||||
nt::StringSubscriber subscriber;
|
||||
NT_ValueListener listener;
|
||||
NT_Listener listener;
|
||||
};
|
||||
|
||||
// mapping from .type topic to subscriber/listener
|
||||
|
||||
@@ -66,9 +66,7 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_owned && m_handle != 0) {
|
||||
m_connectionListener.close();
|
||||
m_topicListener.close();
|
||||
m_valueListener.close();
|
||||
m_listeners.close();
|
||||
NetworkTablesJNI.destroyInstance(m_handle);
|
||||
}
|
||||
}
|
||||
@@ -357,33 +355,84 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
* Callback Creation Functions
|
||||
*/
|
||||
|
||||
private abstract static class ListenerBase<T extends NotificationBase> implements AutoCloseable {
|
||||
protected final ReentrantLock m_lock = new ReentrantLock();
|
||||
protected final Map<Integer, Consumer<T>> m_listeners = new HashMap<>();
|
||||
private static class ListenerStorage implements AutoCloseable {
|
||||
private final ReentrantLock m_lock = new ReentrantLock();
|
||||
private final Map<Integer, Consumer<NetworkTableEvent>> m_listeners = new HashMap<>();
|
||||
private Thread m_thread;
|
||||
protected int m_poller;
|
||||
private int m_poller;
|
||||
private boolean m_waitQueue;
|
||||
private final Event m_waitQueueEvent = new Event();
|
||||
private final Condition m_waitQueueCond = m_lock.newCondition();
|
||||
protected final NetworkTableInstance m_inst;
|
||||
private final NetworkTableInstance m_inst;
|
||||
|
||||
ListenerBase(NetworkTableInstance inst) {
|
||||
ListenerStorage(NetworkTableInstance inst) {
|
||||
m_inst = inst;
|
||||
}
|
||||
|
||||
int add(String[] prefixes, int mask, Consumer<NetworkTableEvent> listener) {
|
||||
m_lock.lock();
|
||||
try {
|
||||
if (m_poller == 0) {
|
||||
m_poller = NetworkTablesJNI.createListenerPoller(m_inst.getHandle());
|
||||
startThread();
|
||||
}
|
||||
int h = NetworkTablesJNI.addListener(m_poller, prefixes, mask);
|
||||
m_listeners.put(h, listener);
|
||||
return h;
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
int add(int handle, int mask, Consumer<NetworkTableEvent> listener) {
|
||||
m_lock.lock();
|
||||
try {
|
||||
if (m_poller == 0) {
|
||||
m_poller = NetworkTablesJNI.createListenerPoller(m_inst.getHandle());
|
||||
startThread();
|
||||
}
|
||||
int h = NetworkTablesJNI.addListener(m_poller, handle, mask);
|
||||
m_listeners.put(h, listener);
|
||||
return h;
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
int addLogger(int minLevel, int maxLevel, Consumer<NetworkTableEvent> listener) {
|
||||
m_lock.lock();
|
||||
try {
|
||||
if (m_poller == 0) {
|
||||
m_poller = NetworkTablesJNI.createListenerPoller(m_inst.getHandle());
|
||||
startThread();
|
||||
}
|
||||
int h = NetworkTablesJNI.addLogger(m_poller, minLevel, maxLevel);
|
||||
m_listeners.put(h, listener);
|
||||
return h;
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void remove(int listener) {
|
||||
m_lock.lock();
|
||||
try {
|
||||
m_listeners.remove(listener);
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
NetworkTablesJNI.removeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (m_poller != 0) {
|
||||
destroyPoller();
|
||||
NetworkTablesJNI.destroyListenerPoller(m_poller);
|
||||
}
|
||||
m_poller = 0;
|
||||
}
|
||||
|
||||
protected abstract T[] readQueue();
|
||||
|
||||
protected abstract void destroyPoller();
|
||||
|
||||
protected void startThread(String name) {
|
||||
private void startThread() {
|
||||
m_thread =
|
||||
new Thread(
|
||||
() -> {
|
||||
@@ -407,8 +456,9 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
wasInterrupted = true;
|
||||
break;
|
||||
}
|
||||
for (T event : readQueue()) {
|
||||
Consumer<T> listener;
|
||||
for (NetworkTableEvent event :
|
||||
NetworkTablesJNI.readListenerQueue(m_inst, m_poller)) {
|
||||
Consumer<NetworkTableEvent> listener;
|
||||
m_lock.lock();
|
||||
try {
|
||||
listener = m_listeners.get(event.listener);
|
||||
@@ -439,14 +489,14 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
m_lock.lock();
|
||||
try {
|
||||
if (!wasInterrupted) {
|
||||
destroyPoller();
|
||||
NetworkTablesJNI.destroyListenerPoller(m_poller);
|
||||
}
|
||||
m_poller = 0;
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
},
|
||||
name);
|
||||
"NTListener");
|
||||
m_thread.setDaemon(true);
|
||||
m_thread.start();
|
||||
}
|
||||
@@ -477,48 +527,29 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ConnectionListener extends ListenerBase<ConnectionNotification> {
|
||||
ConnectionListener(NetworkTableInstance inst) {
|
||||
super(inst);
|
||||
}
|
||||
private ListenerStorage m_listeners = new ListenerStorage(this);
|
||||
|
||||
int add(boolean immediateNotify, Consumer<ConnectionNotification> listener) {
|
||||
m_lock.lock();
|
||||
try {
|
||||
if (m_poller == 0) {
|
||||
m_poller = NetworkTablesJNI.createConnectionListenerPoller(m_inst.getHandle());
|
||||
startThread("NTConnectionListener");
|
||||
}
|
||||
int h = NetworkTablesJNI.addPolledConnectionListener(m_poller, immediateNotify);
|
||||
m_listeners.put(h, listener);
|
||||
return h;
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void remove(int listener) {
|
||||
m_lock.lock();
|
||||
try {
|
||||
m_listeners.remove(listener);
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
NetworkTablesJNI.removeConnectionListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConnectionNotification[] readQueue() {
|
||||
return NetworkTablesJNI.readConnectionListenerQueue(m_inst, m_poller);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void destroyPoller() {
|
||||
NetworkTablesJNI.destroyConnectionListenerPoller(m_poller);
|
||||
}
|
||||
/**
|
||||
* Remove a connection listener.
|
||||
*
|
||||
* @param listener Listener handle to remove
|
||||
*/
|
||||
public void removeListener(int listener) {
|
||||
m_listeners.remove(listener);
|
||||
}
|
||||
|
||||
private ConnectionListener m_connectionListener = new ConnectionListener(this);
|
||||
/**
|
||||
* Wait for the listener queue to be empty. This is primarily useful for deterministic
|
||||
* testing. This blocks until either the listener queue is empty (e.g. there are no
|
||||
* more events that need to be passed along to callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a negative value to
|
||||
* block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
public boolean waitForListenerQueue(double timeout) {
|
||||
return m_listeners.waitForQueue(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a connection listener. The callback function is called asynchronously on a separate
|
||||
@@ -530,110 +561,35 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addConnectionListener(
|
||||
boolean immediateNotify, Consumer<ConnectionNotification> listener) {
|
||||
return m_connectionListener.add(immediateNotify, listener);
|
||||
boolean immediateNotify, Consumer<NetworkTableEvent> listener) {
|
||||
return m_listeners.add(m_handle,
|
||||
NetworkTableEvent.kConnection | (immediateNotify ? NetworkTableEvent.kImmediate : 0),
|
||||
listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a connection listener.
|
||||
*
|
||||
* @param listener Listener handle to remove
|
||||
*/
|
||||
public void removeConnectionListener(int listener) {
|
||||
m_connectionListener.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the connection listener queue to be empty. This is primarily useful for deterministic
|
||||
* testing. This blocks until either the connection listener queue is empty (e.g. there are no
|
||||
* more events that need to be passed along to callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a negative value to
|
||||
* block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
public boolean waitForConnectionListenerQueue(double timeout) {
|
||||
return m_connectionListener.waitForQueue(timeout);
|
||||
}
|
||||
|
||||
private static final class TopicListener extends ListenerBase<TopicNotification> {
|
||||
TopicListener(NetworkTableInstance inst) {
|
||||
super(inst);
|
||||
}
|
||||
|
||||
int add(int handle, int eventMask, Consumer<TopicNotification> listener) {
|
||||
m_lock.lock();
|
||||
try {
|
||||
if (m_poller == 0) {
|
||||
m_poller = NetworkTablesJNI.createTopicListenerPoller(m_inst.getHandle());
|
||||
startThread("NTTopicListener");
|
||||
}
|
||||
int h = NetworkTablesJNI.addPolledTopicListener(m_poller, handle, eventMask);
|
||||
m_listeners.put(h, listener);
|
||||
return h;
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
int add(String[] prefixes, int eventMask, Consumer<TopicNotification> listener) {
|
||||
m_lock.lock();
|
||||
try {
|
||||
if (m_poller == 0) {
|
||||
m_poller = NetworkTablesJNI.createTopicListenerPoller(m_inst.getHandle());
|
||||
startThread("NTTopicListener");
|
||||
}
|
||||
int h = NetworkTablesJNI.addPolledTopicListener(m_poller, prefixes, eventMask);
|
||||
m_listeners.put(h, listener);
|
||||
return h;
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void remove(int listener) {
|
||||
m_lock.lock();
|
||||
try {
|
||||
m_listeners.remove(listener);
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
NetworkTablesJNI.removeTopicListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TopicNotification[] readQueue() {
|
||||
return NetworkTablesJNI.readTopicListenerQueue(m_inst, m_poller);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void destroyPoller() {
|
||||
NetworkTablesJNI.destroyTopicListenerPoller(m_poller);
|
||||
}
|
||||
}
|
||||
|
||||
private TopicListener m_topicListener = new TopicListener(this);
|
||||
|
||||
/**
|
||||
* Add a topic listener for changes on a particular topic. The callback function is called
|
||||
* Add a listener for changes on a particular topic. The callback function is called
|
||||
* asynchronously on a separate thread, so it's important to use synchronization or atomics when
|
||||
* accessing any shared state from the callback function.
|
||||
*
|
||||
* <p>This creates a corresponding internal subscriber with the lifetime of the
|
||||
* listener.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addTopicListener(
|
||||
Topic topic, int eventMask, Consumer<TopicNotification> listener) {
|
||||
public int addListener(
|
||||
Topic topic, int eventMask, Consumer<NetworkTableEvent> listener) {
|
||||
if (topic.getInstance().getHandle() != m_handle) {
|
||||
throw new IllegalArgumentException("topic is not from this instance");
|
||||
}
|
||||
return m_topicListener.add(topic.getHandle(), eventMask, listener);
|
||||
return m_listeners.add(topic.getHandle(), eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a topic listener for topic changes on a subscriber. The callback function is called
|
||||
* Add a listener for changes on a subscriber. The callback function is called
|
||||
* asynchronously on a separate thread, so it's important to use synchronization or atomics when
|
||||
* accessing any shared state from the callback function. This does NOT keep the subscriber
|
||||
* active.
|
||||
@@ -643,16 +599,16 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addTopicListener(
|
||||
Subscriber subscriber, int eventMask, Consumer<TopicNotification> listener) {
|
||||
public int addListener(
|
||||
Subscriber subscriber, int eventMask, Consumer<NetworkTableEvent> listener) {
|
||||
if (subscriber.getTopic().getInstance().getHandle() != m_handle) {
|
||||
throw new IllegalArgumentException("subscriber is not from this instance");
|
||||
}
|
||||
return m_topicListener.add(subscriber.getHandle(), eventMask, listener);
|
||||
return m_listeners.add(subscriber.getHandle(), eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a topic listener for topic changes on a subscriber. The callback function is called
|
||||
* Add a listener for changes on a subscriber. The callback function is called
|
||||
* asynchronously on a separate thread, so it's important to use synchronization or atomics when
|
||||
* accessing any shared state from the callback function. This does NOT keep the subscriber
|
||||
* active.
|
||||
@@ -662,16 +618,16 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addTopicListener(
|
||||
MultiSubscriber subscriber, int eventMask, Consumer<TopicNotification> listener) {
|
||||
public int addListener(
|
||||
MultiSubscriber subscriber, int eventMask, Consumer<NetworkTableEvent> listener) {
|
||||
if (subscriber.getInstance().getHandle() != m_handle) {
|
||||
throw new IllegalArgumentException("subscriber is not from this instance");
|
||||
}
|
||||
return m_topicListener.add(subscriber.getHandle(), eventMask, listener);
|
||||
return m_listeners.add(subscriber.getHandle(), eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a topic listener for topic changes on an entry. The callback function is called
|
||||
* Add a listener for changes on an entry. The callback function is called
|
||||
* asynchronously on a separate thread, so it's important to use synchronization or atomics when
|
||||
* accessing any shared state from the callback function.
|
||||
*
|
||||
@@ -680,173 +636,33 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addTopicListener(
|
||||
NetworkTableEntry entry, int eventMask, Consumer<TopicNotification> listener) {
|
||||
public int addListener(
|
||||
NetworkTableEntry entry, int eventMask, Consumer<NetworkTableEvent> listener) {
|
||||
if (entry.getTopic().getInstance().getHandle() != m_handle) {
|
||||
throw new IllegalArgumentException("entry is not from this instance");
|
||||
}
|
||||
return m_topicListener.add(entry.getHandle(), eventMask, listener);
|
||||
return m_listeners.add(entry.getHandle(), eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a topic listener for changes to topics with names that start with any of the given
|
||||
* Add a listener for changes to topics with names that start with any of the given
|
||||
* prefixes. The callback function is called asynchronously on a separate thread, so it's
|
||||
* important to use synchronization or atomics when accessing any shared state from the callback
|
||||
* function.
|
||||
*
|
||||
* <p>This creates a corresponding internal subscriber with the lifetime of the
|
||||
* listener.
|
||||
*
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addTopicListener(
|
||||
public int addListener(
|
||||
String[] prefixes,
|
||||
int eventMask,
|
||||
Consumer<TopicNotification> listener) {
|
||||
return m_topicListener.add(prefixes, eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a topic listener.
|
||||
*
|
||||
* @param listener Listener handle to remove
|
||||
*/
|
||||
public void removeTopicListener(int listener) {
|
||||
m_topicListener.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the topic listener queue to be empty. This is primarily useful for deterministic
|
||||
* testing. This blocks until either the topic listener queue is empty (e.g. there are no
|
||||
* more events that need to be passed along to callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a negative value to
|
||||
* block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
public boolean waitForTopicListenerQueue(double timeout) {
|
||||
return m_topicListener.waitForQueue(timeout);
|
||||
}
|
||||
|
||||
private static final class ValueListener extends ListenerBase<ValueNotification> {
|
||||
ValueListener(NetworkTableInstance inst) {
|
||||
super(inst);
|
||||
}
|
||||
|
||||
int add(int handle, int eventMask, Consumer<ValueNotification> listener) {
|
||||
m_lock.lock();
|
||||
try {
|
||||
if (m_poller == 0) {
|
||||
m_poller = NetworkTablesJNI.createValueListenerPoller(m_inst.getHandle());
|
||||
startThread("NTValueListener");
|
||||
}
|
||||
int h = NetworkTablesJNI.addPolledValueListener(m_poller, handle, eventMask);
|
||||
m_listeners.put(h, listener);
|
||||
return h;
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void remove(int listener) {
|
||||
m_lock.lock();
|
||||
try {
|
||||
m_listeners.remove(listener);
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
NetworkTablesJNI.removeValueListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ValueNotification[] readQueue() {
|
||||
return NetworkTablesJNI.readValueListenerQueue(m_inst, m_poller);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void destroyPoller() {
|
||||
NetworkTablesJNI.destroyValueListenerPoller(m_poller);
|
||||
}
|
||||
}
|
||||
|
||||
private ValueListener m_valueListener = new ValueListener(this);
|
||||
|
||||
/**
|
||||
* Add a value listener for value changes on a subscriber. The callback function is called
|
||||
* asynchronously on a separate thread, so it's important to use synchronization or atomics when
|
||||
* accessing any shared state from the callback function. This does NOT keep the subscriber
|
||||
* active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addValueListener(
|
||||
Subscriber subscriber, int eventMask, Consumer<ValueNotification> listener) {
|
||||
if (subscriber.getTopic().getInstance().getHandle() != m_handle) {
|
||||
throw new IllegalArgumentException("subscriber is not from this instance");
|
||||
}
|
||||
return m_valueListener.add(subscriber.getHandle(), eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a value listener for value changes on a subscriber. The callback function is called
|
||||
* asynchronously on a separate thread, so it's important to use synchronization or atomics when
|
||||
* accessing any shared state from the callback function. This does NOT keep the subscriber
|
||||
* active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addValueListener(
|
||||
MultiSubscriber subscriber, int eventMask, Consumer<ValueNotification> listener) {
|
||||
if (subscriber.getInstance().getHandle() != m_handle) {
|
||||
throw new IllegalArgumentException("subscriber is not from this instance");
|
||||
}
|
||||
return m_valueListener.add(subscriber.getHandle(), eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a value listener for value changes on an entry. The callback function is called
|
||||
* asynchronously on a separate thread, so it's important to use synchronization or atomics when
|
||||
* accessing any shared state from the callback function.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addValueListener(
|
||||
NetworkTableEntry entry, int eventMask, Consumer<ValueNotification> listener) {
|
||||
if (entry.getTopic().getInstance().getHandle() != m_handle) {
|
||||
throw new IllegalArgumentException("entry is not from this instance");
|
||||
}
|
||||
return m_valueListener.add(entry.getHandle(), eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a value listener.
|
||||
*
|
||||
* @param listener Listener handle to remove
|
||||
*/
|
||||
public void removeValueListener(int listener) {
|
||||
m_valueListener.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the value listener queue to be empty. This is primarily useful for deterministic
|
||||
* testing. This blocks until either the value listener queue is empty (e.g. there are no
|
||||
* more events that need to be passed along to callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a negative value to
|
||||
* block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
public boolean waitForValueListenerQueue(double timeout) {
|
||||
return m_valueListener.waitForQueue(timeout);
|
||||
Consumer<NetworkTableEvent> listener) {
|
||||
return m_listeners.add(prefixes, eventMask, listener);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -871,8 +687,8 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops local-only operation. startServer or startClient can be called after this call to start a
|
||||
* server or client.
|
||||
* Stops local-only operation. startServer or startClient can be called after this call to start
|
||||
* a server or client.
|
||||
*/
|
||||
public void stopLocal() {
|
||||
NetworkTablesJNI.stopLocal(m_handle);
|
||||
@@ -1003,8 +819,8 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets server addresses and ports for client (without restarting client). The client will attempt
|
||||
* to connect to each server in round robin fashion.
|
||||
* Sets server addresses and ports for client (without restarting client). The client will
|
||||
* attempt to connect to each server in round robin fashion.
|
||||
*
|
||||
* @param serverNames array of server names
|
||||
* @param ports array of port numbers (0=default)
|
||||
@@ -1067,8 +883,8 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
|
||||
/**
|
||||
* Flushes all updated values immediately to the network. Note: This is rate-limited to protect
|
||||
* the network from flooding. This is primarily useful for synchronizing network updates with user
|
||||
* code.
|
||||
* the network from flooding. This is primarily useful for synchronizing network updates with
|
||||
* user code.
|
||||
*/
|
||||
public void flush() {
|
||||
NetworkTablesJNI.flush(m_handle);
|
||||
@@ -1137,98 +953,19 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
NetworkTablesJNI.stopConnectionDataLog(logger);
|
||||
}
|
||||
|
||||
private final ReentrantLock m_loggerLock = new ReentrantLock();
|
||||
private final Map<Integer, Consumer<LogMessage>> m_loggers = new HashMap<>();
|
||||
private int m_loggerPoller;
|
||||
|
||||
@SuppressWarnings("PMD.AvoidCatchingThrowable")
|
||||
private void startLogThread() {
|
||||
var loggerThread =
|
||||
new Thread(
|
||||
() -> {
|
||||
boolean wasInterrupted = false;
|
||||
while (!Thread.interrupted()) {
|
||||
try {
|
||||
WPIUtilJNI.waitForObject(m_loggerPoller);
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
// don't try to destroy poller, as its handle is likely no longer valid
|
||||
wasInterrupted = true;
|
||||
break;
|
||||
}
|
||||
LogMessage[] events = NetworkTablesJNI.readLoggerQueue(this, m_loggerPoller);
|
||||
for (LogMessage event : events) {
|
||||
Consumer<LogMessage> logger;
|
||||
m_loggerLock.lock();
|
||||
try {
|
||||
logger = m_loggers.get(event.logger);
|
||||
} finally {
|
||||
m_loggerLock.unlock();
|
||||
}
|
||||
if (logger != null) {
|
||||
try {
|
||||
logger.accept(event);
|
||||
} catch (Throwable throwable) {
|
||||
System.err.println(
|
||||
"Unhandled exception during logger callback: " + throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_loggerLock.lock();
|
||||
try {
|
||||
if (!wasInterrupted) {
|
||||
NetworkTablesJNI.destroyLoggerPoller(m_loggerPoller);
|
||||
}
|
||||
} finally {
|
||||
m_loggerLock.unlock();
|
||||
}
|
||||
},
|
||||
"NTLogger");
|
||||
loggerThread.setDaemon(true);
|
||||
loggerThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add logger callback function. By default, log messages are sent to stderr; this function sends
|
||||
* log messages with the specified levels to the provided callback function instead. The callback
|
||||
* function will only be called for log messages with level greater than or equal to minLevel and
|
||||
* less than or equal to maxLevel; messages outside this range will be silently ignored.
|
||||
*
|
||||
* @param func log callback function
|
||||
* @param minLevel minimum log level
|
||||
* @param maxLevel maximum log level
|
||||
* @return Logger handle
|
||||
* @param func callback function
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addLogger(Consumer<LogMessage> func, int minLevel, int maxLevel) {
|
||||
m_loggerLock.lock();
|
||||
try {
|
||||
if (m_loggerPoller == 0) {
|
||||
m_loggerPoller = NetworkTablesJNI.createLoggerPoller(m_handle);
|
||||
startLogThread();
|
||||
}
|
||||
int handle = NetworkTablesJNI.addPolledLogger(m_loggerPoller, minLevel, maxLevel);
|
||||
m_loggers.put(handle, func);
|
||||
return handle;
|
||||
} finally {
|
||||
m_loggerLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a logger.
|
||||
*
|
||||
* @param logger Logger handle to remove
|
||||
*/
|
||||
public void removeLogger(int logger) {
|
||||
m_loggerLock.lock();
|
||||
try {
|
||||
m_loggers.remove(logger);
|
||||
} finally {
|
||||
m_loggerLock.unlock();
|
||||
}
|
||||
NetworkTablesJNI.removeLogger(logger);
|
||||
public int addLogger(int minLevel, int maxLevel, Consumer<NetworkTableEvent> func) {
|
||||
return m_listeners.addLogger(minLevel, maxLevel, func);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -199,40 +199,18 @@ public final class NetworkTablesJNI {
|
||||
|
||||
public static native TopicInfo getTopicInfo(NetworkTableInstance inst, int topic);
|
||||
|
||||
public static native int createTopicListenerPoller(int inst);
|
||||
public static native int createListenerPoller(int inst);
|
||||
|
||||
public static native void destroyTopicListenerPoller(int poller);
|
||||
public static native void destroyListenerPoller(int poller);
|
||||
|
||||
public static native int addPolledTopicListener(int poller, String[] prefixes, int flags);
|
||||
public static native int addListener(int poller, String[] prefixes, int mask);
|
||||
|
||||
public static native int addPolledTopicListener(int poller, int handle, int flags);
|
||||
public static native int addListener(int poller, int handle, int mask);
|
||||
|
||||
public static native TopicNotification[] readTopicListenerQueue(
|
||||
public static native NetworkTableEvent[] readListenerQueue(
|
||||
NetworkTableInstance inst, int poller);
|
||||
|
||||
public static native void removeTopicListener(int topicListener);
|
||||
|
||||
public static native int createValueListenerPoller(int inst);
|
||||
|
||||
public static native void destroyValueListenerPoller(int poller);
|
||||
|
||||
public static native int addPolledValueListener(int poller, int subentry, int flags);
|
||||
|
||||
public static native ValueNotification[] readValueListenerQueue(
|
||||
NetworkTableInstance inst, int poller);
|
||||
|
||||
public static native void removeValueListener(int valueListener);
|
||||
|
||||
public static native int createConnectionListenerPoller(int inst);
|
||||
|
||||
public static native void destroyConnectionListenerPoller(int poller);
|
||||
|
||||
public static native int addPolledConnectionListener(int poller, boolean immediateNotify);
|
||||
|
||||
public static native ConnectionNotification[] readConnectionListenerQueue(
|
||||
NetworkTableInstance inst, int poller);
|
||||
|
||||
public static native void removeConnectionListener(int connListener);
|
||||
public static native void removeListener(int listener);
|
||||
|
||||
public static native int getNetworkMode(int inst);
|
||||
|
||||
@@ -287,13 +265,5 @@ public final class NetworkTablesJNI {
|
||||
|
||||
public static native void stopConnectionDataLog(int logger);
|
||||
|
||||
public static native int createLoggerPoller(int inst);
|
||||
|
||||
public static native void destroyLoggerPoller(int poller);
|
||||
|
||||
public static native int addPolledLogger(int poller, int minLevel, int maxLevel);
|
||||
|
||||
public static native LogMessage[] readLoggerQueue(NetworkTableInstance inst, int poller);
|
||||
|
||||
public static native void removeLogger(int logger);
|
||||
public static native int addLogger(int poller, int minLevel, int maxLevel);
|
||||
}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Connection listener. This calls back to a callback function when a connection change occurs. The
|
||||
* callback function is called asynchronously on a separate thread, so it's important to use
|
||||
* synchronization or atomics when accessing any shared state from the callback function.
|
||||
*/
|
||||
public final class ConnectionListener implements AutoCloseable {
|
||||
/**
|
||||
* Create a listener for connection changes.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param immediateNotify if notification should be immediately created for existing connections
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public ConnectionListener(
|
||||
NetworkTableInstance inst,
|
||||
boolean immediateNotify,
|
||||
Consumer<ConnectionNotification> listener) {
|
||||
m_inst = inst;
|
||||
m_handle = inst.addConnectionListener(immediateNotify, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
m_inst.removeConnectionListener(m_handle);
|
||||
m_handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Native handle
|
||||
*/
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the connection listener queue to be empty. This is primarily useful for deterministic
|
||||
* testing. This blocks until either the connection listener queue is empty (e.g. there are no
|
||||
* more events that need to be passed along to callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a negative value to
|
||||
* block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
public boolean waitForQueue(double timeout) {
|
||||
return m_inst.waitForConnectionListenerQueue(timeout);
|
||||
}
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
private int m_handle;
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* A connection listener. This queues connection notifications. Code using the listener must
|
||||
* periodically call readQueue() to read the notifications.
|
||||
*/
|
||||
public final class ConnectionListenerPoller implements AutoCloseable {
|
||||
/**
|
||||
* Construct a connection listener poller.
|
||||
*
|
||||
* @param inst Instance
|
||||
*/
|
||||
public ConnectionListenerPoller(NetworkTableInstance inst) {
|
||||
m_inst = inst;
|
||||
m_handle = NetworkTablesJNI.createConnectionListenerPoller(inst.getHandle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a connection listener.
|
||||
*
|
||||
* @param immediateNotify if notification should be immediately created for existing connections
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(boolean immediateNotify) {
|
||||
return NetworkTablesJNI.addPolledConnectionListener(m_handle, immediateNotify);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a connection listener.
|
||||
*
|
||||
* @param listener Listener handle
|
||||
*/
|
||||
public void remove(int listener) {
|
||||
NetworkTablesJNI.removeConnectionListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read connection notifications.
|
||||
*
|
||||
* @return Connection notifications since the previous call to readQueue()
|
||||
*/
|
||||
public ConnectionNotification[] readQueue() {
|
||||
return NetworkTablesJNI.readConnectionListenerQueue(m_inst, m_handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
NetworkTablesJNI.destroyConnectionListenerPoller(m_handle);
|
||||
}
|
||||
m_handle = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
private int m_handle;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/** NetworkTables Connection notification. */
|
||||
@SuppressWarnings("MemberName")
|
||||
public final class ConnectionNotification extends NotificationBase {
|
||||
/** True if event is due to connection being established. */
|
||||
public final boolean connected;
|
||||
|
||||
/** Connection information. */
|
||||
public final ConnectionInfo conn;
|
||||
|
||||
/**
|
||||
* Constructor. This should generally only be used internally to NetworkTables.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param listener Listener that was triggered
|
||||
* @param connected Connected if true
|
||||
* @param conn Connection information
|
||||
*/
|
||||
public ConnectionNotification(
|
||||
NetworkTableInstance inst, int listener, boolean connected, ConnectionInfo conn) {
|
||||
super(inst, listener);
|
||||
this.connected = connected;
|
||||
this.conn = conn;
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,6 @@ public final class LogMessage {
|
||||
public static final int kDebug3 = 7;
|
||||
public static final int kDebug4 = 6;
|
||||
|
||||
/** The logger that generated the message. */
|
||||
public final int logger;
|
||||
|
||||
/** Log level of the message. */
|
||||
public final int level;
|
||||
|
||||
@@ -37,26 +34,15 @@ public final class LogMessage {
|
||||
/**
|
||||
* Constructor. This should generally only be used internally to NetworkTables.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param logger Logger
|
||||
* @param level Log level
|
||||
* @param filename Filename
|
||||
* @param line Line number
|
||||
* @param message Message
|
||||
*/
|
||||
public LogMessage(
|
||||
NetworkTableInstance inst, int logger, int level, String filename, int line, String message) {
|
||||
this.m_inst = inst;
|
||||
this.logger = logger;
|
||||
public LogMessage(int level, String filename, int line, String message) {
|
||||
this.level = level;
|
||||
this.filename = filename;
|
||||
this.line = line;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
|
||||
NetworkTableInstance getInstance() {
|
||||
return m_inst;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* NetworkTables event.
|
||||
*
|
||||
* <p>Events have flags. The flags are a bitmask and must be OR'ed together when listening to an
|
||||
* event to indicate the combination of events desired to be received.
|
||||
*/
|
||||
@SuppressWarnings("MemberName")
|
||||
public final class NetworkTableEvent {
|
||||
/** No flags. */
|
||||
public static final int kNone = 0;
|
||||
|
||||
/**
|
||||
* Initial listener addition. Set this flag to receive immediate notification of matches to the
|
||||
* flag criteria.
|
||||
*/
|
||||
public static final int kImmediate = 0x01;
|
||||
|
||||
/** Client connected (on server, any client connected). */
|
||||
public static final int kConnected = 0x02;
|
||||
|
||||
/** Client disconnected (on server, any client disconnected). */
|
||||
public static final int kDisconnected = 0x04;
|
||||
|
||||
/** Any connection event (connect or disconnect). */
|
||||
public static final int kConnection = kConnected | kDisconnected;
|
||||
|
||||
/** New topic published. */
|
||||
public static final int kPublish = 0x08;
|
||||
|
||||
/** Topic unpublished. */
|
||||
public static final int kUnpublish = 0x10;
|
||||
|
||||
/** Topic properties changed. */
|
||||
public static final int kProperties = 0x20;
|
||||
|
||||
/** Any topic event (publish, unpublish, or properties changed). */
|
||||
public static final int kTopic = kPublish | kUnpublish | kProperties;
|
||||
|
||||
/** Topic value updated (via network). */
|
||||
public static final int kValueRemote = 0x40;
|
||||
|
||||
/** Topic value updated (local). */
|
||||
public static final int kValueLocal = 0x80;
|
||||
|
||||
/** Topic value updated (network or local). */
|
||||
public static final int kValueAll = kValueRemote | kValueLocal;
|
||||
|
||||
/** Log message. */
|
||||
public static final int kLogMessage = 0x100;
|
||||
|
||||
/**
|
||||
* Handle of listener that was triggered. The value returned when adding the listener can be used
|
||||
* to map this to a specific added listener.
|
||||
*/
|
||||
public final int listener;
|
||||
|
||||
/**
|
||||
* Event flags. For example, kPublish if the topic was not previously published. Also indicates
|
||||
* the data included with the event:
|
||||
*
|
||||
* <ul>
|
||||
* <li>kConnected or kDisconnected: connInfo
|
||||
* <li>kPublish, kUnpublish, or kProperties: topicInfo
|
||||
* <li>kValueRemote, kValueLocal: valueData
|
||||
* <li>kLogMessage: logMessage
|
||||
* </ul>
|
||||
*/
|
||||
public final int flags;
|
||||
|
||||
/** Connection information (for connection events). */
|
||||
public final ConnectionInfo connInfo;
|
||||
|
||||
/** Topic information (for topic events). */
|
||||
public final TopicInfo topicInfo;
|
||||
|
||||
/** Value data (for value events). */
|
||||
public final ValueEventData valueData;
|
||||
|
||||
/** Log message (for log message events). */
|
||||
public final LogMessage logMessage;
|
||||
|
||||
/**
|
||||
* Constructor. This should generally only be used internally to NetworkTables.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param listener Listener that was triggered
|
||||
* @param flags Event flags
|
||||
* @param connInfo Connection information
|
||||
* @param topicInfo Topic information
|
||||
* @param valueData Value data
|
||||
* @param logMessage Log message
|
||||
*/
|
||||
public NetworkTableEvent(
|
||||
NetworkTableInstance inst,
|
||||
int listener,
|
||||
int flags,
|
||||
ConnectionInfo connInfo,
|
||||
TopicInfo topicInfo,
|
||||
ValueEventData valueData,
|
||||
LogMessage logMessage) {
|
||||
this.m_inst = inst;
|
||||
this.listener = listener;
|
||||
this.flags = flags;
|
||||
this.connInfo = connInfo;
|
||||
this.topicInfo = topicInfo;
|
||||
this.valueData = valueData;
|
||||
this.logMessage = logMessage;
|
||||
}
|
||||
|
||||
/* Network table instance. */
|
||||
private final NetworkTableInstance m_inst;
|
||||
|
||||
/**
|
||||
* Gets the instance associated with this event.
|
||||
*
|
||||
* @return Instance
|
||||
*/
|
||||
public NetworkTableInstance getInstance() {
|
||||
return m_inst;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Event listener. This calls back to a callback function when an event matching the specified mask
|
||||
* occurs. The callback function is called asynchronously on a separate thread, so it's important to
|
||||
* use synchronization or atomics when accessing any shared state from the callback function.
|
||||
*/
|
||||
public final class NetworkTableListener implements AutoCloseable {
|
||||
/**
|
||||
* Create a listener for changes to topics with names that start with any of the given prefixes.
|
||||
* This creates a corresponding internal subscriber with the lifetime of the listener.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param eventMask Bitmask of NetworkTableEvent flags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
public static NetworkTableListener createListener(
|
||||
NetworkTableInstance inst,
|
||||
String[] prefixes,
|
||||
int eventMask,
|
||||
Consumer<NetworkTableEvent> listener) {
|
||||
return new NetworkTableListener(inst, inst.addListener(prefixes, eventMask, listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for changes on a particular topic. This creates a corresponding internal
|
||||
* subscriber with the lifetime of the listener.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param eventMask Bitmask of NetworkTableEvent flags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
public static NetworkTableListener createListener(
|
||||
Topic topic, int eventMask, Consumer<NetworkTableEvent> listener) {
|
||||
NetworkTableInstance inst = topic.getInstance();
|
||||
return new NetworkTableListener(inst, inst.addListener(topic, eventMask, listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on a subscriber. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of NetworkTableEvent flags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
public static NetworkTableListener createListener(
|
||||
Subscriber subscriber, int eventMask, Consumer<NetworkTableEvent> listener) {
|
||||
NetworkTableInstance inst = subscriber.getTopic().getInstance();
|
||||
return new NetworkTableListener(inst, inst.addListener(subscriber, eventMask, listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on a subscriber. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of NetworkTableEvent flags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
public static NetworkTableListener createListener(
|
||||
MultiSubscriber subscriber, int eventMask, Consumer<NetworkTableEvent> listener) {
|
||||
NetworkTableInstance inst = subscriber.getInstance();
|
||||
return new NetworkTableListener(inst, inst.addListener(subscriber, eventMask, listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param eventMask Bitmask of NetworkTableEvent flags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
public static NetworkTableListener createListener(
|
||||
NetworkTableEntry entry, int eventMask, Consumer<NetworkTableEvent> listener) {
|
||||
NetworkTableInstance inst = entry.getInstance();
|
||||
return new NetworkTableListener(inst, inst.addListener(entry, eventMask, listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a connection listener.
|
||||
*
|
||||
* @param inst instance
|
||||
* @param immediateNotify notify listener of all existing connections
|
||||
* @param listener listener function
|
||||
* @return Listener
|
||||
*/
|
||||
public static NetworkTableListener createConnectionListener(
|
||||
NetworkTableInstance inst, boolean immediateNotify, Consumer<NetworkTableEvent> listener) {
|
||||
return new NetworkTableListener(inst, inst.addConnectionListener(immediateNotify, listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for log messages. By default, log messages are sent to stderr; this function
|
||||
* sends log messages with the specified levels to the provided callback function instead. The
|
||||
* callback function will only be called for log messages with level greater than or equal to
|
||||
* minLevel and less than or equal to maxLevel; messages outside this range will be silently
|
||||
* ignored.
|
||||
*
|
||||
* @param inst instance
|
||||
* @param minLevel minimum log level
|
||||
* @param maxLevel maximum log level
|
||||
* @param listener listener function
|
||||
* @return Listener
|
||||
*/
|
||||
public static NetworkTableListener createLogger(
|
||||
NetworkTableInstance inst, int minLevel, int maxLevel, Consumer<NetworkTableEvent> listener) {
|
||||
return new NetworkTableListener(inst, inst.addLogger(minLevel, maxLevel, listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
m_inst.removeListener(m_handle);
|
||||
m_handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Native handle
|
||||
*/
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the topic listener queue to be empty. This is primarily useful for deterministic
|
||||
* testing. This blocks until either the topic listener queue is empty (e.g. there are no more
|
||||
* events that need to be passed along to callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a negative value to
|
||||
* block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
public boolean waitForQueue(double timeout) {
|
||||
return m_inst.waitForListenerQueue(timeout);
|
||||
}
|
||||
|
||||
private NetworkTableListener(NetworkTableInstance inst, int handle) {
|
||||
m_inst = inst;
|
||||
m_handle = handle;
|
||||
}
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
private int m_handle;
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* Topic change listener. This queues topic change events matching the specified mask. Code using
|
||||
* the listener must periodically call readQueue() to read the events.
|
||||
*/
|
||||
public final class NetworkTableListenerPoller implements AutoCloseable {
|
||||
/**
|
||||
* Construct a topic listener poller.
|
||||
*
|
||||
* @param inst Instance
|
||||
*/
|
||||
public NetworkTableListenerPoller(NetworkTableInstance inst) {
|
||||
m_inst = inst;
|
||||
m_handle = NetworkTablesJNI.createListenerPoller(inst.getHandle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to topic changes for topics with names that start with any of the given
|
||||
* prefixes. This creates a corresponding internal subscriber with the lifetime of the listener.
|
||||
*
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addListener(String[] prefixes, int eventMask) {
|
||||
return NetworkTablesJNI.addListener(m_handle, prefixes, eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to changes to a particular topic. This creates a corresponding internal
|
||||
* subscriber with the lifetime of the listener.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addListener(Topic topic, int eventMask) {
|
||||
return NetworkTablesJNI.addListener(m_handle, topic.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on a subscriber. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addListener(Subscriber subscriber, int eventMask) {
|
||||
return NetworkTablesJNI.addListener(m_handle, subscriber.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on a subscriber. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addListener(MultiSubscriber subscriber, int eventMask) {
|
||||
return NetworkTablesJNI.addListener(m_handle, subscriber.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addListener(NetworkTableEntry entry, int eventMask) {
|
||||
return NetworkTablesJNI.addListener(m_handle, entry.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a connection listener. The callback function is called asynchronously on a separate thread,
|
||||
* so it's important to use synchronization or atomics when accessing any shared state from the
|
||||
* callback function.
|
||||
*
|
||||
* @param immediateNotify notify listener of all existing connections
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addConnectionListener(boolean immediateNotify) {
|
||||
return NetworkTablesJNI.addListener(
|
||||
m_handle,
|
||||
m_inst.getHandle(),
|
||||
NetworkTableEvent.kConnection | (immediateNotify ? NetworkTableEvent.kImmediate : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add logger callback function. By default, log messages are sent to stderr; this function sends
|
||||
* log messages with the specified levels to the provided callback function instead. The callback
|
||||
* function will only be called for log messages with level greater than or equal to minLevel and
|
||||
* less than or equal to maxLevel; messages outside this range will be silently ignored.
|
||||
*
|
||||
* @param minLevel minimum log level
|
||||
* @param maxLevel maximum log level
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addLogger(int minLevel, int maxLevel) {
|
||||
return NetworkTablesJNI.addLogger(m_handle, minLevel, maxLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a listener.
|
||||
*
|
||||
* @param listener Listener handle
|
||||
*/
|
||||
public void removeListener(int listener) {
|
||||
NetworkTablesJNI.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read topic notifications.
|
||||
*
|
||||
* @return Topic notifications since the previous call to readQueue()
|
||||
*/
|
||||
public NetworkTableEvent[] readQueue() {
|
||||
return NetworkTablesJNI.readListenerQueue(m_inst, m_handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
NetworkTablesJNI.destroyListenerPoller(m_handle);
|
||||
}
|
||||
m_handle = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
private int m_handle;
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/** Base class for NetworkTables notifications. */
|
||||
@SuppressWarnings("MemberName")
|
||||
public class NotificationBase {
|
||||
/**
|
||||
* Handle of listener that was triggered. The value returned when adding the listener can be used
|
||||
* to map this to a specific added listener.
|
||||
*/
|
||||
public final int listener;
|
||||
|
||||
/**
|
||||
* Constructor. This should generally only be used internally to NetworkTables.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param listener Listener handle
|
||||
*/
|
||||
public NotificationBase(NetworkTableInstance inst, int listener) {
|
||||
this.m_inst = inst;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/* Network table instance. */
|
||||
protected final NetworkTableInstance m_inst;
|
||||
|
||||
/**
|
||||
* Gets the instance associated with this notification.
|
||||
*
|
||||
* @return Instance
|
||||
*/
|
||||
public NetworkTableInstance getInstance() {
|
||||
return m_inst;
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Topic change listener. This calls back to a callback function when a topic change matching the
|
||||
* specified mask occurs. The callback function is called asynchronously on a separate thread, so
|
||||
* it's important to use synchronization or atomics when accessing any shared state from the
|
||||
* callback function.
|
||||
*/
|
||||
public final class TopicListener implements AutoCloseable {
|
||||
/**
|
||||
* Create a listener for changes on a particular topic.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public TopicListener(Topic topic, int eventMask, Consumer<TopicNotification> listener) {
|
||||
m_inst = topic.getInstance();
|
||||
m_handle = m_inst.addTopicListener(topic, eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on a subscriber. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public TopicListener(Subscriber subscriber, int eventMask, Consumer<TopicNotification> listener) {
|
||||
m_inst = subscriber.getTopic().getInstance();
|
||||
m_handle = m_inst.addTopicListener(subscriber, eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on a subscriber. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public TopicListener(
|
||||
MultiSubscriber subscriber, int eventMask, Consumer<TopicNotification> listener) {
|
||||
m_inst = subscriber.getInstance();
|
||||
m_handle = m_inst.addTopicListener(subscriber, eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public TopicListener(
|
||||
NetworkTableEntry entry, int eventMask, Consumer<TopicNotification> listener) {
|
||||
m_inst = entry.getInstance();
|
||||
m_handle = m_inst.addTopicListener(entry, eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for changes to topics with names that start with any of the given prefixes.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public TopicListener(
|
||||
NetworkTableInstance inst,
|
||||
String[] prefixes,
|
||||
int eventMask,
|
||||
Consumer<TopicNotification> listener) {
|
||||
m_inst = inst;
|
||||
m_handle = inst.addTopicListener(prefixes, eventMask, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
m_inst.removeTopicListener(m_handle);
|
||||
m_handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Native handle
|
||||
*/
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the topic listener queue to be empty. This is primarily useful for deterministic
|
||||
* testing. This blocks until either the topic listener queue is empty (e.g. there are no more
|
||||
* events that need to be passed along to callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a negative value to
|
||||
* block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
public boolean waitForQueue(double timeout) {
|
||||
return m_inst.waitForTopicListenerQueue(timeout);
|
||||
}
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
private int m_handle;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* Flag values for use with topic listeners.
|
||||
*
|
||||
* <p>The flags are a bitmask and must be OR'ed together to indicate the combination of events
|
||||
* desired to be received.
|
||||
*
|
||||
* <p>The constants kPublish, kUnpublish, and kProperties represent different events that can occur
|
||||
* to topics.
|
||||
*/
|
||||
public enum TopicListenerFlags {
|
||||
; // no enum values
|
||||
|
||||
/**
|
||||
* Initial listener addition.
|
||||
*
|
||||
* <p>Set this flag to receive immediate notification of topics matching the flag criteria
|
||||
* (generally only useful when combined with kPublish).
|
||||
*/
|
||||
public static final int kImmediate = 0x01;
|
||||
|
||||
/**
|
||||
* Newly published topic.
|
||||
*
|
||||
* <p>Set this flag to receive a notification when a topic is initially published.
|
||||
*/
|
||||
public static final int kPublish = 0x02;
|
||||
|
||||
/**
|
||||
* Topic has no more publishers.
|
||||
*
|
||||
* <p>Set this flag to receive a notification when a topic has no more publishers.
|
||||
*/
|
||||
public static final int kUnpublish = 0x04;
|
||||
|
||||
/**
|
||||
* Topic's properties changed.
|
||||
*
|
||||
* <p>Set this flag to receive a notification when an topic's properties change.
|
||||
*/
|
||||
public static final int kProperties = 0x08;
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* Topic change listener. This queues topic change events matching the specified mask. Code using
|
||||
* the listener must periodically call readQueue() to read the events.
|
||||
*/
|
||||
public final class TopicListenerPoller implements AutoCloseable {
|
||||
/**
|
||||
* Construct a topic listener poller.
|
||||
*
|
||||
* @param inst Instance
|
||||
*/
|
||||
public TopicListenerPoller(NetworkTableInstance inst) {
|
||||
m_inst = inst;
|
||||
m_handle = NetworkTablesJNI.createTopicListenerPoller(inst.getHandle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to changes to a particular topic.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(Topic topic, int eventMask) {
|
||||
return NetworkTablesJNI.addPolledTopicListener(m_handle, topic.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on a subscriber. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(Subscriber subscriber, int eventMask) {
|
||||
return NetworkTablesJNI.addPolledTopicListener(m_handle, subscriber.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on a subscriber. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(MultiSubscriber subscriber, int eventMask) {
|
||||
return NetworkTablesJNI.addPolledTopicListener(m_handle, subscriber.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(NetworkTableEntry entry, int eventMask) {
|
||||
return NetworkTablesJNI.addPolledTopicListener(m_handle, entry.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to topic changes for topics with names that start with any of the given
|
||||
* prefixes.
|
||||
*
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(String[] prefixes, int eventMask) {
|
||||
return NetworkTablesJNI.addPolledTopicListener(m_handle, prefixes, eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a listener.
|
||||
*
|
||||
* @param listener Listener handle
|
||||
*/
|
||||
public void remove(int listener) {
|
||||
NetworkTablesJNI.removeTopicListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read topic notifications.
|
||||
*
|
||||
* @return Topic notifications since the previous call to readQueue()
|
||||
*/
|
||||
public TopicNotification[] readQueue() {
|
||||
return NetworkTablesJNI.readTopicListenerQueue(m_inst, m_handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
NetworkTablesJNI.destroyTopicListenerPoller(m_handle);
|
||||
}
|
||||
m_handle = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
private int m_handle;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/** NetworkTables topic notification. */
|
||||
@SuppressWarnings("MemberName")
|
||||
public final class TopicNotification extends NotificationBase {
|
||||
/** Topic information. */
|
||||
public final TopicInfo info;
|
||||
|
||||
/**
|
||||
* Update flags. For example, {@link TopicListenerFlags#kPublish} if the topic was not previously
|
||||
* published.
|
||||
*/
|
||||
public final int flags;
|
||||
|
||||
/**
|
||||
* Constructor. This should generally only be used internally to NetworkTables.
|
||||
*
|
||||
* @param listener Listener that was triggered
|
||||
* @param info Topic information
|
||||
* @param flags Update flags
|
||||
*/
|
||||
public TopicNotification(int listener, TopicInfo info, int flags) {
|
||||
super(info.getInstance(), listener);
|
||||
this.info = info;
|
||||
this.flags = flags;
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/** NetworkTables value notification. */
|
||||
/** NetworkTables value event data. */
|
||||
@SuppressWarnings("MemberName")
|
||||
public final class ValueNotification extends NotificationBase {
|
||||
public final class ValueEventData {
|
||||
/** Topic handle. Topic.getHandle() can be used to map this to the corresponding Topic object. */
|
||||
public final int topic;
|
||||
|
||||
@@ -19,35 +19,26 @@ public final class ValueNotification extends NotificationBase {
|
||||
/** The new value. */
|
||||
public final NetworkTableValue value;
|
||||
|
||||
/** Update flags. */
|
||||
public final int flags;
|
||||
|
||||
/**
|
||||
* Constructor. This should generally only be used internally to NetworkTables.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param listener Listener that was triggered
|
||||
* @param topic Topic handle
|
||||
* @param subentry Subscriber/entry handle
|
||||
* @param value The new value
|
||||
* @param flags Update flags
|
||||
*/
|
||||
public ValueNotification(
|
||||
NetworkTableInstance inst,
|
||||
int listener,
|
||||
int topic,
|
||||
int subentry,
|
||||
NetworkTableValue value,
|
||||
int flags) {
|
||||
super(inst, listener);
|
||||
public ValueEventData(
|
||||
NetworkTableInstance inst, int topic, int subentry, NetworkTableValue value) {
|
||||
this.m_inst = inst;
|
||||
this.topic = topic;
|
||||
this.subentry = subentry;
|
||||
this.value = value;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
/* Cached topic object. */
|
||||
Topic m_topicObject;
|
||||
private Topic m_topicObject;
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
|
||||
/**
|
||||
* Get the topic as an object.
|
||||
@@ -1,95 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Value change listener. This calls back to a callback function when a value change matching the
|
||||
* specified mask occurs. The callback function is called asynchronously on a separate thread, so
|
||||
* it's important to use synchronization or atomics when accessing any shared state from the
|
||||
* callback function.
|
||||
*/
|
||||
public final class ValueListener implements AutoCloseable {
|
||||
/**
|
||||
* Create a listener for value changes on a subscriber. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public ValueListener(Subscriber subscriber, int eventMask, Consumer<ValueNotification> listener) {
|
||||
m_inst = subscriber.getTopic().getInstance();
|
||||
m_handle = m_inst.addValueListener(subscriber, eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for value changes on a subscriber. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public ValueListener(
|
||||
MultiSubscriber subscriber, int eventMask, Consumer<ValueNotification> listener) {
|
||||
m_inst = subscriber.getInstance();
|
||||
m_handle = m_inst.addValueListener(subscriber, eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for value changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public ValueListener(
|
||||
NetworkTableEntry entry, int eventMask, Consumer<ValueNotification> listener) {
|
||||
m_inst = entry.getInstance();
|
||||
m_handle = m_inst.addValueListener(entry, eventMask, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
m_inst.removeValueListener(m_handle);
|
||||
m_handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Native handle
|
||||
*/
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the value listener queue to be empty. This is primarily useful for deterministic
|
||||
* testing. This blocks until either the value listener queue is empty (e.g. there are no more
|
||||
* events that need to be passed along to callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a negative value to
|
||||
* block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
public boolean waitForQueue(double timeout) {
|
||||
return m_inst.waitForValueListenerQueue(timeout);
|
||||
}
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
private int m_handle;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* Flag values for use with value listeners.
|
||||
*
|
||||
* <p>The flags are a bitmask and must be OR'ed together to indicate the combination of events
|
||||
* desired to be received.
|
||||
*
|
||||
* <p>By default, notifications are only generated for remote changes occurring after the listener
|
||||
* is created. The constants kImmediate and kLocal are modifiers that cause notifications to be
|
||||
* generated at other times.
|
||||
*/
|
||||
public enum ValueListenerFlags {
|
||||
; // no enum values
|
||||
|
||||
/**
|
||||
* Initial listener addition.
|
||||
*
|
||||
* <p>Set this flag to receive immediate notification of the current value.
|
||||
*/
|
||||
public static final int kImmediate = 0x01;
|
||||
|
||||
/**
|
||||
* Changed locally.
|
||||
*
|
||||
* <p>Set this flag to receive notification of both local changes and changes coming from remote
|
||||
* nodes. By default, notifications are only generated for remote changes.
|
||||
*/
|
||||
public static final int kLocal = 0x02;
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* Value change listener. This queues value change events matching the specified mask. Code using
|
||||
* the listener must periodically call readQueue() to read the events.
|
||||
*/
|
||||
public final class ValueListenerPoller implements AutoCloseable {
|
||||
/**
|
||||
* Construct a value listener poller.
|
||||
*
|
||||
* @param inst Instance
|
||||
*/
|
||||
public ValueListenerPoller(NetworkTableInstance inst) {
|
||||
m_inst = inst;
|
||||
m_handle = NetworkTablesJNI.createValueListenerPoller(inst.getHandle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to value changes on a subscriber. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(Subscriber subscriber, int eventMask) {
|
||||
return NetworkTablesJNI.addPolledValueListener(m_handle, subscriber.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to value changes on a subscriber. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(MultiSubscriber subscriber, int eventMask) {
|
||||
return NetworkTablesJNI.addPolledValueListener(m_handle, subscriber.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to value changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(NetworkTableEntry entry, int eventMask) {
|
||||
return NetworkTablesJNI.addPolledValueListener(m_handle, entry.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a listener.
|
||||
*
|
||||
* @param listener Listener handle
|
||||
*/
|
||||
public void remove(int listener) {
|
||||
NetworkTablesJNI.removeValueListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read value notifications.
|
||||
*
|
||||
* @return Value notifications since the previous call to readQueue()
|
||||
*/
|
||||
public ValueNotification[] readQueue() {
|
||||
return NetworkTablesJNI.readValueListenerQueue(m_inst, m_handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
NetworkTablesJNI.destroyValueListenerPoller(m_handle);
|
||||
}
|
||||
m_handle = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
private int m_handle;
|
||||
}
|
||||
@@ -4,191 +4,16 @@
|
||||
|
||||
#include "ConnectionList.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <optional>
|
||||
|
||||
#include <wpi/DataLog.h>
|
||||
#include <wpi/DenseMap.h>
|
||||
#include <wpi/SafeThread.h>
|
||||
#include <wpi/Synchronization.h>
|
||||
#include <wpi/UidVector.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/json_serializer.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
|
||||
#include "HandleMap.h"
|
||||
#include "IListenerStorage.h"
|
||||
#include "ntcore_c.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
namespace {
|
||||
|
||||
struct PollerData {
|
||||
static constexpr auto kType = Handle::kConnectionListenerPoller;
|
||||
|
||||
explicit PollerData(NT_ConnectionListenerPoller handle) : handle{handle} {}
|
||||
|
||||
wpi::SignalObject<NT_ConnectionListenerPoller> handle;
|
||||
std::vector<ConnectionNotification> queue;
|
||||
};
|
||||
|
||||
struct ListenerData {
|
||||
static constexpr auto kType = Handle::kConnectionListener;
|
||||
|
||||
ListenerData(NT_ConnectionListener handle, PollerData* poller)
|
||||
: handle{handle}, poller{poller} {}
|
||||
|
||||
wpi::SignalObject<NT_ConnectionListener> handle;
|
||||
PollerData* poller;
|
||||
};
|
||||
|
||||
struct DataLoggerData {
|
||||
static constexpr auto kType = Handle::kConnectionDataLogger;
|
||||
|
||||
DataLoggerData(NT_ConnectionDataLogger handle, wpi::log::DataLog& log,
|
||||
std::string_view name, int64_t time)
|
||||
: handle{handle},
|
||||
entry{log, name, "{\"schema\":\"NTConnectionInfo\",\"source\":\"NT\"}",
|
||||
"json", time} {}
|
||||
|
||||
NT_ConnectionDataLogger handle;
|
||||
wpi::log::StringLogEntry entry;
|
||||
};
|
||||
|
||||
class ListenerThread final : public wpi::SafeThreadEvent {
|
||||
public:
|
||||
explicit ListenerThread(NT_ConnectionListenerPoller poller)
|
||||
: m_poller{poller} {}
|
||||
|
||||
void Main() final;
|
||||
|
||||
NT_ConnectionListenerPoller m_poller;
|
||||
wpi::DenseMap<NT_ConnectionListener,
|
||||
std::function<void(const ConnectionNotification& event)>>
|
||||
m_callbacks;
|
||||
wpi::Event m_waitQueueWakeup;
|
||||
wpi::Event m_waitQueueWaiter;
|
||||
};
|
||||
|
||||
class CLImpl {
|
||||
public:
|
||||
explicit CLImpl(int inst) : m_inst{inst} {}
|
||||
|
||||
int m_inst;
|
||||
|
||||
// shared with user (must be atomic or mutex-protected)
|
||||
std::atomic_bool m_connected{false};
|
||||
wpi::UidVector<std::optional<ConnectionInfo>, 8> m_connections;
|
||||
|
||||
HandleMap<PollerData, 8> m_pollers;
|
||||
HandleMap<ListenerData, 8> m_listeners;
|
||||
HandleMap<DataLoggerData, 8> m_dataloggers;
|
||||
|
||||
wpi::SafeThreadOwner<ListenerThread> m_listenerThread;
|
||||
|
||||
NT_ConnectionListener AddListener(
|
||||
std::function<void(const ConnectionNotification& event)> callback,
|
||||
bool immediateNotify);
|
||||
PollerData* CreateListenerPoller() { return m_pollers.Add(m_inst); }
|
||||
void DestroyListenerPoller(NT_ConnectionListenerPoller pollerHandle);
|
||||
NT_ConnectionListener AddPolledListener(
|
||||
NT_ConnectionListenerPoller pollerHandle, bool immediateNotify);
|
||||
void RemoveListener(NT_ConnectionListener listenerHandle);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void ListenerThread::Main() {
|
||||
while (m_active) {
|
||||
WPI_Handle signaledBuf[3];
|
||||
auto signaled = wpi::WaitForObjects(
|
||||
{m_poller, m_stopEvent.GetHandle(), m_waitQueueWakeup.GetHandle()},
|
||||
signaledBuf);
|
||||
if (signaled.empty() || !m_active) {
|
||||
return;
|
||||
}
|
||||
// call all the way back out to the C++ API to ensure valid handle
|
||||
auto events = nt::ReadConnectionListenerQueue(m_poller);
|
||||
if (events.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::unique_lock lock{m_mutex};
|
||||
for (auto&& event : events) {
|
||||
auto callbackIt = m_callbacks.find(event.listener);
|
||||
if (callbackIt != m_callbacks.end()) {
|
||||
auto callback = callbackIt->second;
|
||||
lock.unlock();
|
||||
callback(event);
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
if (std::find(signaled.begin(), signaled.end(),
|
||||
m_waitQueueWakeup.GetHandle()) != signaled.end()) {
|
||||
m_waitQueueWaiter.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NT_ConnectionListener CLImpl::AddListener(
|
||||
std::function<void(const ConnectionNotification& event)> callback,
|
||||
bool immediateNotify) {
|
||||
if (!m_listenerThread) {
|
||||
m_listenerThread.Start(CreateListenerPoller()->handle);
|
||||
}
|
||||
if (auto thr = m_listenerThread.GetThread()) {
|
||||
auto listener = AddPolledListener(thr->m_poller, immediateNotify);
|
||||
if (listener) {
|
||||
thr->m_callbacks.try_emplace(listener, std::move(callback));
|
||||
}
|
||||
return listener;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void CLImpl::DestroyListenerPoller(NT_ConnectionListenerPoller pollerHandle) {
|
||||
if (auto poller = m_pollers.Remove(pollerHandle)) {
|
||||
// ensure all listeners that use this poller are removed
|
||||
wpi::SmallVector<NT_ConnectionListener, 16> toRemove;
|
||||
for (auto&& listener : m_listeners) {
|
||||
if (listener->poller == poller.get()) {
|
||||
toRemove.emplace_back(listener->handle);
|
||||
}
|
||||
}
|
||||
for (auto handle : toRemove) {
|
||||
RemoveListener(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NT_ConnectionListener CLImpl::AddPolledListener(
|
||||
NT_ConnectionListenerPoller pollerHandle, bool immediateNotify) {
|
||||
auto poller = m_pollers.Get(pollerHandle);
|
||||
if (!poller) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto listener = m_listeners.Add(m_inst, poller);
|
||||
if (immediateNotify && !m_connections.empty()) {
|
||||
for (auto&& conn : m_connections) {
|
||||
listener->poller->queue.emplace_back(listener->handle.GetHandle(), true,
|
||||
*conn);
|
||||
}
|
||||
listener->poller->handle.Set();
|
||||
listener->handle.Set();
|
||||
}
|
||||
return listener->handle;
|
||||
}
|
||||
|
||||
void CLImpl::RemoveListener(NT_ConnectionListener listenerHandle) {
|
||||
if (auto listener = m_listeners.Remove(listenerHandle)) {
|
||||
if (auto thr = m_listenerThread.GetThread()) {
|
||||
if (thr->m_poller == listener->poller->handle) {
|
||||
thr->m_callbacks.erase(listenerHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::string ConnInfoToJson(bool connected, const ConnectionInfo& info) {
|
||||
std::string str;
|
||||
wpi::raw_string_ostream os{str};
|
||||
@@ -207,50 +32,35 @@ static std::string ConnInfoToJson(bool connected, const ConnectionInfo& info) {
|
||||
return str;
|
||||
}
|
||||
|
||||
class ConnectionList::Impl : public CLImpl {
|
||||
public:
|
||||
explicit Impl(int inst) : CLImpl{inst} {}
|
||||
};
|
||||
|
||||
ConnectionList::ConnectionList(int inst)
|
||||
: m_impl{std::make_unique<Impl>(inst)} {}
|
||||
ConnectionList::ConnectionList(int inst, IListenerStorage& listenerStorage)
|
||||
: m_inst{inst}, m_listenerStorage{listenerStorage} {}
|
||||
|
||||
ConnectionList::~ConnectionList() = default;
|
||||
|
||||
int ConnectionList::AddConnection(const ConnectionInfo& info) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_impl->m_connected = true;
|
||||
for (auto&& listener : m_impl->m_listeners) {
|
||||
listener->poller->queue.emplace_back(listener->handle.GetHandle(), true,
|
||||
info);
|
||||
listener->poller->handle.Set();
|
||||
listener->handle.Set();
|
||||
}
|
||||
if (!m_impl->m_dataloggers.empty()) {
|
||||
m_connected = true;
|
||||
m_listenerStorage.Notify({}, NT_EVENT_CONNECTED, &info);
|
||||
if (!m_dataloggers.empty()) {
|
||||
auto now = Now();
|
||||
for (auto&& datalogger : m_impl->m_dataloggers) {
|
||||
for (auto&& datalogger : m_dataloggers) {
|
||||
datalogger->entry.Append(ConnInfoToJson(true, info), now);
|
||||
}
|
||||
}
|
||||
return m_impl->m_connections.emplace_back(info);
|
||||
return m_connections.emplace_back(info);
|
||||
}
|
||||
|
||||
void ConnectionList::RemoveConnection(int handle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto val = m_impl->m_connections.erase(handle);
|
||||
if (m_impl->m_connections.empty()) {
|
||||
m_impl->m_connected = false;
|
||||
auto val = m_connections.erase(handle);
|
||||
if (m_connections.empty()) {
|
||||
m_connected = false;
|
||||
}
|
||||
if (val) {
|
||||
for (auto&& listener : m_impl->m_listeners) {
|
||||
listener->poller->queue.emplace_back(listener->handle.GetHandle(), false,
|
||||
*val);
|
||||
listener->poller->handle.Set();
|
||||
listener->handle.Set();
|
||||
}
|
||||
if (!m_impl->m_dataloggers.empty()) {
|
||||
m_listenerStorage.Notify({}, NT_EVENT_DISCONNECTED, &(*val));
|
||||
if (!m_dataloggers.empty()) {
|
||||
auto now = Now();
|
||||
for (auto&& datalogger : m_impl->m_dataloggers) {
|
||||
for (auto&& datalogger : m_dataloggers) {
|
||||
datalogger->entry.Append(ConnInfoToJson(false, *val), now);
|
||||
}
|
||||
}
|
||||
@@ -259,92 +69,50 @@ void ConnectionList::RemoveConnection(int handle) {
|
||||
|
||||
void ConnectionList::ClearConnections() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_impl->m_connected = false;
|
||||
for (auto&& conn : m_impl->m_connections) {
|
||||
for (auto&& listener : m_impl->m_listeners) {
|
||||
listener->poller->queue.emplace_back(listener->handle.GetHandle(), false,
|
||||
*conn);
|
||||
listener->poller->handle.Set();
|
||||
listener->handle.Set();
|
||||
}
|
||||
m_connected = false;
|
||||
for (auto&& conn : m_connections) {
|
||||
m_listenerStorage.Notify({}, NT_EVENT_DISCONNECTED, &(*conn));
|
||||
}
|
||||
m_impl->m_connections.clear();
|
||||
m_connections.clear();
|
||||
}
|
||||
|
||||
std::vector<ConnectionInfo> ConnectionList::GetConnections() const {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
std::vector<ConnectionInfo> info;
|
||||
info.reserve(m_impl->m_connections.size());
|
||||
for (auto&& conn : m_impl->m_connections) {
|
||||
info.reserve(m_connections.size());
|
||||
for (auto&& conn : m_connections) {
|
||||
info.emplace_back(*conn);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
bool ConnectionList::IsConnected() const {
|
||||
return m_impl->m_connected;
|
||||
return m_connected;
|
||||
}
|
||||
|
||||
NT_ConnectionListener ConnectionList::AddListener(
|
||||
bool immediateNotify,
|
||||
std::function<void(const ConnectionNotification& event)> callback) {
|
||||
void ConnectionList::AddListener(NT_Listener listener, unsigned int eventMask) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return m_impl->AddListener(std::move(callback), immediateNotify);
|
||||
}
|
||||
|
||||
bool ConnectionList::WaitForListenerQueue(double timeout) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
WPI_EventHandle h;
|
||||
if (auto thr = m_impl->m_listenerThread.GetThread()) {
|
||||
h = thr->m_waitQueueWaiter.GetHandle();
|
||||
thr->m_waitQueueWakeup.Set();
|
||||
} else {
|
||||
return false;
|
||||
eventMask &= (NT_EVENT_CONNECTION | NT_EVENT_IMMEDIATE);
|
||||
m_listenerStorage.Activate(listener, eventMask);
|
||||
if ((eventMask & (NT_EVENT_CONNECTED | NT_EVENT_IMMEDIATE)) ==
|
||||
(NT_EVENT_CONNECTED | NT_EVENT_IMMEDIATE) &&
|
||||
!m_connections.empty()) {
|
||||
wpi::SmallVector<const ConnectionInfo*, 16> infos;
|
||||
infos.reserve(m_connections.size());
|
||||
for (auto&& conn : m_connections) {
|
||||
infos.emplace_back(&(*conn));
|
||||
}
|
||||
m_listenerStorage.Notify({&listener, 1},
|
||||
NT_EVENT_CONNECTED | NT_EVENT_IMMEDIATE, infos);
|
||||
}
|
||||
bool timedOut;
|
||||
return wpi::WaitForObject(h, timeout, &timedOut);
|
||||
}
|
||||
|
||||
NT_ConnectionListenerPoller ConnectionList::CreateListenerPoller() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return m_impl->CreateListenerPoller()->handle;
|
||||
}
|
||||
|
||||
void ConnectionList::DestroyListenerPoller(
|
||||
NT_ConnectionListenerPoller pollerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_impl->DestroyListenerPoller(pollerHandle);
|
||||
}
|
||||
|
||||
NT_ConnectionListener ConnectionList::AddPolledListener(
|
||||
NT_ConnectionListenerPoller pollerHandle, bool immediateNotify) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return m_impl->AddPolledListener(pollerHandle, immediateNotify);
|
||||
}
|
||||
|
||||
std::vector<ConnectionNotification> ConnectionList::ReadListenerQueue(
|
||||
NT_ConnectionListenerPoller pollerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (auto poller = m_impl->m_pollers.Get(pollerHandle)) {
|
||||
std::vector<ConnectionNotification> rv;
|
||||
rv.swap(poller->queue);
|
||||
return rv;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionList::RemoveListener(NT_ConnectionListener listenerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_impl->RemoveListener(listenerHandle);
|
||||
}
|
||||
|
||||
NT_ConnectionDataLogger ConnectionList::StartDataLog(wpi::log::DataLog& log,
|
||||
std::string_view name) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto now = Now();
|
||||
auto datalogger = m_impl->m_dataloggers.Add(m_impl->m_inst, log, name, now);
|
||||
for (auto&& conn : m_impl->m_connections) {
|
||||
auto datalogger = m_dataloggers.Add(m_inst, log, name, now);
|
||||
for (auto&& conn : m_connections) {
|
||||
datalogger->entry.Append(ConnInfoToJson(true, *conn), now);
|
||||
}
|
||||
return datalogger->handle;
|
||||
@@ -352,7 +120,7 @@ NT_ConnectionDataLogger ConnectionList::StartDataLog(wpi::log::DataLog& log,
|
||||
|
||||
void ConnectionList::StopDataLog(NT_ConnectionDataLogger logger) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (auto datalogger = m_impl->m_dataloggers.Remove(logger)) {
|
||||
if (auto datalogger = m_dataloggers.Remove(logger)) {
|
||||
datalogger->entry.Finish(Now());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,21 +4,31 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/DataLog.h>
|
||||
#include <wpi/UidVector.h>
|
||||
#include <wpi/mutex.h>
|
||||
|
||||
#include "Handle.h"
|
||||
#include "HandleMap.h"
|
||||
#include "IConnectionList.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class IListenerStorage;
|
||||
|
||||
class ConnectionList final : public IConnectionList {
|
||||
public:
|
||||
explicit ConnectionList(int inst);
|
||||
ConnectionList(int inst, IListenerStorage& listenerStorage);
|
||||
~ConnectionList() final;
|
||||
|
||||
// IConnectionList interface
|
||||
@@ -30,28 +40,35 @@ class ConnectionList final : public IConnectionList {
|
||||
std::vector<ConnectionInfo> GetConnections() const final;
|
||||
bool IsConnected() const final;
|
||||
|
||||
NT_ConnectionListener AddListener(
|
||||
bool immediateNotify,
|
||||
std::function<void(const ConnectionNotification& event)> callback);
|
||||
bool WaitForListenerQueue(double timeout);
|
||||
|
||||
NT_ConnectionListenerPoller CreateListenerPoller();
|
||||
void DestroyListenerPoller(NT_ConnectionListenerPoller pollerHandle);
|
||||
NT_ConnectionListener AddPolledListener(
|
||||
NT_ConnectionListenerPoller pollerHandle, bool immediateNotify);
|
||||
std::vector<ConnectionNotification> ReadListenerQueue(
|
||||
NT_ConnectionListenerPoller pollerHandle);
|
||||
void RemoveListener(NT_ConnectionListener listenerHandle);
|
||||
void AddListener(NT_Listener listener, unsigned int eventMask);
|
||||
|
||||
NT_ConnectionDataLogger StartDataLog(wpi::log::DataLog& log,
|
||||
std::string_view name);
|
||||
void StopDataLog(NT_ConnectionDataLogger logger);
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
int m_inst;
|
||||
IListenerStorage& m_listenerStorage;
|
||||
mutable wpi::mutex m_mutex;
|
||||
|
||||
// shared with user (must be atomic or mutex-protected)
|
||||
std::atomic_bool m_connected{false};
|
||||
wpi::UidVector<std::optional<ConnectionInfo>, 8> m_connections;
|
||||
|
||||
struct DataLoggerData {
|
||||
static constexpr auto kType = Handle::kConnectionDataLogger;
|
||||
|
||||
DataLoggerData(NT_ConnectionDataLogger handle, wpi::log::DataLog& log,
|
||||
std::string_view name, int64_t time)
|
||||
: handle{handle},
|
||||
entry{log, name,
|
||||
"{\"schema\":\"NTConnectionInfo\",\"source\":\"NT\"}", "json",
|
||||
time} {}
|
||||
|
||||
NT_ConnectionDataLogger handle;
|
||||
wpi::log::StringLogEntry entry;
|
||||
};
|
||||
HandleMap<DataLoggerData, 8> m_dataloggers;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
@@ -18,24 +18,16 @@ namespace nt {
|
||||
class Handle {
|
||||
public:
|
||||
enum Type {
|
||||
kConnectionListener = wpi::kHandleTypeNTBase,
|
||||
kConnectionListenerPoller,
|
||||
kListener = wpi::kHandleTypeNTBase,
|
||||
kListenerPoller,
|
||||
kEntry,
|
||||
kEntryListener,
|
||||
kEntryListenerPoller,
|
||||
kInstance,
|
||||
kLogger,
|
||||
kLoggerPoller,
|
||||
kDataLogger,
|
||||
kConnectionDataLogger,
|
||||
kMultiSubscriber,
|
||||
kTopic,
|
||||
kTopicListener,
|
||||
kTopicListenerPoller,
|
||||
kSubscriber,
|
||||
kPublisher,
|
||||
kValueListener,
|
||||
kValueListenerPoller,
|
||||
kTypeMax
|
||||
};
|
||||
static_assert(kTypeMax <= wpi::kHandleTypeHALBase);
|
||||
|
||||
49
ntcore/src/main/native/cpp/IListenerStorage.h
Normal file
49
ntcore/src/main/native/cpp/IListenerStorage.h
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class IListenerStorage {
|
||||
public:
|
||||
// Return false if event should not be issued (final check).
|
||||
// This is called only during Notify() processing.
|
||||
using FinishEventFunc = std::function<bool(unsigned int mask, Event* event)>;
|
||||
|
||||
virtual ~IListenerStorage() = default;
|
||||
|
||||
virtual void Activate(NT_Listener listener, unsigned int mask,
|
||||
FinishEventFunc finishEvent = {}) = 0;
|
||||
|
||||
// If handles is not empty, notifies ONLY those listeners
|
||||
virtual void Notify(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
std::span<ConnectionInfo const* const> infos) = 0;
|
||||
virtual void Notify(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
std::span<const TopicInfo> infos) = 0;
|
||||
virtual void Notify(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
NT_Topic topic, NT_Handle subentry,
|
||||
const Value& value) = 0;
|
||||
virtual void Notify(unsigned int flags, unsigned int level,
|
||||
std::string_view filename, unsigned int line,
|
||||
std::string_view message) = 0;
|
||||
|
||||
void Notify(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
const ConnectionInfo* info) {
|
||||
Notify(handles, flags, {&info, 1});
|
||||
}
|
||||
void Notify(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
const TopicInfo& info) {
|
||||
Notify(handles, flags, {&info, 1});
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
@@ -13,11 +13,12 @@ wpi::mutex InstanceImpl::s_mutex;
|
||||
using namespace std::placeholders;
|
||||
|
||||
InstanceImpl::InstanceImpl(int inst)
|
||||
: logger_impl(inst),
|
||||
logger(
|
||||
std::bind(&LoggerImpl::Log, &logger_impl, _1, _2, _3, _4)), // NOLINT
|
||||
connectionList(inst),
|
||||
localStorage(inst, logger),
|
||||
: listenerStorage{inst},
|
||||
logger_impl{listenerStorage},
|
||||
logger{
|
||||
std::bind(&LoggerImpl::Log, &logger_impl, _1, _2, _3, _4)}, // NOLINT
|
||||
connectionList{inst, listenerStorage},
|
||||
localStorage{inst, listenerStorage, logger},
|
||||
m_inst{inst} {
|
||||
logger.set_min_level(logger_impl.GetMinLevel());
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "ConnectionList.h"
|
||||
#include "Handle.h"
|
||||
#include "ListenerStorage.h"
|
||||
#include "LocalStorage.h"
|
||||
#include "Log.h"
|
||||
#include "LoggerImpl.h"
|
||||
@@ -55,6 +56,7 @@ class InstanceImpl {
|
||||
std::shared_ptr<NetworkServer> GetServer();
|
||||
std::shared_ptr<INetworkClient> GetClient();
|
||||
|
||||
ListenerStorage listenerStorage;
|
||||
LoggerImpl logger_impl;
|
||||
wpi::Logger logger;
|
||||
ConnectionList connectionList;
|
||||
|
||||
351
ntcore/src/main/native/cpp/ListenerStorage.cpp
Normal file
351
ntcore/src/main/native/cpp/ListenerStorage.cpp
Normal file
@@ -0,0 +1,351 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "ListenerStorage.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <wpi/DenseMap.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
|
||||
#include "ntcore_c.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
class ListenerStorage::Thread final : public wpi::SafeThreadEvent {
|
||||
public:
|
||||
explicit Thread(NT_ListenerPoller poller) : m_poller{poller} {}
|
||||
|
||||
void Main() final;
|
||||
|
||||
NT_ListenerPoller m_poller;
|
||||
wpi::DenseMap<NT_Listener, ListenerCallback> m_callbacks;
|
||||
wpi::Event m_waitQueueWakeup;
|
||||
wpi::Event m_waitQueueWaiter;
|
||||
};
|
||||
|
||||
void ListenerStorage::Thread::Main() {
|
||||
while (m_active) {
|
||||
WPI_Handle signaledBuf[3];
|
||||
auto signaled = wpi::WaitForObjects(
|
||||
{m_poller, m_stopEvent.GetHandle(), m_waitQueueWakeup.GetHandle()},
|
||||
signaledBuf);
|
||||
if (signaled.empty() || !m_active) {
|
||||
return;
|
||||
}
|
||||
// call all the way back out to the C++ API to ensure valid handle
|
||||
auto events = nt::ReadListenerQueue(m_poller);
|
||||
if (events.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::unique_lock lock{m_mutex};
|
||||
for (auto&& event : events) {
|
||||
auto callbackIt = m_callbacks.find(event.listener);
|
||||
if (callbackIt != m_callbacks.end()) {
|
||||
auto callback = callbackIt->second;
|
||||
lock.unlock();
|
||||
callback(event);
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
if (std::find(signaled.begin(), signaled.end(),
|
||||
m_waitQueueWakeup.GetHandle()) != signaled.end()) {
|
||||
m_waitQueueWaiter.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListenerStorage::ListenerStorage(int inst) : m_inst{inst} {}
|
||||
|
||||
ListenerStorage::~ListenerStorage() = default;
|
||||
|
||||
void ListenerStorage::Activate(NT_Listener listenerHandle, unsigned int mask,
|
||||
FinishEventFunc finishEvent) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (auto listener = m_listeners.Get(listenerHandle)) {
|
||||
listener->sources.emplace_back(std::move(finishEvent), mask);
|
||||
unsigned int deltaMask = mask & (~listener->eventMask);
|
||||
listener->eventMask |= mask;
|
||||
|
||||
if ((deltaMask & NT_EVENT_CONNECTION) != 0) {
|
||||
m_connListeners.Add(listener);
|
||||
}
|
||||
if ((deltaMask & NT_EVENT_TOPIC) != 0) {
|
||||
m_topicListeners.Add(listener);
|
||||
}
|
||||
if ((deltaMask & NT_EVENT_VALUE_ALL) != 0) {
|
||||
m_valueListeners.Add(listener);
|
||||
}
|
||||
// detect the higher log bits too; see LoggerImpl
|
||||
if ((deltaMask & NT_EVENT_LOGMESSAGE) != 0 ||
|
||||
(deltaMask & 0x1ff0000) != 0) {
|
||||
m_logListeners.Add(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ListenerStorage::Notify(std::span<const NT_Listener> handles,
|
||||
unsigned int flags,
|
||||
std::span<ConnectionInfo const* const> infos) {
|
||||
if (flags == 0) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
auto doSignal = [&](ListenerData& listener) {
|
||||
if ((flags & listener.eventMask) != 0) {
|
||||
for (auto&& [finishEvent, mask] : listener.sources) {
|
||||
if ((flags & mask) != 0) {
|
||||
for (auto&& info : infos) {
|
||||
listener.poller->queue.emplace_back(listener.handle, flags, *info);
|
||||
// finishEvent is never set (see ConnectionList)
|
||||
}
|
||||
}
|
||||
}
|
||||
listener.handle.Set();
|
||||
listener.poller->handle.Set();
|
||||
}
|
||||
};
|
||||
|
||||
if (!handles.empty()) {
|
||||
for (auto handle : handles) {
|
||||
if (auto listener = m_listeners.Get(handle)) {
|
||||
doSignal(*listener);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto&& listener : m_connListeners) {
|
||||
doSignal(*listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ListenerStorage::Notify(std::span<const NT_Listener> handles,
|
||||
unsigned int flags,
|
||||
std::span<const TopicInfo> infos) {
|
||||
if (flags == 0) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
auto doSignal = [&](ListenerData& listener) {
|
||||
if ((flags & listener.eventMask) != 0) {
|
||||
int count = 0;
|
||||
for (auto&& [finishEvent, mask] : listener.sources) {
|
||||
if ((flags & mask) != 0) {
|
||||
for (auto&& info : infos) {
|
||||
listener.poller->queue.emplace_back(listener.handle, flags, info);
|
||||
if (finishEvent &&
|
||||
!finishEvent(mask, &listener.poller->queue.back())) {
|
||||
listener.poller->queue.pop_back();
|
||||
} else {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
listener.handle.Set();
|
||||
listener.poller->handle.Set();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!handles.empty()) {
|
||||
for (auto handle : handles) {
|
||||
if (auto listener = m_listeners.Get(handle)) {
|
||||
doSignal(*listener);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto&& listener : m_topicListeners) {
|
||||
doSignal(*listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ListenerStorage::Notify(std::span<const NT_Listener> handles,
|
||||
unsigned int flags, NT_Topic topic,
|
||||
NT_Handle subentry, const Value& value) {
|
||||
if (flags == 0) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
auto doSignal = [&](ListenerData& listener) {
|
||||
if ((flags & listener.eventMask) != 0) {
|
||||
int count = 0;
|
||||
for (auto&& [finishEvent, mask] : listener.sources) {
|
||||
if ((flags & mask) != 0) {
|
||||
listener.poller->queue.emplace_back(listener.handle, flags, topic,
|
||||
subentry, value);
|
||||
if (finishEvent &&
|
||||
!finishEvent(mask, &listener.poller->queue.back())) {
|
||||
listener.poller->queue.pop_back();
|
||||
} else {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
listener.handle.Set();
|
||||
listener.poller->handle.Set();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!handles.empty()) {
|
||||
for (auto handle : handles) {
|
||||
if (auto listener = m_listeners.Get(handle)) {
|
||||
doSignal(*listener);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto&& listener : m_valueListeners) {
|
||||
doSignal(*listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ListenerStorage::Notify(unsigned int flags, unsigned int level,
|
||||
std::string_view filename, unsigned int line,
|
||||
std::string_view message) {
|
||||
if (flags == 0) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{m_mutex};
|
||||
for (auto&& listener : m_logListeners) {
|
||||
if ((flags & listener->eventMask) != 0) {
|
||||
int count = 0;
|
||||
for (auto&& [finishEvent, mask] : listener->sources) {
|
||||
if ((flags & mask) != 0) {
|
||||
listener->poller->queue.emplace_back(listener->handle, flags, level,
|
||||
filename, line, message);
|
||||
if (finishEvent &&
|
||||
!finishEvent(mask, &listener->poller->queue.back())) {
|
||||
listener->poller->queue.pop_back();
|
||||
} else {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
listener->handle.Set();
|
||||
listener->poller->handle.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NT_Listener ListenerStorage::AddListener(ListenerCallback callback) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (!m_thread) {
|
||||
m_thread.Start(m_pollers.Add(m_inst)->handle);
|
||||
}
|
||||
if (auto thr = m_thread.GetThread()) {
|
||||
auto listener = DoAddListener(thr->m_poller);
|
||||
if (listener) {
|
||||
thr->m_callbacks.try_emplace(listener, std::move(callback));
|
||||
}
|
||||
return listener;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
NT_Listener ListenerStorage::AddListener(NT_ListenerPoller pollerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return DoAddListener(pollerHandle);
|
||||
}
|
||||
|
||||
NT_Listener ListenerStorage::DoAddListener(NT_ListenerPoller pollerHandle) {
|
||||
if (auto poller = m_pollers.Get(pollerHandle)) {
|
||||
return m_listeners.Add(m_inst, poller)->handle;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
NT_ListenerPoller ListenerStorage::CreateListenerPoller() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return m_pollers.Add(m_inst)->handle;
|
||||
}
|
||||
|
||||
std::vector<std::pair<NT_Listener, unsigned int>>
|
||||
ListenerStorage::DestroyListenerPoller(NT_ListenerPoller pollerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (auto poller = m_pollers.Remove(pollerHandle)) {
|
||||
// ensure all listeners that use this poller are removed
|
||||
wpi::SmallVector<NT_Listener, 16> toRemove;
|
||||
for (auto&& listener : m_listeners) {
|
||||
if (listener->poller == poller.get()) {
|
||||
toRemove.emplace_back(listener->handle);
|
||||
}
|
||||
}
|
||||
return DoRemoveListeners(toRemove);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Event> ListenerStorage::ReadListenerQueue(
|
||||
NT_ListenerPoller pollerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (auto poller = m_pollers.Get(pollerHandle)) {
|
||||
std::vector<Event> rv;
|
||||
rv.swap(poller->queue);
|
||||
return rv;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<NT_Listener, unsigned int>>
|
||||
ListenerStorage::RemoveListener(NT_Listener listenerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return DoRemoveListeners({&listenerHandle, 1});
|
||||
}
|
||||
|
||||
bool ListenerStorage::WaitForListenerQueue(double timeout) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
WPI_EventHandle h;
|
||||
if (auto thr = m_thread.GetThread()) {
|
||||
h = thr->m_waitQueueWaiter.GetHandle();
|
||||
thr->m_waitQueueWakeup.Set();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
bool timedOut;
|
||||
return wpi::WaitForObject(h, timeout, &timedOut);
|
||||
}
|
||||
|
||||
std::vector<std::pair<NT_Listener, unsigned int>>
|
||||
ListenerStorage::DoRemoveListeners(std::span<const NT_Listener> handles) {
|
||||
std::vector<std::pair<NT_Listener, unsigned int>> rv;
|
||||
auto thr = m_thread.GetThread();
|
||||
for (auto handle : handles) {
|
||||
if (auto listener = m_listeners.Remove(handle)) {
|
||||
rv.emplace_back(handle, listener->eventMask);
|
||||
if (thr) {
|
||||
if (thr->m_poller == listener->poller->handle) {
|
||||
thr->m_callbacks.erase(handle);
|
||||
}
|
||||
}
|
||||
if ((listener->eventMask & NT_EVENT_CONNECTION) != 0) {
|
||||
m_connListeners.Remove(listener.get());
|
||||
}
|
||||
if ((listener->eventMask & NT_EVENT_TOPIC) != 0) {
|
||||
m_topicListeners.Remove(listener.get());
|
||||
}
|
||||
if ((listener->eventMask & NT_EVENT_VALUE_ALL) != 0) {
|
||||
m_valueListeners.Remove(listener.get());
|
||||
}
|
||||
if ((listener->eventMask & NT_EVENT_LOGMESSAGE) != 0 ||
|
||||
(listener->eventMask & 0x1ff0000) != 0) {
|
||||
m_logListeners.Remove(listener.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
111
ntcore/src/main/native/cpp/ListenerStorage.h
Normal file
111
ntcore/src/main/native/cpp/ListenerStorage.h
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/SafeThread.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/Synchronization.h>
|
||||
#include <wpi/mutex.h>
|
||||
|
||||
#include "Handle.h"
|
||||
#include "HandleMap.h"
|
||||
#include "IListenerStorage.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class ListenerStorage final : public IListenerStorage {
|
||||
public:
|
||||
explicit ListenerStorage(int inst);
|
||||
ListenerStorage(const ListenerStorage&) = delete;
|
||||
ListenerStorage& operator=(const ListenerStorage&) = delete;
|
||||
~ListenerStorage() final;
|
||||
|
||||
// IListenerStorage interface
|
||||
void Activate(NT_Listener listenerHandle, unsigned int mask,
|
||||
FinishEventFunc finishEvent = {}) final;
|
||||
void Notify(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
std::span<ConnectionInfo const* const> infos) final;
|
||||
void Notify(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
std::span<const TopicInfo> infos) final;
|
||||
void Notify(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
NT_Topic topic, NT_Handle subentry, const Value& value) final;
|
||||
void Notify(unsigned int flags, unsigned int level, std::string_view filename,
|
||||
unsigned int line, std::string_view message) final;
|
||||
|
||||
// user-facing functions
|
||||
NT_Listener AddListener(ListenerCallback callback);
|
||||
NT_Listener AddListener(NT_ListenerPoller pollerHandle);
|
||||
NT_ListenerPoller CreateListenerPoller();
|
||||
|
||||
// returns listener handle and mask for each listener that was destroyed
|
||||
[[nodiscard]] std::vector<std::pair<NT_Listener, unsigned int>>
|
||||
DestroyListenerPoller(NT_ListenerPoller pollerHandle);
|
||||
|
||||
std::vector<Event> ReadListenerQueue(NT_ListenerPoller pollerHandle);
|
||||
|
||||
// returns listener handle and mask for each listener that was destroyed
|
||||
[[nodiscard]] std::vector<std::pair<NT_Listener, unsigned int>>
|
||||
RemoveListener(NT_Listener listenerHandle);
|
||||
|
||||
bool WaitForListenerQueue(double timeout);
|
||||
|
||||
private:
|
||||
// these assume the mutex is already held
|
||||
NT_Listener DoAddListener(NT_ListenerPoller pollerHandle);
|
||||
std::vector<std::pair<NT_Listener, unsigned int>> DoRemoveListeners(
|
||||
std::span<const NT_Listener> handles);
|
||||
|
||||
int m_inst;
|
||||
mutable wpi::mutex m_mutex;
|
||||
|
||||
struct PollerData {
|
||||
static constexpr auto kType = Handle::kListenerPoller;
|
||||
|
||||
explicit PollerData(NT_ListenerPoller handle) : handle{handle} {}
|
||||
|
||||
wpi::SignalObject<NT_ListenerPoller> handle;
|
||||
std::vector<Event> queue;
|
||||
};
|
||||
HandleMap<PollerData, 8> m_pollers;
|
||||
|
||||
struct ListenerData {
|
||||
static constexpr auto kType = Handle::kListener;
|
||||
|
||||
ListenerData(NT_Listener handle, PollerData* poller)
|
||||
: handle{handle}, poller{poller} {}
|
||||
|
||||
wpi::SignalObject<NT_Listener> handle;
|
||||
PollerData* poller;
|
||||
wpi::SmallVector<std::pair<FinishEventFunc, unsigned int>, 2> sources;
|
||||
unsigned int eventMask{0};
|
||||
};
|
||||
HandleMap<ListenerData, 8> m_listeners;
|
||||
|
||||
// Utility wrapper for making a set-like vector
|
||||
template <typename T>
|
||||
class VectorSet : public std::vector<T> {
|
||||
public:
|
||||
void Add(T value) { this->push_back(value); }
|
||||
void Remove(T value) { std::erase(*this, value); }
|
||||
};
|
||||
|
||||
VectorSet<ListenerData*> m_connListeners;
|
||||
VectorSet<ListenerData*> m_topicListeners;
|
||||
VectorSet<ListenerData*> m_valueListeners;
|
||||
VectorSet<ListenerData*> m_logListeners;
|
||||
|
||||
class Thread;
|
||||
wpi::SafeThreadOwner<Thread> m_thread;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,9 +25,12 @@ class Logger;
|
||||
|
||||
namespace nt {
|
||||
|
||||
class IListenerStorage;
|
||||
|
||||
class LocalStorage final : public net::ILocalStorage {
|
||||
public:
|
||||
LocalStorage(int inst, wpi::Logger& logger);
|
||||
LocalStorage(int inst, IListenerStorage& listenerStorage,
|
||||
wpi::Logger& logger);
|
||||
LocalStorage(const LocalStorage&) = delete;
|
||||
LocalStorage& operator=(const LocalStorage&) = delete;
|
||||
~LocalStorage() final;
|
||||
@@ -189,51 +192,15 @@ class LocalStorage final : public net::ILocalStorage {
|
||||
int64_t GetEntryLastChange(NT_Entry entry);
|
||||
|
||||
//
|
||||
// Topic listener functions
|
||||
// Listener functions
|
||||
//
|
||||
|
||||
NT_TopicListener AddTopicListener(
|
||||
std::span<const std::string_view> prefixes, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> callback);
|
||||
NT_TopicListener AddTopicListener(
|
||||
NT_Handle handle, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> callback);
|
||||
bool WaitForTopicListenerQueue(double timeout);
|
||||
void AddListener(NT_Listener listener,
|
||||
std::span<const std::string_view> prefixes,
|
||||
unsigned int mask);
|
||||
void AddListener(NT_Listener listener, NT_Handle handle, unsigned int mask);
|
||||
|
||||
NT_TopicListenerPoller CreateTopicListenerPoller();
|
||||
void DestroyTopicListenerPoller(NT_TopicListenerPoller poller);
|
||||
|
||||
NT_TopicListener AddPolledTopicListener(
|
||||
NT_TopicListenerPoller poller, std::span<const std::string_view> prefixes,
|
||||
unsigned int mask);
|
||||
NT_TopicListener AddPolledTopicListener(NT_TopicListenerPoller poller,
|
||||
NT_Handle handle, unsigned int mask);
|
||||
|
||||
std::vector<TopicNotification> ReadTopicListenerQueue(
|
||||
NT_TopicListenerPoller poller);
|
||||
|
||||
void RemoveTopicListener(NT_TopicListener listener);
|
||||
|
||||
//
|
||||
// Value listener functions
|
||||
//
|
||||
|
||||
NT_ValueListener AddValueListener(
|
||||
NT_Handle subentry, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> callback);
|
||||
bool WaitForValueListenerQueue(double timeout);
|
||||
|
||||
NT_ValueListenerPoller CreateValueListenerPoller();
|
||||
void DestroyValueListenerPoller(NT_ValueListenerPoller poller);
|
||||
|
||||
NT_ValueListener AddPolledValueListener(NT_ValueListenerPoller poller,
|
||||
NT_Handle subentry,
|
||||
unsigned int mask);
|
||||
|
||||
std::vector<ValueNotification> ReadValueListenerQueue(
|
||||
NT_ValueListenerPoller poller);
|
||||
|
||||
void RemoveValueListener(NT_ValueListener listener);
|
||||
void RemoveListener(NT_Listener listener, unsigned int mask);
|
||||
|
||||
//
|
||||
// Data log functions
|
||||
|
||||
@@ -5,24 +5,27 @@
|
||||
#include "LoggerImpl.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <wpi/DenseMap.h>
|
||||
#include <wpi/Logger.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/fs.h>
|
||||
|
||||
#include "IListenerStorage.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
static void DefaultLogger(unsigned int level, const char* file,
|
||||
unsigned int line, const char* msg) {
|
||||
if (level == 20) {
|
||||
if (level == wpi::WPI_LOG_INFO) {
|
||||
fmt::print(stderr, "NT: {}\n", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string_view levelmsg;
|
||||
if (level >= 50) {
|
||||
if (level >= wpi::WPI_LOG_CRITICAL) {
|
||||
levelmsg = "CRITICAL";
|
||||
} else if (level >= 40) {
|
||||
} else if (level >= wpi::WPI_LOG_ERROR) {
|
||||
levelmsg = "ERROR";
|
||||
} else if (level >= 30) {
|
||||
} else if (level >= wpi::WPI_LOG_WARNING) {
|
||||
levelmsg = "WARNING";
|
||||
} else {
|
||||
return;
|
||||
@@ -30,108 +33,107 @@ static void DefaultLogger(unsigned int level, const char* file,
|
||||
fmt::print(stderr, "NT: {}: {} ({}:{})\n", levelmsg, msg, file, line);
|
||||
}
|
||||
|
||||
class LoggerImpl::Thread final : public wpi::SafeThreadEvent {
|
||||
public:
|
||||
explicit Thread(NT_LoggerPoller poller) : m_poller{poller} {}
|
||||
static constexpr unsigned int kFlagCritical = 1u << 16;
|
||||
static constexpr unsigned int kFlagError = 1u << 17;
|
||||
static constexpr unsigned int kFlagWarning = 1u << 18;
|
||||
static constexpr unsigned int kFlagInfo = 1u << 19;
|
||||
static constexpr unsigned int kFlagDebug = 1u << 20;
|
||||
static constexpr unsigned int kFlagDebug1 = 1u << 21;
|
||||
static constexpr unsigned int kFlagDebug2 = 1u << 22;
|
||||
static constexpr unsigned int kFlagDebug3 = 1u << 23;
|
||||
static constexpr unsigned int kFlagDebug4 = 1u << 24;
|
||||
|
||||
void Main() final;
|
||||
|
||||
NT_LoggerPoller m_poller;
|
||||
wpi::DenseMap<NT_Logger, std::function<void(const LogMessage& msg)>>
|
||||
m_callbacks;
|
||||
};
|
||||
|
||||
void LoggerImpl::Thread::Main() {
|
||||
while (m_active) {
|
||||
WPI_Handle signaledBuf[2];
|
||||
auto signaled =
|
||||
wpi::WaitForObjects({m_poller, m_stopEvent.GetHandle()}, signaledBuf);
|
||||
if (signaled.empty() || !m_active) {
|
||||
return;
|
||||
}
|
||||
// call all the way back out to the C++ API to ensure valid handle
|
||||
auto events = nt::ReadLoggerQueue(m_poller);
|
||||
if (events.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::unique_lock lock{m_mutex};
|
||||
for (auto&& event : events) {
|
||||
auto callbackIt = m_callbacks.find(event.logger);
|
||||
if (callbackIt != m_callbacks.end()) {
|
||||
auto callback = callbackIt->second;
|
||||
lock.unlock();
|
||||
callback(event);
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
static unsigned int LevelToFlag(unsigned int level) {
|
||||
if (level >= wpi::WPI_LOG_CRITICAL) {
|
||||
return EventFlags::kLogMessage | kFlagCritical;
|
||||
} else if (level >= wpi::WPI_LOG_ERROR) {
|
||||
return EventFlags::kLogMessage | kFlagError;
|
||||
} else if (level >= wpi::WPI_LOG_WARNING) {
|
||||
return EventFlags::kLogMessage | kFlagWarning;
|
||||
} else if (level >= wpi::WPI_LOG_INFO) {
|
||||
return EventFlags::kLogMessage | kFlagInfo;
|
||||
} else if (level >= wpi::WPI_LOG_DEBUG) {
|
||||
return EventFlags::kLogMessage | kFlagDebug;
|
||||
} else if (level >= wpi::WPI_LOG_DEBUG1) {
|
||||
return EventFlags::kLogMessage | kFlagDebug1;
|
||||
} else if (level >= wpi::WPI_LOG_DEBUG2) {
|
||||
return EventFlags::kLogMessage | kFlagDebug2;
|
||||
} else if (level >= wpi::WPI_LOG_DEBUG3) {
|
||||
return EventFlags::kLogMessage | kFlagDebug3;
|
||||
} else if (level >= wpi::WPI_LOG_DEBUG4) {
|
||||
return EventFlags::kLogMessage | kFlagDebug4;
|
||||
} else {
|
||||
return EventFlags::kLogMessage;
|
||||
}
|
||||
}
|
||||
|
||||
LoggerImpl::LoggerImpl(int inst) : m_inst{inst} {}
|
||||
static unsigned int LevelsToEventMask(unsigned int minLevel,
|
||||
unsigned int maxLevel) {
|
||||
unsigned int mask = 0;
|
||||
if (minLevel <= wpi::WPI_LOG_CRITICAL && maxLevel >= wpi::WPI_LOG_CRITICAL) {
|
||||
mask |= kFlagCritical;
|
||||
}
|
||||
if (minLevel <= wpi::WPI_LOG_ERROR && maxLevel >= wpi::WPI_LOG_ERROR) {
|
||||
mask |= kFlagError;
|
||||
}
|
||||
if (minLevel <= wpi::WPI_LOG_WARNING && maxLevel >= wpi::WPI_LOG_WARNING) {
|
||||
mask |= kFlagWarning;
|
||||
}
|
||||
if (minLevel <= wpi::WPI_LOG_INFO && maxLevel >= wpi::WPI_LOG_INFO) {
|
||||
mask |= kFlagInfo;
|
||||
}
|
||||
if (minLevel <= wpi::WPI_LOG_DEBUG && maxLevel >= wpi::WPI_LOG_DEBUG) {
|
||||
mask |= kFlagDebug;
|
||||
}
|
||||
if (minLevel <= wpi::WPI_LOG_DEBUG1 && maxLevel >= wpi::WPI_LOG_DEBUG1) {
|
||||
mask |= kFlagDebug1;
|
||||
}
|
||||
if (minLevel <= wpi::WPI_LOG_DEBUG2 && maxLevel >= wpi::WPI_LOG_DEBUG2) {
|
||||
mask |= kFlagDebug2;
|
||||
}
|
||||
if (minLevel <= wpi::WPI_LOG_DEBUG3 && maxLevel >= wpi::WPI_LOG_DEBUG3) {
|
||||
mask |= kFlagDebug3;
|
||||
}
|
||||
if (minLevel <= wpi::WPI_LOG_DEBUG4 && maxLevel >= wpi::WPI_LOG_DEBUG4) {
|
||||
mask |= kFlagDebug4;
|
||||
}
|
||||
if (mask == 0) {
|
||||
mask = EventFlags::kLogMessage;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
LoggerImpl::LoggerImpl(IListenerStorage& listenerStorage)
|
||||
: m_listenerStorage{listenerStorage} {}
|
||||
|
||||
LoggerImpl::~LoggerImpl() = default;
|
||||
|
||||
NT_Logger LoggerImpl::Add(std::function<void(const LogMessage& msg)> callback,
|
||||
unsigned int minLevel, unsigned int maxLevel) {
|
||||
if (!m_thread) {
|
||||
m_thread.Start(CreatePoller());
|
||||
}
|
||||
if (auto thr = m_thread.GetThread()) {
|
||||
auto listener = AddPolled(thr->m_poller, minLevel, maxLevel);
|
||||
if (listener) {
|
||||
thr->m_callbacks.try_emplace(listener, std::move(callback));
|
||||
}
|
||||
return listener;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
void LoggerImpl::AddListener(NT_Listener listener, unsigned int minLevel,
|
||||
unsigned int maxLevel) {
|
||||
++m_listenerCount;
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_listenerLevels.emplace_back(listener, minLevel, maxLevel);
|
||||
m_listenerStorage.Activate(listener, LevelsToEventMask(minLevel, maxLevel),
|
||||
[](unsigned int mask, Event* event) {
|
||||
event->flags = NT_EVENT_LOGMESSAGE;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
NT_LoggerPoller LoggerImpl::CreatePoller() {
|
||||
void LoggerImpl::RemoveListener(NT_Listener listener) {
|
||||
--m_listenerCount;
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return m_pollers.Add(m_inst)->handle;
|
||||
}
|
||||
|
||||
void LoggerImpl::DestroyPoller(NT_LoggerPoller pollerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_pollers.Remove(pollerHandle);
|
||||
}
|
||||
|
||||
NT_Logger LoggerImpl::AddPolled(NT_LoggerPoller pollerHandle,
|
||||
unsigned int minLevel, unsigned int maxLevel) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (auto poller = m_pollers.Get(pollerHandle)) {
|
||||
return m_listeners.Add(m_inst, poller, minLevel, maxLevel)->handle;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<LogMessage> LoggerImpl::ReadQueue(NT_LoggerPoller pollerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (auto poller = m_pollers.Get(pollerHandle)) {
|
||||
std::vector<LogMessage> rv;
|
||||
rv.swap(poller->queue);
|
||||
return rv;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void LoggerImpl::Remove(NT_Logger listenerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_listeners.Remove(listenerHandle);
|
||||
if (auto thr = m_thread.GetThread()) {
|
||||
thr->m_callbacks.erase(listenerHandle);
|
||||
}
|
||||
std::erase_if(m_listenerLevels,
|
||||
[&](auto& v) { return v.listener == listener; });
|
||||
}
|
||||
|
||||
unsigned int LoggerImpl::GetMinLevel() {
|
||||
// return 0;
|
||||
std::scoped_lock lock{m_mutex};
|
||||
unsigned int level = NT_LOG_INFO;
|
||||
for (auto&& listener : m_listeners) {
|
||||
if (listener && listener->minLevel < level) {
|
||||
level = listener->minLevel;
|
||||
for (auto&& listenerLevel : m_listenerLevels) {
|
||||
if (listenerLevel.minLevel < level) {
|
||||
level = listenerLevel.minLevel;
|
||||
}
|
||||
}
|
||||
return level;
|
||||
@@ -140,19 +142,10 @@ unsigned int LoggerImpl::GetMinLevel() {
|
||||
void LoggerImpl::Log(unsigned int level, const char* file, unsigned int line,
|
||||
const char* msg) {
|
||||
auto filename = fs::path{file}.filename();
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (m_listeners.empty()) {
|
||||
DefaultLogger(level, filename.string().c_str(), line, msg);
|
||||
} else {
|
||||
for (auto&& listener : m_listeners) {
|
||||
if (level >= listener->minLevel && level <= listener->maxLevel) {
|
||||
listener->poller->queue.emplace_back(listener->handle.GetHandle(),
|
||||
level, file, line, msg);
|
||||
listener->poller->handle.Set();
|
||||
listener->handle.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_listenerCount == 0) {
|
||||
DefaultLogger(level, filename.string().c_str(), line, msg);
|
||||
} else {
|
||||
m_listenerStorage.Notify(LevelToFlag(level), level, filename.string(), line,
|
||||
msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,34 +4,29 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/SafeThread.h>
|
||||
#include <wpi/Synchronization.h>
|
||||
#include <wpi/mutex.h>
|
||||
|
||||
#include "Handle.h"
|
||||
#include "HandleMap.h"
|
||||
#include "IListenerStorage.h"
|
||||
#include "ntcore_c.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class IListenerStorage;
|
||||
|
||||
class LoggerImpl {
|
||||
public:
|
||||
explicit LoggerImpl(int inst);
|
||||
explicit LoggerImpl(IListenerStorage& listenerStorage);
|
||||
LoggerImpl(const LoggerImpl&) = delete;
|
||||
LoggerImpl& operator=(const LoggerImpl&) = delete;
|
||||
~LoggerImpl();
|
||||
|
||||
NT_Logger Add(std::function<void(const LogMessage& msg)> callback,
|
||||
unsigned int minLevel, unsigned int maxLevel);
|
||||
|
||||
NT_LoggerPoller CreatePoller();
|
||||
void DestroyPoller(NT_LoggerPoller pollerHandle);
|
||||
NT_Logger AddPolled(NT_LoggerPoller pollerHandle, unsigned int minLevel,
|
||||
unsigned int maxLevel);
|
||||
std::vector<LogMessage> ReadQueue(NT_LoggerPoller pollerHandle);
|
||||
void Remove(NT_Logger listenerHandle);
|
||||
void AddListener(NT_Listener listener, unsigned int minLevel,
|
||||
unsigned int maxLevel);
|
||||
void RemoveListener(NT_Listener listener);
|
||||
|
||||
unsigned int GetMinLevel();
|
||||
|
||||
@@ -39,38 +34,20 @@ class LoggerImpl {
|
||||
const char* msg);
|
||||
|
||||
private:
|
||||
int m_inst;
|
||||
mutable wpi::mutex m_mutex;
|
||||
IListenerStorage& m_listenerStorage;
|
||||
std::atomic_int m_listenerCount{0};
|
||||
wpi::mutex m_mutex;
|
||||
|
||||
struct PollerData {
|
||||
static constexpr auto kType = Handle::kLoggerPoller;
|
||||
struct ListenerLevels {
|
||||
ListenerLevels(NT_Listener listener, unsigned int minLevel,
|
||||
unsigned int maxLevel)
|
||||
: listener{listener}, minLevel{minLevel}, maxLevel{maxLevel} {}
|
||||
|
||||
explicit PollerData(NT_LoggerPoller handle) : handle{handle} {}
|
||||
|
||||
wpi::SignalObject<NT_LoggerPoller> handle;
|
||||
std::vector<LogMessage> queue;
|
||||
};
|
||||
HandleMap<PollerData, 8> m_pollers;
|
||||
|
||||
struct ListenerData {
|
||||
static constexpr auto kType = Handle::kLogger;
|
||||
|
||||
ListenerData(NT_Logger handle, PollerData* poller, unsigned int minLevel,
|
||||
unsigned int maxLevel)
|
||||
: handle{handle},
|
||||
poller{poller},
|
||||
minLevel{minLevel},
|
||||
maxLevel{maxLevel} {}
|
||||
|
||||
wpi::SignalObject<NT_Logger> handle;
|
||||
PollerData* poller;
|
||||
NT_Listener listener;
|
||||
unsigned int minLevel;
|
||||
unsigned int maxLevel;
|
||||
};
|
||||
HandleMap<ListenerData, 8> m_listeners;
|
||||
|
||||
class Thread;
|
||||
wpi::SafeThreadOwner<Thread> m_thread;
|
||||
std::vector<ListenerLevels> m_listenerLevels;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
@@ -34,15 +34,14 @@ void JNI_UnloadTypes(JNIEnv* env);
|
||||
static JavaVM* jvm = nullptr;
|
||||
static JClass booleanCls;
|
||||
static JClass connectionInfoCls;
|
||||
static JClass connectionNotificationCls;
|
||||
static JClass doubleCls;
|
||||
static JClass eventCls;
|
||||
static JClass floatCls;
|
||||
static JClass logMessageCls;
|
||||
static JClass longCls;
|
||||
static JClass topicInfoCls;
|
||||
static JClass topicNotificationCls;
|
||||
static JClass valueCls;
|
||||
static JClass valueNotificationCls;
|
||||
static JClass valueEventDataCls;
|
||||
static JException illegalArgEx;
|
||||
static JException interruptedEx;
|
||||
static JException nullPointerEx;
|
||||
@@ -50,16 +49,14 @@ static JException nullPointerEx;
|
||||
static const JClassInit classes[] = {
|
||||
{"java/lang/Boolean", &booleanCls},
|
||||
{"edu/wpi/first/networktables/ConnectionInfo", &connectionInfoCls},
|
||||
{"edu/wpi/first/networktables/ConnectionNotification",
|
||||
&connectionNotificationCls},
|
||||
{"java/lang/Double", &doubleCls},
|
||||
{"edu/wpi/first/networktables/NetworkTableEvent", &eventCls},
|
||||
{"java/lang/Float", &floatCls},
|
||||
{"edu/wpi/first/networktables/LogMessage", &logMessageCls},
|
||||
{"java/lang/Long", &longCls},
|
||||
{"edu/wpi/first/networktables/TopicInfo", &topicInfoCls},
|
||||
{"edu/wpi/first/networktables/TopicNotification", &topicNotificationCls},
|
||||
{"edu/wpi/first/networktables/NetworkTableValue", &valueCls},
|
||||
{"edu/wpi/first/networktables/ValueNotification", &valueNotificationCls}};
|
||||
{"edu/wpi/first/networktables/ValueEventData", &valueEventDataCls}};
|
||||
|
||||
static const JExceptionInit exceptions[] = {
|
||||
{"java/lang/IllegalArgumentException", &illegalArgEx},
|
||||
@@ -216,29 +213,12 @@ static jobject MakeJObject(JNIEnv* env, const nt::ConnectionInfo& info) {
|
||||
static_cast<jint>(info.protocol_version));
|
||||
}
|
||||
|
||||
static jobject MakeJObject(JNIEnv* env, jobject inst,
|
||||
const nt::ConnectionNotification& notification) {
|
||||
static jobject MakeJObject(JNIEnv* env, const nt::LogMessage& msg) {
|
||||
static jmethodID constructor = env->GetMethodID(
|
||||
connectionNotificationCls, "<init>",
|
||||
"(Ledu/wpi/first/networktables/NetworkTableInstance;IZLedu/wpi/first/"
|
||||
"networktables/ConnectionInfo;)V");
|
||||
JLocal<jobject> conn{env, MakeJObject(env, notification.conn)};
|
||||
return env->NewObject(connectionNotificationCls, constructor, inst,
|
||||
static_cast<jint>(notification.listener),
|
||||
static_cast<jboolean>(notification.connected),
|
||||
conn.obj());
|
||||
}
|
||||
|
||||
static jobject MakeJObject(JNIEnv* env, jobject inst,
|
||||
const nt::LogMessage& msg) {
|
||||
static jmethodID constructor = env->GetMethodID(
|
||||
logMessageCls, "<init>",
|
||||
"(Ledu/wpi/first/networktables/NetworkTableInstance;IILjava/lang/"
|
||||
"String;ILjava/lang/String;)V");
|
||||
logMessageCls, "<init>", "(ILjava/lang/String;ILjava/lang/String;)V");
|
||||
JLocal<jstring> filename{env, MakeJString(env, msg.filename)};
|
||||
JLocal<jstring> message{env, MakeJString(env, msg.message)};
|
||||
return env->NewObject(logMessageCls, constructor, inst,
|
||||
static_cast<jint>(msg.logger),
|
||||
return env->NewObject(logMessageCls, constructor,
|
||||
static_cast<jint>(msg.level), filename.obj(),
|
||||
static_cast<jint>(msg.line), message.obj());
|
||||
}
|
||||
@@ -257,28 +237,42 @@ static jobject MakeJObject(JNIEnv* env, jobject inst,
|
||||
}
|
||||
|
||||
static jobject MakeJObject(JNIEnv* env, jobject inst,
|
||||
const nt::TopicNotification& notification) {
|
||||
const nt::ValueEventData& data) {
|
||||
static jmethodID constructor =
|
||||
env->GetMethodID(topicNotificationCls, "<init>",
|
||||
"(ILedu/wpi/first/networktables/TopicInfo;I)V");
|
||||
JLocal<jobject> info{env, MakeJObject(env, inst, notification.info)};
|
||||
return env->NewObject(topicNotificationCls, constructor,
|
||||
static_cast<jint>(notification.listener), info.obj(),
|
||||
static_cast<jint>(notification.flags));
|
||||
env->GetMethodID(valueEventDataCls, "<init>",
|
||||
"(Ledu/wpi/first/networktables/NetworkTableInstance;II"
|
||||
"Ledu/wpi/first/networktables/NetworkTableValue;)V");
|
||||
JLocal<jobject> value{env, MakeJValue(env, data.value)};
|
||||
return env->NewObject(valueEventDataCls, constructor, inst,
|
||||
static_cast<jint>(data.topic),
|
||||
static_cast<jint>(data.subentry), value.obj());
|
||||
}
|
||||
|
||||
static jobject MakeJObject(JNIEnv* env, jobject inst,
|
||||
const nt::ValueNotification& notification) {
|
||||
static jobject MakeJObject(JNIEnv* env, jobject inst, const nt::Event& event) {
|
||||
static jmethodID constructor =
|
||||
env->GetMethodID(valueNotificationCls, "<init>",
|
||||
"(Ledu/wpi/first/networktables/NetworkTableInstance;III"
|
||||
"Ledu/wpi/first/networktables/NetworkTableValue;I)V");
|
||||
JLocal<jobject> value{env, MakeJValue(env, notification.value)};
|
||||
return env->NewObject(valueNotificationCls, constructor, inst,
|
||||
static_cast<jint>(notification.listener),
|
||||
static_cast<jint>(notification.topic),
|
||||
static_cast<jint>(notification.subentry), value.obj(),
|
||||
static_cast<jint>(notification.flags));
|
||||
env->GetMethodID(eventCls, "<init>",
|
||||
"(Ledu/wpi/first/networktables/NetworkTableInstance;II"
|
||||
"Ledu/wpi/first/networktables/ConnectionInfo;"
|
||||
"Ledu/wpi/first/networktables/TopicInfo;"
|
||||
"Ledu/wpi/first/networktables/ValueEventData;"
|
||||
"Ledu/wpi/first/networktables/LogMessage;)V");
|
||||
JLocal<jobject> connInfo{env, nullptr};
|
||||
JLocal<jobject> topicInfo{env, nullptr};
|
||||
JLocal<jobject> valueData{env, nullptr};
|
||||
JLocal<jobject> logMessage{env, nullptr};
|
||||
if (auto v = event.GetConnectionInfo()) {
|
||||
connInfo = JLocal<jobject>{env, MakeJObject(env, *v)};
|
||||
} else if (auto v = event.GetTopicInfo()) {
|
||||
topicInfo = JLocal<jobject>{env, MakeJObject(env, inst, *v)};
|
||||
} else if (auto v = event.GetValueEventData()) {
|
||||
valueData = JLocal<jobject>{env, MakeJObject(env, inst, *v)};
|
||||
} else if (auto v = event.GetLogMessage()) {
|
||||
logMessage = JLocal<jobject>{env, MakeJObject(env, *v)};
|
||||
}
|
||||
return env->NewObject(eventCls, constructor, inst,
|
||||
static_cast<jint>(event.listener),
|
||||
static_cast<jint>(event.flags), connInfo.obj(),
|
||||
topicInfo.obj(), valueData.obj(), logMessage.obj());
|
||||
}
|
||||
|
||||
static jobjectArray MakeJObject(JNIEnv* env, std::span<const nt::Value> arr) {
|
||||
@@ -293,52 +287,9 @@ static jobjectArray MakeJObject(JNIEnv* env, std::span<const nt::Value> arr) {
|
||||
return jarr;
|
||||
}
|
||||
|
||||
static jobjectArray MakeJObject(
|
||||
JNIEnv* env, jobject inst,
|
||||
std::span<const nt::ConnectionNotification> arr) {
|
||||
jobjectArray jarr =
|
||||
env->NewObjectArray(arr.size(), connectionNotificationCls, nullptr);
|
||||
if (!jarr) {
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < arr.size(); ++i) {
|
||||
JLocal<jobject> elem{env, MakeJObject(env, inst, arr[i])};
|
||||
env->SetObjectArrayElement(jarr, i, elem.obj());
|
||||
}
|
||||
return jarr;
|
||||
}
|
||||
|
||||
static jobjectArray MakeJObject(JNIEnv* env, jobject inst,
|
||||
std::span<const nt::LogMessage> arr) {
|
||||
jobjectArray jarr = env->NewObjectArray(arr.size(), logMessageCls, nullptr);
|
||||
if (!jarr) {
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < arr.size(); ++i) {
|
||||
JLocal<jobject> elem{env, MakeJObject(env, inst, arr[i])};
|
||||
env->SetObjectArrayElement(jarr, i, elem.obj());
|
||||
}
|
||||
return jarr;
|
||||
}
|
||||
|
||||
static jobjectArray MakeJObject(JNIEnv* env, jobject inst,
|
||||
std::span<const nt::TopicNotification> arr) {
|
||||
jobjectArray jarr =
|
||||
env->NewObjectArray(arr.size(), topicNotificationCls, nullptr);
|
||||
if (!jarr) {
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < arr.size(); ++i) {
|
||||
JLocal<jobject> elem{env, MakeJObject(env, inst, arr[i])};
|
||||
env->SetObjectArrayElement(jarr, i, elem.obj());
|
||||
}
|
||||
return jarr;
|
||||
}
|
||||
|
||||
static jobjectArray MakeJObject(JNIEnv* env, jobject inst,
|
||||
std::span<const nt::ValueNotification> arr) {
|
||||
jobjectArray jarr =
|
||||
env->NewObjectArray(arr.size(), valueNotificationCls, nullptr);
|
||||
std::span<const nt::Event> arr) {
|
||||
jobjectArray jarr = env->NewObjectArray(arr.size(), eventCls, nullptr);
|
||||
if (!jarr) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1013,35 +964,35 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_getTopicInfo
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: createTopicListenerPoller
|
||||
* Method: createListenerPoller
|
||||
* Signature: (I)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_createTopicListenerPoller
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_createListenerPoller
|
||||
(JNIEnv*, jclass, jint inst)
|
||||
{
|
||||
return nt::CreateTopicListenerPoller(inst);
|
||||
return nt::CreateListenerPoller(inst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: destroyTopicListenerPoller
|
||||
* Method: destroyListenerPoller
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_destroyTopicListenerPoller
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_destroyListenerPoller
|
||||
(JNIEnv*, jclass, jint poller)
|
||||
{
|
||||
nt::DestroyTopicListenerPoller(poller);
|
||||
nt::DestroyListenerPoller(poller);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: addPolledTopicListener
|
||||
* Method: addListener
|
||||
* Signature: (I[Ljava/lang/Object;I)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_addPolledTopicListener__I_3Ljava_lang_String_2I
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_addListener__I_3Ljava_lang_String_2I
|
||||
(JNIEnv* env, jclass, jint poller, jobjectArray prefixes, jint flags)
|
||||
{
|
||||
if (!prefixes) {
|
||||
@@ -1066,163 +1017,43 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_addPolledTopicListener__I_3Lja
|
||||
arrview.emplace_back(arr.back());
|
||||
}
|
||||
|
||||
return nt::AddPolledTopicListener(poller, arrview, flags);
|
||||
return nt::AddPolledListener(poller, arrview, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: addPolledTopicListener
|
||||
* Method: addListener
|
||||
* Signature: (III)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_addPolledTopicListener__III
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_addListener__III
|
||||
(JNIEnv* env, jclass, jint poller, jint handle, jint flags)
|
||||
{
|
||||
return nt::AddPolledTopicListener(poller, handle, flags);
|
||||
return nt::AddPolledListener(poller, handle, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: readTopicListenerQueue
|
||||
* Method: readListenerQueue
|
||||
* Signature: (Ljava/lang/Object;I)[Ljava/lang/Object;
|
||||
*/
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_readTopicListenerQueue
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_readListenerQueue
|
||||
(JNIEnv* env, jclass, jobject inst, jint poller)
|
||||
{
|
||||
return MakeJObject(env, inst, nt::ReadTopicListenerQueue(poller));
|
||||
return MakeJObject(env, inst, nt::ReadListenerQueue(poller));
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: removeTopicListener
|
||||
* Method: removeListener
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_removeTopicListener
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_removeListener
|
||||
(JNIEnv*, jclass, jint topicListener)
|
||||
{
|
||||
nt::RemoveTopicListener(topicListener);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: createValueListenerPoller
|
||||
* Signature: (I)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_createValueListenerPoller
|
||||
(JNIEnv*, jclass, jint inst)
|
||||
{
|
||||
return nt::CreateValueListenerPoller(inst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: destroyValueListenerPoller
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_destroyValueListenerPoller
|
||||
(JNIEnv*, jclass, jint poller)
|
||||
{
|
||||
nt::DestroyValueListenerPoller(poller);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: addPolledValueListener
|
||||
* Signature: (III)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_addPolledValueListener
|
||||
(JNIEnv* env, jclass, jint poller, jint topic, jint flags)
|
||||
{
|
||||
return nt::AddPolledValueListener(poller, topic, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: readValueListenerQueue
|
||||
* Signature: (Ljava/lang/Object;I)[Ljava/lang/Object;
|
||||
*/
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_readValueListenerQueue
|
||||
(JNIEnv* env, jclass, jobject inst, jint poller)
|
||||
{
|
||||
return MakeJObject(env, inst, nt::ReadValueListenerQueue(poller));
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: removeValueListener
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_removeValueListener
|
||||
(JNIEnv*, jclass, jint topicListener)
|
||||
{
|
||||
nt::RemoveValueListener(topicListener);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: createConnectionListenerPoller
|
||||
* Signature: (I)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_createConnectionListenerPoller
|
||||
(JNIEnv*, jclass, jint inst)
|
||||
{
|
||||
return nt::CreateConnectionListenerPoller(inst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: destroyConnectionListenerPoller
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_destroyConnectionListenerPoller
|
||||
(JNIEnv*, jclass, jint poller)
|
||||
{
|
||||
nt::DestroyConnectionListenerPoller(poller);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: addPolledConnectionListener
|
||||
* Signature: (IZ)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_addPolledConnectionListener
|
||||
(JNIEnv* env, jclass, jint poller, jboolean immediateNotify)
|
||||
{
|
||||
return nt::AddPolledConnectionListener(poller, immediateNotify);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: readConnectionListenerQueue
|
||||
* Signature: (Ljava/lang/Object;I)[Ljava/lang/Object;
|
||||
*/
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_readConnectionListenerQueue
|
||||
(JNIEnv* env, jclass, jobject inst, jint poller)
|
||||
{
|
||||
return MakeJObject(env, inst, nt::ReadConnectionListenerQueue(poller));
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: removeConnectionListener
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_removeConnectionListener
|
||||
(JNIEnv*, jclass, jint connListenerUid)
|
||||
{
|
||||
nt::RemoveConnectionListener(connListenerUid);
|
||||
nt::RemoveListener(topicListener);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1561,62 +1392,14 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_stopConnectionDataLog
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: createLoggerPoller
|
||||
* Signature: (I)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_createLoggerPoller
|
||||
(JNIEnv*, jclass, jint inst)
|
||||
{
|
||||
return nt::CreateLoggerPoller(inst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: destroyLoggerPoller
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_destroyLoggerPoller
|
||||
(JNIEnv*, jclass, jint poller)
|
||||
{
|
||||
nt::DestroyLoggerPoller(poller);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: addPolledLogger
|
||||
* Method: addLogger
|
||||
* Signature: (III)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_addPolledLogger
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_addLogger
|
||||
(JNIEnv*, jclass, jint poller, jint minLevel, jint maxLevel)
|
||||
{
|
||||
return nt::AddPolledLogger(poller, minLevel, maxLevel);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: readLoggerQueue
|
||||
* Signature: (Ljava/lang/Object;I)[Ljava/lang/Object;
|
||||
*/
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_readLoggerQueue
|
||||
(JNIEnv* env, jclass, jobject inst, jint poller)
|
||||
{
|
||||
return MakeJObject(env, inst, nt::ReadLoggerQueue(poller));
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: removeLogger
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_removeLogger
|
||||
(JNIEnv*, jclass, jint logger)
|
||||
{
|
||||
nt::RemoveLogger(logger);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -101,16 +101,9 @@ void NetworkTableInstance::SetServer(std::span<const std::string_view> servers,
|
||||
SetServer(serversArr);
|
||||
}
|
||||
|
||||
NT_TopicListener NetworkTableInstance::AddTopicListener(
|
||||
MultiSubscriber& subscriber, int eventMask,
|
||||
std::function<void(const TopicNotification&)> listener) {
|
||||
return ::nt::AddTopicListener(subscriber.GetHandle(), eventMask,
|
||||
std::move(listener));
|
||||
}
|
||||
|
||||
NT_ValueListener NetworkTableInstance::AddValueListener(
|
||||
MultiSubscriber& subscriber, unsigned int eventMask,
|
||||
std::function<void(const ValueNotification&)> listener) {
|
||||
return ::nt::AddValueListener(subscriber.GetHandle(), eventMask,
|
||||
std::move(listener));
|
||||
NT_Listener NetworkTableInstance::AddListener(MultiSubscriber& subscriber,
|
||||
int eventMask,
|
||||
ListenerCallback listener) {
|
||||
return ::nt::AddListener(subscriber.GetHandle(), eventMask,
|
||||
std::move(listener));
|
||||
}
|
||||
|
||||
@@ -37,35 +37,42 @@ static void ConvertToC(const ConnectionInfo& in, NT_ConnectionInfo* out) {
|
||||
out->protocol_version = in.protocol_version;
|
||||
}
|
||||
|
||||
static void ConvertToC(const TopicNotification& in, NT_TopicNotification* out) {
|
||||
out->listener = in.listener;
|
||||
ConvertToC(in.info, &out->info);
|
||||
out->flags = in.flags;
|
||||
}
|
||||
|
||||
static void ConvertToC(const ValueNotification& in, NT_ValueNotification* out) {
|
||||
out->listener = in.listener;
|
||||
static void ConvertToC(const ValueEventData& in, NT_ValueEventData* out) {
|
||||
out->topic = in.topic;
|
||||
out->subentry = in.subentry;
|
||||
ConvertToC(in.value, &out->value);
|
||||
out->flags = in.flags;
|
||||
}
|
||||
|
||||
static void ConvertToC(const ConnectionNotification& in,
|
||||
NT_ConnectionNotification* out) {
|
||||
out->listener = in.listener;
|
||||
out->connected = in.connected;
|
||||
ConvertToC(in.conn, &out->conn);
|
||||
}
|
||||
|
||||
static void ConvertToC(const LogMessage& in, NT_LogMessage* out) {
|
||||
out->logger = in.logger;
|
||||
out->level = in.level;
|
||||
ConvertToC(in.filename, &out->filename);
|
||||
out->line = in.line;
|
||||
ConvertToC(in.message, &out->message);
|
||||
}
|
||||
|
||||
static void ConvertToC(const Event& in, NT_Event* out) {
|
||||
out->listener = in.listener;
|
||||
out->flags = in.flags;
|
||||
if ((in.flags & NT_EVENT_VALUE_ALL) != 0) {
|
||||
if (auto v = in.GetValueEventData()) {
|
||||
return ConvertToC(*v, &out->data.valueData);
|
||||
}
|
||||
} else if ((in.flags & NT_EVENT_TOPIC) != 0) {
|
||||
if (auto v = in.GetTopicInfo()) {
|
||||
return ConvertToC(*v, &out->data.topicInfo);
|
||||
}
|
||||
} else if ((in.flags & NT_EVENT_CONNECTION) != 0) {
|
||||
if (auto v = in.GetConnectionInfo()) {
|
||||
return ConvertToC(*v, &out->data.connInfo);
|
||||
}
|
||||
} else if ((in.flags & NT_EVENT_LOGMESSAGE) != 0) {
|
||||
if (auto v = in.GetLogMessage()) {
|
||||
return ConvertToC(*v, &out->data.logMessage);
|
||||
}
|
||||
}
|
||||
out->flags = NT_EVENT_NONE; // sanity to make sure we don't dispose
|
||||
}
|
||||
|
||||
static void DisposeConnectionInfo(NT_ConnectionInfo* info) {
|
||||
std::free(info->remote_id.str);
|
||||
std::free(info->remote_ip.str);
|
||||
@@ -77,16 +84,21 @@ static void DisposeTopicInfo(NT_TopicInfo* info) {
|
||||
std::free(info->properties.str);
|
||||
}
|
||||
|
||||
static void DisposeTopicNotification(NT_TopicNotification* info) {
|
||||
DisposeTopicInfo(&info->info);
|
||||
static void DisposeLogMessage(NT_LogMessage* msg) {
|
||||
std::free(msg->filename);
|
||||
std::free(msg->message);
|
||||
}
|
||||
|
||||
static void DisposeValueNotification(NT_ValueNotification* info) {
|
||||
NT_DisposeValue(&info->value);
|
||||
}
|
||||
|
||||
static void DisposeConnectionNotification(NT_ConnectionNotification* info) {
|
||||
DisposeConnectionInfo(&info->conn);
|
||||
static void DisposeEvent(NT_Event* event) {
|
||||
if ((event->flags & NT_EVENT_VALUE_ALL) != 0) {
|
||||
NT_DisposeValue(&event->data.valueData.value);
|
||||
} else if ((event->flags & NT_EVENT_TOPIC) != 0) {
|
||||
DisposeTopicInfo(&event->data.topicInfo);
|
||||
} else if ((event->flags & NT_EVENT_CONNECTION) != 0) {
|
||||
DisposeConnectionInfo(&event->data.connInfo);
|
||||
} else if ((event->flags & NT_EVENT_LOGMESSAGE) != 0) {
|
||||
DisposeLogMessage(&event->data.logMessage);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
@@ -383,169 +395,87 @@ NT_Topic NT_GetTopicFromHandle(NT_Handle pubsubentry) {
|
||||
* Callback Creation Functions
|
||||
*/
|
||||
|
||||
NT_TopicListener NT_AddTopicListener(NT_Inst inst, const char* prefix,
|
||||
size_t prefix_len, unsigned int mask,
|
||||
void* data,
|
||||
NT_TopicListenerCallback callback) {
|
||||
NT_ListenerPoller NT_CreateListenerPoller(NT_Inst inst) {
|
||||
return nt::CreateListenerPoller(inst);
|
||||
}
|
||||
|
||||
void NT_DestroyListenerPoller(NT_ListenerPoller poller) {
|
||||
nt::DestroyListenerPoller(poller);
|
||||
}
|
||||
|
||||
struct NT_Event* NT_ReadListenerQueue(NT_ListenerPoller poller, size_t* len) {
|
||||
auto arr_cpp = nt::ReadListenerQueue(poller);
|
||||
return ConvertToC<NT_Event>(arr_cpp, len);
|
||||
}
|
||||
|
||||
void NT_RemoveListener(NT_Listener listener) {
|
||||
nt::RemoveListener(listener);
|
||||
}
|
||||
|
||||
NT_Bool NT_WaitForListenerQueue(NT_Handle handle, double timeout) {
|
||||
return nt::WaitForListenerQueue(handle, timeout);
|
||||
}
|
||||
|
||||
NT_Listener NT_AddListenerSingle(NT_Inst inst, const char* prefix,
|
||||
size_t prefix_len, unsigned int mask,
|
||||
void* data, NT_ListenerCallback callback) {
|
||||
std::string_view p{prefix, prefix_len};
|
||||
return nt::AddTopicListener(inst, {{p}}, mask, [=](auto& event) {
|
||||
NT_TopicNotification event_c;
|
||||
return nt::AddListener(inst, {{p}}, mask, [=](auto& event) {
|
||||
NT_Event event_c;
|
||||
ConvertToC(event, &event_c);
|
||||
callback(data, &event_c);
|
||||
DisposeTopicNotification(&event_c);
|
||||
DisposeEvent(&event_c);
|
||||
});
|
||||
}
|
||||
|
||||
NT_TopicListener NT_AddTopicListenerMultiple(
|
||||
NT_Inst inst, const NT_String* prefixes, size_t prefixes_len,
|
||||
unsigned int mask, void* data, NT_TopicListenerCallback callback) {
|
||||
NT_Listener NT_AddListenerMultiple(NT_Inst inst, const NT_String* prefixes,
|
||||
size_t prefixes_len, unsigned int mask,
|
||||
void* data, NT_ListenerCallback callback) {
|
||||
wpi::SmallVector<std::string_view, 8> p;
|
||||
p.reserve(prefixes_len);
|
||||
for (size_t i = 0; i < prefixes_len; ++i) {
|
||||
p.emplace_back(prefixes[i].str, prefixes[i].len);
|
||||
}
|
||||
return nt::AddTopicListener(inst, p, mask, [=](auto& event) {
|
||||
NT_TopicNotification event_c;
|
||||
return nt::AddListener(inst, p, mask, [=](auto& event) {
|
||||
NT_Event event_c;
|
||||
ConvertToC(event, &event_c);
|
||||
callback(data, &event_c);
|
||||
DisposeTopicNotification(&event_c);
|
||||
DisposeEvent(&event_c);
|
||||
});
|
||||
}
|
||||
|
||||
NT_TopicListener NT_AddTopicListenerSingle(NT_Topic topic, unsigned int mask,
|
||||
void* data,
|
||||
NT_TopicListenerCallback callback) {
|
||||
return nt::AddTopicListener(topic, mask, [=](auto& event) {
|
||||
NT_TopicNotification event_c;
|
||||
NT_Listener NT_AddListener(NT_Topic topic, unsigned int mask, void* data,
|
||||
NT_ListenerCallback callback) {
|
||||
return nt::AddListener(topic, mask, [=](auto& event) {
|
||||
NT_Event event_c;
|
||||
ConvertToC(event, &event_c);
|
||||
callback(data, &event_c);
|
||||
DisposeTopicNotification(&event_c);
|
||||
DisposeEvent(&event_c);
|
||||
});
|
||||
}
|
||||
|
||||
NT_Bool NT_WaitForTopicListenerQueue(NT_Handle handle, double timeout) {
|
||||
return nt::WaitForTopicListenerQueue(handle, timeout);
|
||||
}
|
||||
|
||||
NT_TopicListenerPoller NT_CreateTopicListenerPoller(NT_Inst inst) {
|
||||
return nt::CreateTopicListenerPoller(inst);
|
||||
}
|
||||
|
||||
void NT_DestroyTopicListenerPoller(NT_TopicListenerPoller poller) {
|
||||
nt::DestroyTopicListenerPoller(poller);
|
||||
}
|
||||
|
||||
NT_TopicListener NT_AddPolledTopicListener(NT_TopicListenerPoller poller,
|
||||
const char* prefix,
|
||||
size_t prefix_len,
|
||||
unsigned int mask) {
|
||||
NT_Listener NT_AddPolledListenerSingle(NT_ListenerPoller poller,
|
||||
const char* prefix, size_t prefix_len,
|
||||
unsigned int mask) {
|
||||
std::string_view p{prefix, prefix_len};
|
||||
return nt::AddPolledTopicListener(poller, {{p}}, mask);
|
||||
return nt::AddPolledListener(poller, {{p}}, mask);
|
||||
}
|
||||
|
||||
NT_TopicListener NT_AddPolledTopicListenerMultiple(
|
||||
NT_TopicListenerPoller poller, const NT_String* prefixes,
|
||||
size_t prefixes_len, unsigned int mask) {
|
||||
NT_Listener NT_AddPolledListenerMultiple(NT_ListenerPoller poller,
|
||||
const NT_String* prefixes,
|
||||
size_t prefixes_len,
|
||||
unsigned int mask) {
|
||||
wpi::SmallVector<std::string_view, 8> p;
|
||||
p.reserve(prefixes_len);
|
||||
for (size_t i = 0; i < prefixes_len; ++i) {
|
||||
p.emplace_back(prefixes[i].str, prefixes[i].len);
|
||||
}
|
||||
return nt::AddPolledTopicListener(poller, p, mask);
|
||||
return nt::AddPolledListener(poller, p, mask);
|
||||
}
|
||||
|
||||
NT_TopicListener NT_AddPolledTopicListenerSingle(NT_TopicListenerPoller poller,
|
||||
NT_Topic topic,
|
||||
unsigned int mask) {
|
||||
return nt::AddPolledTopicListener(poller, topic, mask);
|
||||
}
|
||||
|
||||
struct NT_TopicNotification* NT_ReadTopicListenerQueue(
|
||||
NT_TopicListenerPoller poller, size_t* len) {
|
||||
auto arr_cpp = nt::ReadTopicListenerQueue(poller);
|
||||
return ConvertToC<NT_TopicNotification>(arr_cpp, len);
|
||||
}
|
||||
|
||||
void NT_RemoveTopicListener(NT_TopicListener topic_listener) {
|
||||
nt::RemoveTopicListener(topic_listener);
|
||||
}
|
||||
|
||||
NT_ValueListener NT_AddValueListener(NT_Handle subentry, unsigned int mask,
|
||||
void* data,
|
||||
NT_ValueListenerCallback callback) {
|
||||
return nt::AddValueListener(subentry, mask, [=](auto& event) {
|
||||
NT_ValueNotification event_c;
|
||||
ConvertToC(event, &event_c);
|
||||
callback(data, &event_c);
|
||||
DisposeValueNotification(&event_c);
|
||||
});
|
||||
}
|
||||
|
||||
NT_Bool NT_WaitForValueListenerQueue(NT_Handle handle, double timeout) {
|
||||
return nt::WaitForValueListenerQueue(handle, timeout);
|
||||
}
|
||||
|
||||
NT_ValueListenerPoller NT_CreateValueListenerPoller(NT_Inst inst) {
|
||||
return nt::CreateValueListenerPoller(inst);
|
||||
}
|
||||
|
||||
void NT_DestroyValueListenerPoller(NT_ValueListenerPoller poller) {
|
||||
nt::DestroyValueListenerPoller(poller);
|
||||
}
|
||||
|
||||
NT_ValueListener NT_AddPolledValueListener(NT_ValueListenerPoller poller,
|
||||
NT_Handle subentry,
|
||||
unsigned int mask) {
|
||||
return nt::AddPolledValueListener(poller, subentry, mask);
|
||||
}
|
||||
|
||||
struct NT_ValueNotification* NT_ReadValueListenerQueue(
|
||||
NT_ValueListenerPoller poller, size_t* len) {
|
||||
auto arr_cpp = nt::ReadValueListenerQueue(poller);
|
||||
return ConvertToC<NT_ValueNotification>(arr_cpp, len);
|
||||
}
|
||||
|
||||
void NT_RemoveValueListener(NT_ValueListener value_listener) {
|
||||
nt::RemoveValueListener(value_listener);
|
||||
}
|
||||
|
||||
NT_ConnectionListener NT_AddConnectionListener(
|
||||
NT_Inst inst, NT_Bool immediate_notify, void* data,
|
||||
NT_ConnectionListenerCallback callback) {
|
||||
return nt::AddConnectionListener(inst, immediate_notify != 0,
|
||||
[=](const ConnectionNotification& event) {
|
||||
NT_ConnectionNotification event_c;
|
||||
ConvertToC(event, &event_c);
|
||||
callback(data, &event_c);
|
||||
DisposeConnectionNotification(&event_c);
|
||||
});
|
||||
}
|
||||
|
||||
NT_Bool NT_WaitForConnectionListenerQueue(NT_Handle handle, double timeout) {
|
||||
return nt::WaitForConnectionListenerQueue(handle, timeout);
|
||||
}
|
||||
|
||||
NT_ConnectionListenerPoller NT_CreateConnectionListenerPoller(NT_Inst inst) {
|
||||
return nt::CreateConnectionListenerPoller(inst);
|
||||
}
|
||||
|
||||
void NT_DestroyConnectionListenerPoller(NT_ConnectionListenerPoller poller) {
|
||||
nt::DestroyConnectionListenerPoller(poller);
|
||||
}
|
||||
|
||||
NT_ConnectionListener NT_AddPolledConnectionListener(
|
||||
NT_ConnectionListenerPoller poller, NT_Bool immediate_notify) {
|
||||
return nt::AddPolledConnectionListener(poller, immediate_notify);
|
||||
}
|
||||
|
||||
struct NT_ConnectionNotification* NT_ReadConnectionListenerQueue(
|
||||
NT_ConnectionListenerPoller poller, size_t* len) {
|
||||
auto arr_cpp = nt::ReadConnectionListenerQueue(poller);
|
||||
return ConvertToC<NT_ConnectionNotification>(arr_cpp, len);
|
||||
}
|
||||
|
||||
void NT_RemoveConnectionListener(NT_ConnectionListener conn_listener) {
|
||||
nt::RemoveConnectionListener(conn_listener);
|
||||
NT_Listener NT_AddPolledListener(NT_ListenerPoller poller, NT_Topic topic,
|
||||
unsigned int mask) {
|
||||
return nt::AddPolledListener(poller, topic, mask);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -641,41 +571,22 @@ void NT_SetNow(uint64_t timestamp) {
|
||||
nt::SetNow(timestamp);
|
||||
}
|
||||
|
||||
NT_Logger NT_AddLogger(NT_Inst inst, void* data, NT_LogFunc func,
|
||||
unsigned int min_level, unsigned int max_level) {
|
||||
return nt::AddLogger(
|
||||
inst,
|
||||
[=](const LogMessage& msg) {
|
||||
NT_LogMessage msg_c;
|
||||
ConvertToC(msg, &msg_c);
|
||||
func(data, &msg_c);
|
||||
NT_DisposeLogMessage(&msg_c);
|
||||
},
|
||||
min_level, max_level);
|
||||
NT_Listener NT_AddLogger(NT_Inst inst, unsigned int min_level,
|
||||
unsigned int max_level, void* data,
|
||||
NT_ListenerCallback func) {
|
||||
return nt::AddLogger(inst, min_level, max_level, [=](auto& event) {
|
||||
NT_Event event_c;
|
||||
ConvertToC(event, &event_c);
|
||||
func(data, &event_c);
|
||||
NT_DisposeEvent(&event_c);
|
||||
});
|
||||
}
|
||||
|
||||
NT_LoggerPoller NT_CreateLoggerPoller(NT_Inst inst) {
|
||||
return nt::CreateLoggerPoller(inst);
|
||||
}
|
||||
|
||||
void NT_DestroyLoggerPoller(NT_LoggerPoller poller) {
|
||||
nt::DestroyLoggerPoller(poller);
|
||||
}
|
||||
|
||||
NT_Logger NT_AddPolledLogger(NT_LoggerPoller poller, unsigned int min_level,
|
||||
unsigned int max_level) {
|
||||
NT_Listener NT_AddPolledLogger(NT_ListenerPoller poller, unsigned int min_level,
|
||||
unsigned int max_level) {
|
||||
return nt::AddPolledLogger(poller, min_level, max_level);
|
||||
}
|
||||
|
||||
struct NT_LogMessage* NT_ReadLoggerQueue(NT_LoggerPoller poller, size_t* len) {
|
||||
auto arr_cpp = nt::ReadLoggerQueue(poller);
|
||||
return ConvertToC<NT_LogMessage>(arr_cpp, len);
|
||||
}
|
||||
|
||||
void NT_RemoveLogger(NT_Logger logger) {
|
||||
nt::RemoveLogger(logger);
|
||||
}
|
||||
|
||||
void NT_DisposeValue(NT_Value* value) {
|
||||
switch (value->type) {
|
||||
case NT_UNASSIGNED:
|
||||
@@ -759,50 +670,15 @@ void NT_DisposeTopicInfo(NT_TopicInfo* info) {
|
||||
DisposeTopicInfo(info);
|
||||
}
|
||||
|
||||
void NT_DisposeTopicNotificationArray(NT_TopicNotification* arr, size_t count) {
|
||||
void NT_DisposeEventArray(NT_Event* arr, size_t count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
DisposeTopicNotification(&arr[i]);
|
||||
DisposeEvent(&arr[i]);
|
||||
}
|
||||
std::free(arr);
|
||||
}
|
||||
|
||||
void NT_DisposeTopicNotification(NT_TopicNotification* info) {
|
||||
DisposeTopicNotification(info);
|
||||
}
|
||||
|
||||
void NT_DisposeValueNotificationArray(NT_ValueNotification* arr, size_t count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
DisposeValueNotification(&arr[i]);
|
||||
}
|
||||
std::free(arr);
|
||||
}
|
||||
|
||||
void NT_DisposeValueNotification(NT_ValueNotification* info) {
|
||||
DisposeValueNotification(info);
|
||||
}
|
||||
|
||||
void NT_DisposeConnectionNotificationArray(NT_ConnectionNotification* arr,
|
||||
size_t count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
DisposeConnectionNotification(&arr[i]);
|
||||
}
|
||||
std::free(arr);
|
||||
}
|
||||
|
||||
void NT_DisposeConnectionNotification(NT_ConnectionNotification* info) {
|
||||
DisposeConnectionNotification(info);
|
||||
}
|
||||
|
||||
void NT_DisposeLogMessageArray(NT_LogMessage* arr, size_t count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
NT_DisposeLogMessage(&arr[i]);
|
||||
}
|
||||
std::free(arr);
|
||||
}
|
||||
|
||||
void NT_DisposeLogMessage(NT_LogMessage* info) {
|
||||
std::free(info->filename);
|
||||
std::free(info->message);
|
||||
void NT_DisposeEvent(NT_Event* event) {
|
||||
DisposeEvent(event);
|
||||
}
|
||||
|
||||
/* Interop Utility Functions */
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <wpi/json.h>
|
||||
#include <wpi/timestamp.h>
|
||||
|
||||
@@ -17,6 +18,7 @@
|
||||
#include "Log.h"
|
||||
#include "Types_internal.h"
|
||||
#include "ntcore.h"
|
||||
#include "ntcore_c.h"
|
||||
|
||||
static std::atomic_bool gNowSet{false};
|
||||
static std::atomic<int64_t> gNowTime;
|
||||
@@ -54,7 +56,7 @@ void DestroyInstance(NT_Inst inst) {
|
||||
NT_Inst GetInstanceFromHandle(NT_Handle handle) {
|
||||
Handle h{handle};
|
||||
auto type = h.GetType();
|
||||
if (type >= Handle::kConnectionListener && type < Handle::kTypeMax) {
|
||||
if (type >= Handle::kListener && type < Handle::kTypeMax) {
|
||||
return Handle(h.GetInst(), 0, Handle::kInstance);
|
||||
}
|
||||
|
||||
@@ -386,200 +388,123 @@ void UnsubscribeMultiple(NT_MultiSubscriber sub) {
|
||||
* Callback Creation Functions
|
||||
*/
|
||||
|
||||
NT_TopicListener AddTopicListener(
|
||||
NT_Inst inst, std::span<const std::string_view> prefixes, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> callback) {
|
||||
static void CleanupListeners(
|
||||
InstanceImpl& ii,
|
||||
std::span<const std::pair<NT_Listener, unsigned int>> listeners) {
|
||||
bool updateMinLevel = false;
|
||||
for (auto&& [listener, mask] : listeners) {
|
||||
// connection doesn't need removal notification
|
||||
if ((mask & (NT_EVENT_TOPIC | NT_EVENT_VALUE_ALL)) != 0) {
|
||||
ii.localStorage.RemoveListener(listener, mask);
|
||||
}
|
||||
if ((mask & NT_EVENT_LOGMESSAGE) != 0) {
|
||||
ii.logger_impl.RemoveListener(listener);
|
||||
updateMinLevel = true;
|
||||
}
|
||||
}
|
||||
if (updateMinLevel) {
|
||||
ii.logger.set_min_level(ii.logger_impl.GetMinLevel());
|
||||
}
|
||||
}
|
||||
|
||||
static void DoAddListener(InstanceImpl& ii, NT_Listener listener,
|
||||
NT_Handle handle, unsigned int mask) {
|
||||
if (Handle{handle}.IsType(Handle::kInstance)) {
|
||||
if ((mask & NT_EVENT_CONNECTION) != 0) {
|
||||
ii.connectionList.AddListener(listener, mask);
|
||||
}
|
||||
if ((mask & NT_EVENT_LOGMESSAGE) != 0) {
|
||||
ii.logger_impl.AddListener(listener, NT_LOG_INFO, UINT_MAX);
|
||||
}
|
||||
} else if ((mask & (NT_EVENT_TOPIC | NT_EVENT_VALUE_ALL)) != 0) {
|
||||
ii.localStorage.AddListener(listener, handle, mask);
|
||||
}
|
||||
}
|
||||
|
||||
NT_ListenerPoller CreateListenerPoller(NT_Inst inst) {
|
||||
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
|
||||
return ii->localStorage.AddTopicListener(prefixes, mask,
|
||||
std::move(callback));
|
||||
return ii->listenerStorage.CreateListenerPoller();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
NT_TopicListener AddTopicListener(
|
||||
NT_Handle handle, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> callback) {
|
||||
if (auto ii = InstanceImpl::GetTyped(handle, Handle::kTopic)) {
|
||||
return ii->localStorage.AddTopicListener(handle, mask, std::move(callback));
|
||||
void DestroyListenerPoller(NT_ListenerPoller poller) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kListenerPoller)) {
|
||||
CleanupListeners(*ii, ii->listenerStorage.DestroyListenerPoller(poller));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Event> ReadListenerQueue(NT_ListenerPoller poller) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kListenerPoller)) {
|
||||
return ii->listenerStorage.ReadListenerQueue(poller);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
bool WaitForTopicListenerQueue(NT_Handle handle, double timeout) {
|
||||
void RemoveListener(NT_Listener listener) {
|
||||
if (auto ii = InstanceImpl::GetTyped(listener, Handle::kListener)) {
|
||||
CleanupListeners(*ii, ii->listenerStorage.RemoveListener(listener));
|
||||
}
|
||||
}
|
||||
|
||||
bool WaitForListenerQueue(NT_Handle handle, double timeout) {
|
||||
if (auto ii = InstanceImpl::GetHandle(handle)) {
|
||||
return ii->localStorage.WaitForTopicListenerQueue(timeout);
|
||||
return ii->listenerStorage.WaitForListenerQueue(timeout);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
NT_TopicListenerPoller CreateTopicListenerPoller(NT_Inst inst) {
|
||||
NT_Listener AddListener(NT_Inst inst,
|
||||
std::span<const std::string_view> prefixes,
|
||||
unsigned int mask, ListenerCallback callback) {
|
||||
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
|
||||
return ii->localStorage.CreateTopicListenerPoller();
|
||||
} else {
|
||||
return {};
|
||||
if ((mask & (NT_EVENT_TOPIC | NT_EVENT_VALUE_ALL)) != 0) {
|
||||
auto listener = ii->listenerStorage.AddListener(std::move(callback));
|
||||
ii->localStorage.AddListener(listener, prefixes, mask);
|
||||
return listener;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void DestroyTopicListenerPoller(NT_TopicListenerPoller poller) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kTopicListenerPoller)) {
|
||||
ii->localStorage.DestroyTopicListenerPoller(poller);
|
||||
}
|
||||
}
|
||||
|
||||
NT_TopicListener AddPolledTopicListener(
|
||||
NT_TopicListenerPoller poller, std::span<const std::string_view> prefixes,
|
||||
unsigned int mask) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kTopicListenerPoller)) {
|
||||
return ii->localStorage.AddPolledTopicListener(poller, prefixes, mask);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
NT_TopicListener AddPolledTopicListener(NT_TopicListenerPoller poller,
|
||||
NT_Handle handle, unsigned int mask) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kTopicListenerPoller)) {
|
||||
return ii->localStorage.AddPolledTopicListener(poller, handle, mask);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<TopicNotification> ReadTopicListenerQueue(
|
||||
NT_TopicListenerPoller poller) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kTopicListenerPoller)) {
|
||||
return ii->localStorage.ReadTopicListenerQueue(poller);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveTopicListener(NT_TopicListener listener) {
|
||||
if (auto ii = InstanceImpl::GetTyped(listener, Handle::kTopicListener)) {
|
||||
return ii->localStorage.RemoveTopicListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
NT_ValueListener AddValueListener(
|
||||
NT_Handle subentry, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> callback) {
|
||||
if (auto ii = InstanceImpl::GetHandle(subentry)) {
|
||||
return ii->localStorage.AddValueListener(subentry, mask,
|
||||
std::move(callback));
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
bool WaitForValueListenerQueue(NT_Handle handle, double timeout) {
|
||||
NT_Listener AddListener(NT_Handle handle, unsigned int mask,
|
||||
ListenerCallback callback) {
|
||||
if (auto ii = InstanceImpl::GetHandle(handle)) {
|
||||
return ii->localStorage.WaitForValueListenerQueue(timeout);
|
||||
auto listener = ii->listenerStorage.AddListener(std::move(callback));
|
||||
DoAddListener(*ii, listener, handle, mask);
|
||||
return listener;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
NT_ValueListenerPoller CreateValueListenerPoller(NT_Inst inst) {
|
||||
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
|
||||
return ii->localStorage.CreateValueListenerPoller();
|
||||
NT_Listener AddPolledListener(NT_ListenerPoller poller,
|
||||
std::span<const std::string_view> prefixes,
|
||||
unsigned int mask) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kListenerPoller)) {
|
||||
if ((mask & (NT_EVENT_TOPIC | NT_EVENT_VALUE_ALL)) != 0) {
|
||||
auto listener = ii->listenerStorage.AddListener(poller);
|
||||
ii->localStorage.AddListener(listener, prefixes, mask);
|
||||
return listener;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
NT_Listener AddPolledListener(NT_ListenerPoller poller, NT_Handle handle,
|
||||
unsigned int mask) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kListenerPoller)) {
|
||||
auto listener = ii->listenerStorage.AddListener(poller);
|
||||
DoAddListener(*ii, listener, handle, mask);
|
||||
return listener;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyValueListenerPoller(NT_ValueListenerPoller poller) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kValueListenerPoller)) {
|
||||
ii->localStorage.DestroyValueListenerPoller(poller);
|
||||
}
|
||||
}
|
||||
|
||||
NT_ValueListener AddPolledValueListener(NT_ValueListenerPoller poller,
|
||||
NT_Handle subentry, unsigned int mask) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kValueListenerPoller)) {
|
||||
return ii->localStorage.AddPolledValueListener(poller, subentry, mask);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ValueNotification> ReadValueListenerQueue(
|
||||
NT_ValueListenerPoller poller) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kValueListenerPoller)) {
|
||||
return ii->localStorage.ReadValueListenerQueue(poller);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveValueListener(NT_ValueListener listener) {
|
||||
if (auto ii = InstanceImpl::GetTyped(listener, Handle::kValueListener)) {
|
||||
return ii->localStorage.RemoveValueListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
NT_ConnectionListener AddConnectionListener(
|
||||
NT_Inst inst, bool immediate_notify,
|
||||
std::function<void(const ConnectionNotification& event)> callback) {
|
||||
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
|
||||
return ii->connectionList.AddListener(immediate_notify,
|
||||
std::move(callback));
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
bool WaitForConnectionListenerQueue(NT_Handle handle, double timeout) {
|
||||
if (auto ii = InstanceImpl::GetHandle(handle)) {
|
||||
return ii->connectionList.WaitForListenerQueue(timeout);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
NT_ConnectionListenerPoller CreateConnectionListenerPoller(NT_Inst inst) {
|
||||
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
|
||||
return ii->connectionList.CreateListenerPoller();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyConnectionListenerPoller(NT_ConnectionListenerPoller poller) {
|
||||
if (auto ii =
|
||||
InstanceImpl::GetTyped(poller, Handle::kConnectionListenerPoller)) {
|
||||
return ii->connectionList.DestroyListenerPoller(poller);
|
||||
}
|
||||
}
|
||||
|
||||
NT_ConnectionListener AddPolledConnectionListener(
|
||||
NT_ConnectionListenerPoller poller, bool immediate_notify) {
|
||||
if (auto ii =
|
||||
InstanceImpl::GetTyped(poller, Handle::kConnectionListenerPoller)) {
|
||||
return ii->connectionList.AddPolledListener(poller, immediate_notify);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ConnectionNotification> ReadConnectionListenerQueue(
|
||||
NT_ConnectionListenerPoller poller) {
|
||||
if (auto ii =
|
||||
InstanceImpl::GetTyped(poller, Handle::kConnectionListenerPoller)) {
|
||||
return ii->connectionList.ReadListenerQueue(poller);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveConnectionListener(NT_ConnectionListener listener) {
|
||||
if (auto ii = InstanceImpl::GetTyped(listener, Handle::kConnectionListener)) {
|
||||
return ii->connectionList.RemoveListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
int64_t Now() {
|
||||
if (gNowSet) {
|
||||
return gNowTime;
|
||||
@@ -796,58 +721,32 @@ bool IsConnected(NT_Inst inst) {
|
||||
}
|
||||
}
|
||||
|
||||
NT_Logger AddLogger(NT_Inst inst,
|
||||
std::function<void(const LogMessage& msg)> func,
|
||||
unsigned int minLevel, unsigned int maxLevel) {
|
||||
NT_Listener AddLogger(NT_Inst inst, unsigned int minLevel,
|
||||
unsigned int maxLevel, ListenerCallback func) {
|
||||
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
|
||||
if (minLevel < ii->logger.min_level()) {
|
||||
ii->logger.set_min_level(minLevel);
|
||||
}
|
||||
return ii->logger_impl.Add(std::move(func), minLevel, maxLevel);
|
||||
auto listener = ii->listenerStorage.AddListener(std::move(func));
|
||||
ii->logger_impl.AddListener(listener, minLevel, maxLevel);
|
||||
return listener;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
NT_LoggerPoller CreateLoggerPoller(NT_Inst inst) {
|
||||
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
|
||||
return ii->logger_impl.CreatePoller();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyLoggerPoller(NT_LoggerPoller poller) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kLoggerPoller)) {
|
||||
ii->logger_impl.DestroyPoller(poller);
|
||||
}
|
||||
}
|
||||
|
||||
NT_Logger AddPolledLogger(NT_LoggerPoller poller, unsigned int min_level,
|
||||
unsigned int max_level) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kLoggerPoller)) {
|
||||
if (min_level < ii->logger.min_level()) {
|
||||
ii->logger.set_min_level(min_level);
|
||||
NT_Listener AddPolledLogger(NT_ListenerPoller poller, unsigned int minLevel,
|
||||
unsigned int maxLevel) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kListenerPoller)) {
|
||||
if (minLevel < ii->logger.min_level()) {
|
||||
ii->logger.set_min_level(minLevel);
|
||||
}
|
||||
return ii->logger_impl.AddPolled(poller, min_level, max_level);
|
||||
auto listener = ii->listenerStorage.AddListener(poller);
|
||||
ii->logger_impl.AddListener(listener, minLevel, maxLevel);
|
||||
return listener;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<LogMessage> ReadLoggerQueue(NT_LoggerPoller poller) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kLoggerPoller)) {
|
||||
return ii->logger_impl.ReadQueue(poller);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveLogger(NT_Logger logger) {
|
||||
if (auto ii = InstanceImpl::GetTyped(logger, Handle::kLogger)) {
|
||||
ii->logger_impl.Remove(logger);
|
||||
ii->logger.set_min_level(ii->logger_impl.GetMinLevel());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class NetworkTableInstance;
|
||||
|
||||
/**
|
||||
* Connection listener. This calls back to a callback function when a connection
|
||||
* change occurs. The callback function is called asynchronously on a separate
|
||||
* thread, so it's important to use synchronization or atomics when accessing
|
||||
* any shared state from the callback function.
|
||||
*/
|
||||
class ConnectionListener final {
|
||||
public:
|
||||
ConnectionListener() = default;
|
||||
|
||||
/**
|
||||
* Create a listener for connection changes.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param immediateNotify if notification should be immediately created for
|
||||
* existing connections
|
||||
* @param listener Listener function
|
||||
*/
|
||||
ConnectionListener(
|
||||
NetworkTableInstance inst, bool immediateNotify,
|
||||
std::function<void(const ConnectionNotification&)> listener);
|
||||
|
||||
ConnectionListener(const ConnectionListener&) = delete;
|
||||
ConnectionListener& operator=(const ConnectionListener&) = delete;
|
||||
ConnectionListener(ConnectionListener&& rhs);
|
||||
ConnectionListener& operator=(ConnectionListener&& rhs);
|
||||
~ConnectionListener();
|
||||
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
NT_ConnectionListener GetHandle() const { return m_handle; }
|
||||
|
||||
/**
|
||||
* Wait for the connection listener queue to be empty. This is primarily
|
||||
* useful for deterministic testing. This blocks until either the connection
|
||||
* listener queue is empty (e.g. there are no more events that need to be
|
||||
* passed along to callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or
|
||||
* a negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForQueue(double timeout);
|
||||
|
||||
private:
|
||||
NT_ConnectionListener m_handle{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* A connection listener. This queues connection notifications. Code using
|
||||
* the listener must periodically call readQueue() to read the notifications.
|
||||
*/
|
||||
class ConnectionListenerPoller final {
|
||||
public:
|
||||
ConnectionListenerPoller() = default;
|
||||
|
||||
/**
|
||||
* Construct a connection listener poller.
|
||||
*
|
||||
* @param inst Instance
|
||||
*/
|
||||
explicit ConnectionListenerPoller(NetworkTableInstance inst);
|
||||
|
||||
ConnectionListenerPoller(const ConnectionListenerPoller&) = delete;
|
||||
ConnectionListenerPoller& operator=(const ConnectionListenerPoller&) = delete;
|
||||
ConnectionListenerPoller(ConnectionListenerPoller&& rhs);
|
||||
ConnectionListenerPoller& operator=(ConnectionListenerPoller&& rhs);
|
||||
~ConnectionListenerPoller();
|
||||
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
NT_ConnectionListenerPoller GetHandle() const { return m_handle; }
|
||||
|
||||
/**
|
||||
* Create a connection listener.
|
||||
*
|
||||
* @param immediateNotify if notification should be immediately created for
|
||||
* existing connections
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ConnectionListener Add(bool immediateNotify);
|
||||
|
||||
/**
|
||||
* Remove a connection listener.
|
||||
*
|
||||
* @param listener Listener handle
|
||||
*/
|
||||
void Remove(NT_ConnectionListener listener);
|
||||
|
||||
/**
|
||||
* Read connection notifications.
|
||||
*
|
||||
* @return Connection notifications since the previous call to readQueue()
|
||||
*/
|
||||
std::vector<ConnectionNotification> ReadQueue();
|
||||
|
||||
private:
|
||||
NT_ConnectionListenerPoller m_handle{0};
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#include "ConnectionListener.inc"
|
||||
@@ -1,83 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "networktables/ConnectionListener.h"
|
||||
#include "networktables/NetworkTableInstance.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
inline ConnectionListener::ConnectionListener(
|
||||
NetworkTableInstance inst, bool immediateNotify,
|
||||
std::function<void(const ConnectionNotification&)> listener)
|
||||
: m_handle{
|
||||
AddConnectionListener(inst.GetHandle(), immediateNotify, listener)} {}
|
||||
|
||||
inline ConnectionListener::ConnectionListener(ConnectionListener&& rhs)
|
||||
: m_handle(rhs.m_handle) {
|
||||
rhs.m_handle = 0;
|
||||
}
|
||||
|
||||
inline ConnectionListener& ConnectionListener::operator=(
|
||||
ConnectionListener&& rhs) {
|
||||
std::swap(m_handle, rhs.m_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ConnectionListener::~ConnectionListener() {
|
||||
if (m_handle != 0) {
|
||||
nt::RemoveConnectionListener(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ConnectionListener::WaitForQueue(double timeout) {
|
||||
if (m_handle != 0) {
|
||||
return nt::WaitForConnectionListenerQueue(m_handle, timeout);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline ConnectionListenerPoller::ConnectionListenerPoller(
|
||||
NetworkTableInstance inst)
|
||||
: m_handle(nt::CreateConnectionListenerPoller(inst.GetHandle())) {}
|
||||
|
||||
inline ConnectionListenerPoller::ConnectionListenerPoller(
|
||||
ConnectionListenerPoller&& rhs)
|
||||
: m_handle(rhs.m_handle) {
|
||||
rhs.m_handle = 0;
|
||||
}
|
||||
|
||||
inline ConnectionListenerPoller& ConnectionListenerPoller::operator=(
|
||||
ConnectionListenerPoller&& rhs) {
|
||||
std::swap(m_handle, rhs.m_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ConnectionListenerPoller::~ConnectionListenerPoller() {
|
||||
if (m_handle != 0) {
|
||||
nt::DestroyConnectionListenerPoller(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
inline NT_ConnectionListener ConnectionListenerPoller::Add(
|
||||
bool immediateNotify) {
|
||||
return nt::AddPolledConnectionListener(m_handle, immediateNotify);
|
||||
}
|
||||
|
||||
inline void ConnectionListenerPoller::Remove(NT_ConnectionListener listener) {
|
||||
nt::RemoveConnectionListener(listener);
|
||||
}
|
||||
|
||||
inline std::vector<ConnectionNotification>
|
||||
ConnectionListenerPoller::ReadQueue() {
|
||||
return nt::ReadConnectionListenerQueue(m_handle);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
@@ -357,9 +357,28 @@ class NetworkTableInstance final {
|
||||
|
||||
/**
|
||||
* @{
|
||||
* @name Connection Listener Functions
|
||||
* @name Listener Functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Remove a listener.
|
||||
*
|
||||
* @param listener Listener handle to remove
|
||||
*/
|
||||
static void RemoveListener(NT_Listener listener);
|
||||
|
||||
/**
|
||||
* Wait for the listener queue to be empty. This is primarily
|
||||
* useful for deterministic testing. This blocks until either the
|
||||
* listener queue is empty (e.g. there are no more events that need to be
|
||||
* passed along to callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or
|
||||
* a negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForListenerQueue(double timeout);
|
||||
|
||||
/**
|
||||
* Add a connection listener. The callback function is called asynchronously
|
||||
* on a separate thread, so it's important to use synchronization or atomics
|
||||
@@ -369,200 +388,84 @@ class NetworkTableInstance final {
|
||||
* @param callback listener to add
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ConnectionListener AddConnectionListener(
|
||||
bool immediate_notify,
|
||||
std::function<void(const ConnectionNotification& event)> callback) const;
|
||||
NT_Listener AddConnectionListener(bool immediate_notify,
|
||||
ListenerCallback callback) const;
|
||||
|
||||
/**
|
||||
* Remove a connection listener.
|
||||
*
|
||||
* @param conn_listener Listener handle to remove
|
||||
*/
|
||||
static void RemoveConnectionListener(NT_ConnectionListener conn_listener);
|
||||
|
||||
/**
|
||||
* Wait for the connection listener queue to be empty. This is primarily
|
||||
* useful for deterministic testing. This blocks until either the connection
|
||||
* listener queue is empty (e.g. there are no more events that need to be
|
||||
* passed along to callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or
|
||||
* a negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForConnectionListenerQueue(double timeout);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @{
|
||||
* @name Topic Listener Functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add a topic listener for changes on a particular topic. The callback
|
||||
* Add a listener for changes on a particular topic. The callback
|
||||
* function is called asynchronously on a separate thread, so it's important
|
||||
* to use synchronization or atomics when accessing any shared state from the
|
||||
* callback function.
|
||||
*
|
||||
* This creates a corresponding internal subscriber with the lifetime of the
|
||||
* listener.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param eventMask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener AddTopicListener(
|
||||
Topic topic, unsigned int eventMask,
|
||||
std::function<void(const TopicNotification&)> listener);
|
||||
NT_Listener AddListener(Topic topic, unsigned int eventMask,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Add a topic listener for topic changes on a subscriber. The callback
|
||||
* Add a listener for changes on a subscriber. The callback
|
||||
* function is called asynchronously on a separate thread, so it's important
|
||||
* to use synchronization or atomics when accessing any shared state from the
|
||||
* callback function. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param eventMask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener AddTopicListener(
|
||||
Subscriber& subscriber, unsigned int eventMask,
|
||||
std::function<void(const TopicNotification&)> listener);
|
||||
NT_Listener AddListener(Subscriber& subscriber, unsigned int eventMask,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Add a topic listener for topic changes on a subscriber. The callback
|
||||
* Add a listener for changes on a subscriber. The callback
|
||||
* function is called asynchronously on a separate thread, so it's important
|
||||
* to use synchronization or atomics when accessing any shared state from the
|
||||
* callback function. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param eventMask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener AddTopicListener(
|
||||
MultiSubscriber& subscriber, int eventMask,
|
||||
std::function<void(const TopicNotification&)> listener);
|
||||
NT_Listener AddListener(MultiSubscriber& subscriber, int eventMask,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Add a topic listener for topic changes on an entry. The callback function
|
||||
* Add a listener for changes on an entry. The callback function
|
||||
* is called asynchronously on a separate thread, so it's important to use
|
||||
* synchronization or atomics when accessing any shared state from the
|
||||
* callback function.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param eventMask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener AddTopicListener(
|
||||
NetworkTableEntry& entry, int eventMask,
|
||||
std::function<void(const TopicNotification&)> listener);
|
||||
NT_Listener AddListener(NetworkTableEntry& entry, int eventMask,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Add a topic listener for changes to topics with names that start with any
|
||||
* Add a listener for changes to topics with names that start with any
|
||||
* of the given prefixes. The callback function is called asynchronously on a
|
||||
* separate thread, so it's important to use synchronization or atomics when
|
||||
* accessing any shared state from the callback function.
|
||||
*
|
||||
* This creates a corresponding internal subscriber with the lifetime of the
|
||||
* listener.
|
||||
*
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param eventMask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener AddTopicListener(
|
||||
std::span<const std::string_view> prefixes, int eventMask,
|
||||
std::function<void(const TopicNotification&)> listener);
|
||||
|
||||
/**
|
||||
* Remove a topic listener.
|
||||
*
|
||||
* @param listener Listener handle to remove
|
||||
*/
|
||||
static void RemoveTopicListener(NT_TopicListener listener);
|
||||
|
||||
/**
|
||||
* Wait for the topic listener queue to be empty. This is primarily useful for
|
||||
* deterministic testing. This blocks until either the topic listener queue is
|
||||
* empty (e.g. there are no more events that need to be passed along to
|
||||
* callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or
|
||||
* a negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForTopicListenerQueue(double timeout);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @{
|
||||
* @name Value Listener Functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add a value listener for value changes on a subscriber. The callback
|
||||
* function is called asynchronously on a separate thread, so it's important
|
||||
* to use synchronization or atomics when accessing any shared state from the
|
||||
* callback function. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ValueListener AddValueListener(
|
||||
Subscriber& subscriber, unsigned int eventMask,
|
||||
std::function<void(const ValueNotification&)> listener);
|
||||
|
||||
/**
|
||||
* Add a value listener for value changes on a subscriber. The callback
|
||||
* function is called asynchronously on a separate thread, so it's important
|
||||
* to use synchronization or atomics when accessing any shared state from the
|
||||
* callback function. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ValueListener AddValueListener(
|
||||
MultiSubscriber& subscriber, unsigned int eventMask,
|
||||
std::function<void(const ValueNotification&)> listener);
|
||||
|
||||
/**
|
||||
* Add a value listener for value changes on an entry. The callback function
|
||||
* is called asynchronously on a separate thread, so it's important to use
|
||||
* synchronization or atomics when accessing any shared state from the
|
||||
* callback function.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ValueListener AddValueListener(
|
||||
NetworkTableEntry& entry, int eventMask,
|
||||
std::function<void(const ValueNotification&)> listener);
|
||||
|
||||
/**
|
||||
* Remove a value listener.
|
||||
*
|
||||
* @param listener Listener handle to remove
|
||||
*/
|
||||
static void RemoveValueListener(NT_ValueListener listener);
|
||||
|
||||
/**
|
||||
* Wait for the value listener queue to be empty. This is primarily useful for
|
||||
* deterministic testing. This blocks until either the value listener queue is
|
||||
* empty (e.g. there are no more events that need to be passed along to
|
||||
* callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or
|
||||
* a negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForValueListenerQueue(double timeout);
|
||||
NT_Listener AddListener(std::span<const std::string_view> prefixes,
|
||||
int eventMask, ListenerCallback listener);
|
||||
|
||||
/** @} */
|
||||
|
||||
@@ -771,20 +674,13 @@ class NetworkTableInstance final {
|
||||
* log messages with level greater than or equal to minLevel and less than or
|
||||
* equal to maxLevel; messages outside this range will be silently ignored.
|
||||
*
|
||||
* @param func log callback function
|
||||
* @param minLevel minimum log level
|
||||
* @param maxLevel maximum log level
|
||||
* @return Logger handle
|
||||
* @param func callback function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Logger AddLogger(std::function<void(const LogMessage& msg)> func,
|
||||
unsigned int minLevel, unsigned int maxLevel);
|
||||
|
||||
/**
|
||||
* Remove a logger.
|
||||
*
|
||||
* @param logger Logger handle to remove
|
||||
*/
|
||||
static void RemoveLogger(NT_Logger logger);
|
||||
NT_Listener AddLogger(unsigned int minLevel, unsigned int maxLevel,
|
||||
ListenerCallback func);
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "networktables/NetworkTableInstance.h"
|
||||
#include "networktables/Topic.h"
|
||||
#include "ntcore_c.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
@@ -82,81 +83,42 @@ inline NetworkTableEntry NetworkTableInstance::GetEntry(std::string_view name) {
|
||||
return NetworkTableEntry{::nt::GetEntry(m_handle, name)};
|
||||
}
|
||||
|
||||
inline NT_ConnectionListener NetworkTableInstance::AddConnectionListener(
|
||||
bool immediate_notify,
|
||||
std::function<void(const ConnectionNotification& event)> callback) const {
|
||||
return ::nt::AddConnectionListener(m_handle, immediate_notify,
|
||||
std::move(callback));
|
||||
inline bool NetworkTableInstance::WaitForListenerQueue(double timeout) {
|
||||
return ::nt::WaitForListenerQueue(m_handle, timeout);
|
||||
}
|
||||
|
||||
inline bool NetworkTableInstance::WaitForConnectionListenerQueue(
|
||||
double timeout) {
|
||||
return ::nt::WaitForConnectionListenerQueue(m_handle, timeout);
|
||||
inline void NetworkTableInstance::RemoveListener(NT_Listener listener) {
|
||||
::nt::RemoveListener(listener);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::RemoveConnectionListener(
|
||||
NT_ConnectionListener conn_listener) {
|
||||
::nt::RemoveConnectionListener(conn_listener);
|
||||
inline NT_Listener NetworkTableInstance::AddConnectionListener(
|
||||
bool immediate_notify, ListenerCallback callback) const {
|
||||
return ::nt::AddListener(
|
||||
m_handle,
|
||||
NT_EVENT_CONNECTION | (immediate_notify ? NT_EVENT_IMMEDIATE : 0),
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
inline NT_TopicListener NetworkTableInstance::AddTopicListener(
|
||||
Topic topic, unsigned int eventMask,
|
||||
std::function<void(const TopicNotification&)> listener) {
|
||||
return ::nt::AddTopicListener(topic.GetHandle(), eventMask,
|
||||
std::move(listener));
|
||||
inline NT_Listener NetworkTableInstance::AddListener(
|
||||
Topic topic, unsigned int eventMask, ListenerCallback listener) {
|
||||
return ::nt::AddListener(topic.GetHandle(), eventMask, std::move(listener));
|
||||
}
|
||||
|
||||
inline NT_TopicListener NetworkTableInstance::AddTopicListener(
|
||||
Subscriber& subscriber, unsigned int eventMask,
|
||||
std::function<void(const TopicNotification&)> listener) {
|
||||
return ::nt::AddTopicListener(subscriber.GetHandle(), eventMask,
|
||||
std::move(listener));
|
||||
inline NT_Listener NetworkTableInstance::AddListener(
|
||||
Subscriber& subscriber, unsigned int eventMask, ListenerCallback listener) {
|
||||
return ::nt::AddListener(subscriber.GetHandle(), eventMask,
|
||||
std::move(listener));
|
||||
}
|
||||
|
||||
inline NT_TopicListener NetworkTableInstance::AddTopicListener(
|
||||
NetworkTableEntry& entry, int eventMask,
|
||||
std::function<void(const TopicNotification&)> listener) {
|
||||
return ::nt::AddTopicListener(entry.GetHandle(), eventMask,
|
||||
std::move(listener));
|
||||
inline NT_Listener NetworkTableInstance::AddListener(
|
||||
NetworkTableEntry& entry, int eventMask, ListenerCallback listener) {
|
||||
return ::nt::AddListener(entry.GetHandle(), eventMask, std::move(listener));
|
||||
}
|
||||
|
||||
inline NT_TopicListener NetworkTableInstance::AddTopicListener(
|
||||
inline NT_Listener NetworkTableInstance::AddListener(
|
||||
std::span<const std::string_view> prefixes, int eventMask,
|
||||
std::function<void(const TopicNotification&)> listener) {
|
||||
return ::nt::AddTopicListener(m_handle, prefixes, eventMask,
|
||||
std::move(listener));
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::RemoveTopicListener(
|
||||
NT_TopicListener listener) {
|
||||
return ::nt::RemoveTopicListener(listener);
|
||||
}
|
||||
|
||||
inline bool NetworkTableInstance::WaitForTopicListenerQueue(double timeout) {
|
||||
return ::nt::WaitForTopicListenerQueue(m_handle, timeout);
|
||||
}
|
||||
|
||||
inline NT_ValueListener NetworkTableInstance::AddValueListener(
|
||||
Subscriber& subscriber, unsigned int eventMask,
|
||||
std::function<void(const ValueNotification&)> listener) {
|
||||
return ::nt::AddValueListener(subscriber.GetHandle(), eventMask,
|
||||
std::move(listener));
|
||||
}
|
||||
|
||||
inline NT_ValueListener NetworkTableInstance::AddValueListener(
|
||||
NetworkTableEntry& entry, int eventMask,
|
||||
std::function<void(const ValueNotification&)> listener) {
|
||||
return ::nt::AddValueListener(entry.GetHandle(), eventMask,
|
||||
std::move(listener));
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::RemoveValueListener(
|
||||
NT_ValueListener listener) {
|
||||
::nt::RemoveValueListener(listener);
|
||||
}
|
||||
|
||||
inline bool NetworkTableInstance::WaitForValueListenerQueue(double timeout) {
|
||||
return ::nt::WaitForValueListenerQueue(m_handle, timeout);
|
||||
ListenerCallback listener) {
|
||||
return ::nt::AddListener(m_handle, prefixes, eventMask, std::move(listener));
|
||||
}
|
||||
|
||||
inline unsigned int NetworkTableInstance::GetNetworkMode() const {
|
||||
@@ -254,14 +216,10 @@ inline void NetworkTableInstance::StopConnectionDataLog(
|
||||
::nt::StopConnectionDataLog(logger);
|
||||
}
|
||||
|
||||
inline NT_Logger NetworkTableInstance::AddLogger(
|
||||
std::function<void(const LogMessage& msg)> func, unsigned int min_level,
|
||||
unsigned int max_level) {
|
||||
return ::nt::AddLogger(m_handle, func, min_level, max_level);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::RemoveLogger(NT_Logger logger) {
|
||||
::nt::RemoveLogger(logger);
|
||||
inline NT_Listener NetworkTableInstance::AddLogger(unsigned int min_level,
|
||||
unsigned int max_level,
|
||||
ListenerCallback func) {
|
||||
return ::nt::AddLogger(m_handle, min_level, max_level, std::move(func));
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class MultiSubscriber;
|
||||
class NetworkTableEntry;
|
||||
class NetworkTableInstance;
|
||||
class Subscriber;
|
||||
class Topic;
|
||||
|
||||
/**
|
||||
* Event listener. This calls back to a callback function when an event
|
||||
* matching the specified mask occurs. The callback function is called
|
||||
* asynchronously on a separate thread, so it's important to use synchronization
|
||||
* or atomics when accessing any shared state from the callback function.
|
||||
*/
|
||||
class NetworkTableListener final {
|
||||
public:
|
||||
NetworkTableListener() = default;
|
||||
|
||||
/**
|
||||
* Create a listener for changes to topics with names that start with any of
|
||||
* the given prefixes. This creates a corresponding internal subscriber with
|
||||
* the lifetime of the listener.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
static NetworkTableListener CreateListener(
|
||||
NetworkTableInstance inst, std::span<const std::string_view> prefixes,
|
||||
unsigned int mask, ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Create a listener for changes on a particular topic. This creates a
|
||||
* corresponding internal subscriber with the lifetime of the listener.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
static NetworkTableListener CreateListener(Topic topic, unsigned int mask,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on a subscriber. This does NOT keep the
|
||||
* subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
static NetworkTableListener CreateListener(Subscriber& subscriber,
|
||||
unsigned int mask,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on a subscriber. This does NOT keep the
|
||||
* subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
static NetworkTableListener CreateListener(MultiSubscriber& subscriber,
|
||||
unsigned int mask,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
static NetworkTableListener CreateListener(NetworkTableEntry& entry,
|
||||
unsigned int mask,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Create a connection listener.
|
||||
*
|
||||
* @param inst instance
|
||||
* @param immediate_notify notify listener of all existing connections
|
||||
* @param listener listener function
|
||||
* @return Listener
|
||||
*/
|
||||
static NetworkTableListener CreateConnectionListener(
|
||||
NetworkTableInstance inst, bool immediate_notify,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Create a listener for log messages. By default, log messages are sent to
|
||||
* stderr; this function sends log messages with the specified levels to the
|
||||
* provided callback function instead. The callback function will only be
|
||||
* called for log messages with level greater than or equal to minLevel and
|
||||
* less than or equal to maxLevel; messages outside this range will be
|
||||
* silently ignored.
|
||||
*
|
||||
* @param inst instance
|
||||
* @param minLevel minimum log level
|
||||
* @param maxLevel maximum log level
|
||||
* @param listener listener function
|
||||
* @return Listener
|
||||
*/
|
||||
static NetworkTableListener CreateLogger(NetworkTableInstance inst,
|
||||
unsigned int minLevel,
|
||||
unsigned int maxLevel,
|
||||
ListenerCallback listener);
|
||||
|
||||
NetworkTableListener(const NetworkTableListener&) = delete;
|
||||
NetworkTableListener& operator=(const NetworkTableListener&) = delete;
|
||||
NetworkTableListener(NetworkTableListener&& rhs);
|
||||
NetworkTableListener& operator=(NetworkTableListener&& rhs);
|
||||
~NetworkTableListener();
|
||||
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
NT_Listener GetHandle() const { return m_handle; }
|
||||
|
||||
/**
|
||||
* Wait for the listener queue to be empty. This is primarily useful for
|
||||
* deterministic testing. This blocks until either the listener queue is
|
||||
* empty (e.g. there are no more events that need to be passed along to
|
||||
* callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or
|
||||
* a negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForQueue(double timeout);
|
||||
|
||||
private:
|
||||
explicit NetworkTableListener(NT_Listener handle) : m_handle{handle} {}
|
||||
|
||||
NT_Listener m_handle{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* Event polled listener. This queues events matching the specified mask. Code
|
||||
* using the listener must periodically call ReadQueue() to read the
|
||||
* events.
|
||||
*/
|
||||
class NetworkTableListenerPoller final {
|
||||
public:
|
||||
NetworkTableListenerPoller() = default;
|
||||
|
||||
/**
|
||||
* Construct a listener poller.
|
||||
*
|
||||
* @param inst Instance
|
||||
*/
|
||||
explicit NetworkTableListenerPoller(NetworkTableInstance inst);
|
||||
|
||||
NetworkTableListenerPoller(const NetworkTableListenerPoller&) = delete;
|
||||
NetworkTableListenerPoller& operator=(const NetworkTableListenerPoller&) =
|
||||
delete;
|
||||
NetworkTableListenerPoller(NetworkTableListenerPoller&& rhs);
|
||||
NetworkTableListenerPoller& operator=(NetworkTableListenerPoller&& rhs);
|
||||
~NetworkTableListenerPoller();
|
||||
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
NT_ListenerPoller GetHandle() const { return m_handle; }
|
||||
|
||||
/**
|
||||
* Start listening to topic changes for topics with names that start with any
|
||||
* of the given prefixes. This creates a corresponding internal subscriber
|
||||
* with the lifetime of the listener.
|
||||
*
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddListener(std::span<const std::string_view> prefixes,
|
||||
unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to changes to a particular topic. This creates a
|
||||
* corresponding internal subscriber with the lifetime of the listener.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddListener(Topic topic, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on a subscriber. This does NOT keep the
|
||||
* subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddListener(Subscriber& subscriber, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on a subscriber. This does NOT keep the
|
||||
* subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddListener(MultiSubscriber& subscriber, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddListener(NetworkTableEntry& entry, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Add a connection listener. The callback function is called asynchronously
|
||||
* on a separate thread, so it's important to use synchronization or atomics
|
||||
* when accessing any shared state from the callback function.
|
||||
*
|
||||
* @param immediate_notify notify listener of all existing connections
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddConnectionListener(bool immediate_notify);
|
||||
|
||||
/**
|
||||
* Add logger callback function. By default, log messages are sent to stderr;
|
||||
* this function sends log messages with the specified levels to the provided
|
||||
* callback function instead. The callback function will only be called for
|
||||
* log messages with level greater than or equal to minLevel and less than or
|
||||
* equal to maxLevel; messages outside this range will be silently ignored.
|
||||
*
|
||||
* @param minLevel minimum log level
|
||||
* @param maxLevel maximum log level
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddLogger(unsigned int minLevel, unsigned int maxLevel);
|
||||
|
||||
/**
|
||||
* Remove a listener.
|
||||
*
|
||||
* @param listener Listener handle
|
||||
*/
|
||||
void RemoveListener(NT_Listener listener);
|
||||
|
||||
/**
|
||||
* Read events.
|
||||
*
|
||||
* @return Events since the previous call to ReadQueue()
|
||||
*/
|
||||
std::vector<Event> ReadQueue();
|
||||
|
||||
private:
|
||||
NT_ListenerPoller m_handle{0};
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#include "NetworkTableListener.inc"
|
||||
@@ -0,0 +1,160 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "networktables/MultiSubscriber.h"
|
||||
#include "networktables/NetworkTableEntry.h"
|
||||
#include "networktables/NetworkTableInstance.h"
|
||||
#include "networktables/NetworkTableListener.h"
|
||||
#include "networktables/Topic.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
inline NetworkTableListener NetworkTableListener::CreateListener(
|
||||
NetworkTableInstance inst, std::span<const std::string_view> prefixes,
|
||||
unsigned int mask, ListenerCallback listener) {
|
||||
return NetworkTableListener{
|
||||
::nt::AddListener(inst.GetHandle(), prefixes, mask, std::move(listener))};
|
||||
}
|
||||
|
||||
inline NetworkTableListener NetworkTableListener::CreateListener(
|
||||
Topic topic, unsigned int mask, ListenerCallback listener) {
|
||||
return NetworkTableListener{
|
||||
nt::AddListener(topic.GetHandle(), mask, std::move(listener))};
|
||||
}
|
||||
|
||||
inline NetworkTableListener NetworkTableListener::CreateListener(
|
||||
Subscriber& subscriber, unsigned int mask, ListenerCallback listener) {
|
||||
return NetworkTableListener{
|
||||
::nt::AddListener(subscriber.GetHandle(), mask, std::move(listener))};
|
||||
}
|
||||
|
||||
inline NetworkTableListener NetworkTableListener::CreateListener(
|
||||
MultiSubscriber& subscriber, unsigned int mask, ListenerCallback listener) {
|
||||
return NetworkTableListener{
|
||||
::nt::AddListener(subscriber.GetHandle(), mask, std::move(listener))};
|
||||
}
|
||||
|
||||
inline NetworkTableListener NetworkTableListener::CreateListener(
|
||||
NetworkTableEntry& entry, unsigned int mask, ListenerCallback listener) {
|
||||
return NetworkTableListener{
|
||||
::nt::AddListener(entry.GetHandle(), mask, std::move(listener))};
|
||||
}
|
||||
|
||||
inline NetworkTableListener NetworkTableListener::CreateConnectionListener(
|
||||
NetworkTableInstance inst, bool immediate_notify,
|
||||
ListenerCallback listener) {
|
||||
return NetworkTableListener{::nt::AddListener(
|
||||
inst.GetHandle(),
|
||||
NT_EVENT_CONNECTION | (immediate_notify ? NT_EVENT_IMMEDIATE : 0),
|
||||
std::move(listener))};
|
||||
}
|
||||
|
||||
inline NetworkTableListener NetworkTableListener::CreateLogger(
|
||||
NetworkTableInstance inst, unsigned int minLevel, unsigned int maxLevel,
|
||||
ListenerCallback listener) {
|
||||
return NetworkTableListener{::nt::AddLogger(inst.GetHandle(), minLevel,
|
||||
maxLevel, std::move(listener))};
|
||||
}
|
||||
|
||||
inline NetworkTableListener::NetworkTableListener(NetworkTableListener&& rhs)
|
||||
: m_handle(rhs.m_handle) {
|
||||
rhs.m_handle = 0;
|
||||
}
|
||||
|
||||
inline NetworkTableListener& NetworkTableListener::operator=(
|
||||
NetworkTableListener&& rhs) {
|
||||
std::swap(m_handle, rhs.m_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline NetworkTableListener::~NetworkTableListener() {
|
||||
if (m_handle != 0) {
|
||||
nt::RemoveListener(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool NetworkTableListener::WaitForQueue(double timeout) {
|
||||
if (m_handle != 0) {
|
||||
return nt::WaitForListenerQueue(m_handle, timeout);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline NetworkTableListenerPoller::NetworkTableListenerPoller(
|
||||
NetworkTableInstance inst)
|
||||
: m_handle(nt::CreateListenerPoller(inst.GetHandle())) {}
|
||||
|
||||
inline NetworkTableListenerPoller::NetworkTableListenerPoller(
|
||||
NetworkTableListenerPoller&& rhs)
|
||||
: m_handle(rhs.m_handle) {
|
||||
rhs.m_handle = 0;
|
||||
}
|
||||
|
||||
inline NetworkTableListenerPoller& NetworkTableListenerPoller::operator=(
|
||||
NetworkTableListenerPoller&& rhs) {
|
||||
std::swap(m_handle, rhs.m_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline NetworkTableListenerPoller::~NetworkTableListenerPoller() {
|
||||
if (m_handle != 0) {
|
||||
nt::DestroyListenerPoller(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableListenerPoller::AddListener(
|
||||
std::span<const std::string_view> prefixes, unsigned int mask) {
|
||||
return nt::AddPolledListener(m_handle, prefixes, mask);
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableListenerPoller::AddListener(Topic topic,
|
||||
unsigned int mask) {
|
||||
return ::nt::AddPolledListener(m_handle, topic.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableListenerPoller::AddListener(
|
||||
Subscriber& subscriber, unsigned int mask) {
|
||||
return ::nt::AddPolledListener(m_handle, subscriber.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableListenerPoller::AddListener(
|
||||
MultiSubscriber& subscriber, unsigned int mask) {
|
||||
return ::nt::AddPolledListener(m_handle, subscriber.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableListenerPoller::AddListener(
|
||||
NetworkTableEntry& entry, unsigned int mask) {
|
||||
return ::nt::AddPolledListener(m_handle, entry.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableListenerPoller::AddConnectionListener(
|
||||
bool immediate_notify) {
|
||||
return ::nt::AddPolledListener(
|
||||
m_handle, ::nt::GetInstanceFromHandle(m_handle),
|
||||
NT_EVENT_CONNECTION | (immediate_notify ? NT_EVENT_IMMEDIATE : 0));
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableListenerPoller::AddLogger(
|
||||
unsigned int minLevel, unsigned int maxLevel) {
|
||||
return ::nt::AddPolledLogger(m_handle, minLevel, maxLevel);
|
||||
}
|
||||
|
||||
inline void NetworkTableListenerPoller::RemoveListener(NT_Listener listener) {
|
||||
::nt::RemoveListener(listener);
|
||||
}
|
||||
|
||||
inline std::vector<Event> NetworkTableListenerPoller::ReadQueue() {
|
||||
return ::nt::ReadListenerQueue(m_handle);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
@@ -1,263 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class MultiSubscriber;
|
||||
class NetworkTableEntry;
|
||||
class NetworkTableInstance;
|
||||
class Subscriber;
|
||||
class Topic;
|
||||
|
||||
/**
|
||||
* Flag values for use with topic listeners.
|
||||
*
|
||||
* The flags are a bitmask and must be OR'ed together to indicate the
|
||||
* combination of events desired to be received.
|
||||
*
|
||||
* The constants kPublish, kUnpublish, and kProperties represent different
|
||||
* events that can occur to entries.
|
||||
*
|
||||
* @ingroup ntcore_cpp_api
|
||||
*/
|
||||
struct TopicListenerFlags {
|
||||
TopicListenerFlags() = delete;
|
||||
|
||||
/**
|
||||
* Initial listener addition.
|
||||
* Set this flag to receive immediate notification of entries matching the
|
||||
* flag criteria (generally only useful when combined with kPublish).
|
||||
*/
|
||||
static constexpr unsigned int kImmediate = NT_TOPIC_NOTIFY_IMMEDIATE;
|
||||
|
||||
/**
|
||||
* Newly published topic.
|
||||
*
|
||||
* Set this flag to receive a notification when a topic is initially
|
||||
* published.
|
||||
*/
|
||||
static constexpr unsigned int kPublish = NT_TOPIC_NOTIFY_PUBLISH;
|
||||
|
||||
/**
|
||||
* Topic has no more publishers.
|
||||
*
|
||||
* Set this flag to receive a notification when a topic has no more
|
||||
* publishers.
|
||||
*/
|
||||
static constexpr unsigned int kUnpublish = NT_TOPIC_NOTIFY_UNPUBLISH;
|
||||
|
||||
/**
|
||||
* Topic's properties changed.
|
||||
*
|
||||
* Set this flag to receive a notification when an topic's properties change.
|
||||
*/
|
||||
static constexpr unsigned int kProperties = NT_TOPIC_NOTIFY_PROPERTIES;
|
||||
};
|
||||
|
||||
/**
|
||||
* Topic change listener. This calls back to a callback function when a topic
|
||||
* change matching the specified mask occurs. The callback function is called
|
||||
* asynchronously on a separate thread, so it's important to use synchronization
|
||||
* or atomics when accessing any shared state from the callback function.
|
||||
*/
|
||||
class TopicListener final {
|
||||
public:
|
||||
TopicListener() = default;
|
||||
|
||||
/**
|
||||
* Create a listener for changes to topics with names that start with any of
|
||||
* the given prefixes.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
TopicListener(NetworkTableInstance inst,
|
||||
std::span<const std::string_view> prefixes, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener);
|
||||
|
||||
/**
|
||||
* Create a listener for changes on a particular topic.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
TopicListener(Topic topic, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener);
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on a subscriber. This does NOT keep the
|
||||
* subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
TopicListener(Subscriber& subscriber, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener);
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on a subscriber. This does NOT keep the
|
||||
* subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
TopicListener(MultiSubscriber& subscriber, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener);
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
TopicListener(NetworkTableEntry& entry, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener);
|
||||
|
||||
TopicListener(const TopicListener&) = delete;
|
||||
TopicListener& operator=(const TopicListener&) = delete;
|
||||
TopicListener(TopicListener&& rhs);
|
||||
TopicListener& operator=(TopicListener&& rhs);
|
||||
~TopicListener();
|
||||
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
NT_TopicListener GetHandle() const { return m_handle; }
|
||||
|
||||
/**
|
||||
* Wait for the topic listener queue to be empty. This is primarily useful for
|
||||
* deterministic testing. This blocks until either the topic listener queue is
|
||||
* empty (e.g. there are no more events that need to be passed along to
|
||||
* callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or
|
||||
* a negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForQueue(double timeout);
|
||||
|
||||
private:
|
||||
NT_TopicListener m_handle{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* Topic change listener. This queues topic change events matching the specified
|
||||
* mask. Code using the listener must periodically call readQueue() to read the
|
||||
* events.
|
||||
*/
|
||||
class TopicListenerPoller final {
|
||||
public:
|
||||
TopicListenerPoller() = default;
|
||||
|
||||
/**
|
||||
* Construct a topic listener poller.
|
||||
*
|
||||
* @param inst Instance
|
||||
*/
|
||||
explicit TopicListenerPoller(NetworkTableInstance inst);
|
||||
|
||||
TopicListenerPoller(const TopicListenerPoller&) = delete;
|
||||
TopicListenerPoller& operator=(const TopicListenerPoller&) = delete;
|
||||
TopicListenerPoller(TopicListenerPoller&& rhs);
|
||||
TopicListenerPoller& operator=(TopicListenerPoller&& rhs);
|
||||
~TopicListenerPoller();
|
||||
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
NT_TopicListenerPoller GetHandle() const { return m_handle; }
|
||||
|
||||
/**
|
||||
* Start listening to changes to a particular topic.
|
||||
*
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener Add(std::span<const std::string_view> prefixes,
|
||||
unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to topic changes for topics with names that start with any
|
||||
* of the given prefixes.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener Add(Topic topic, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on a subscriber. This does NOT keep the
|
||||
* subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener Add(Subscriber& subscriber, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on a subscriber. This does NOT keep the
|
||||
* subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener Add(MultiSubscriber& subscriber, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener Add(NetworkTableEntry& entry, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Remove a listener.
|
||||
*
|
||||
* @param listener Listener handle
|
||||
*/
|
||||
void Remove(NT_TopicListener listener);
|
||||
|
||||
/**
|
||||
* Read topic notifications.
|
||||
*
|
||||
* @return Topic notifications since the previous call to readQueue()
|
||||
*/
|
||||
std::vector<TopicNotification> ReadQueue();
|
||||
|
||||
private:
|
||||
NT_TopicListenerPoller m_handle{0};
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#include "TopicListener.inc"
|
||||
@@ -1,123 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "networktables/MultiSubscriber.h"
|
||||
#include "networktables/NetworkTableEntry.h"
|
||||
#include "networktables/NetworkTableInstance.h"
|
||||
#include "networktables/Topic.h"
|
||||
#include "networktables/TopicListener.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
inline TopicListener::TopicListener(
|
||||
NetworkTableInstance inst, std::span<const std::string_view> prefixes,
|
||||
unsigned int mask, std::function<void(const TopicNotification&)> listener)
|
||||
: m_handle{AddTopicListener(inst.GetHandle(), prefixes, mask, listener)} {}
|
||||
|
||||
inline TopicListener::TopicListener(
|
||||
Topic topic, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener)
|
||||
: m_handle{AddTopicListener(topic.GetHandle(), mask, listener)} {}
|
||||
|
||||
inline TopicListener::TopicListener(
|
||||
Subscriber& subscriber, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener)
|
||||
: m_handle{AddTopicListener(subscriber.GetHandle(), mask, listener)} {}
|
||||
|
||||
inline TopicListener::TopicListener(
|
||||
MultiSubscriber& subscriber, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener)
|
||||
: m_handle{AddTopicListener(subscriber.GetHandle(), mask, listener)} {}
|
||||
|
||||
inline TopicListener::TopicListener(
|
||||
NetworkTableEntry& entry, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener)
|
||||
: m_handle{AddTopicListener(entry.GetHandle(), mask, listener)} {}
|
||||
|
||||
inline TopicListener::TopicListener(TopicListener&& rhs)
|
||||
: m_handle(rhs.m_handle) {
|
||||
rhs.m_handle = 0;
|
||||
}
|
||||
|
||||
inline TopicListener& TopicListener::operator=(TopicListener&& rhs) {
|
||||
std::swap(m_handle, rhs.m_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline TopicListener::~TopicListener() {
|
||||
if (m_handle != 0) {
|
||||
nt::RemoveTopicListener(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool TopicListener::WaitForQueue(double timeout) {
|
||||
if (m_handle != 0) {
|
||||
return nt::WaitForTopicListenerQueue(m_handle, timeout);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline TopicListenerPoller::TopicListenerPoller(NetworkTableInstance inst)
|
||||
: m_handle(nt::CreateTopicListenerPoller(inst.GetHandle())) {}
|
||||
|
||||
inline TopicListenerPoller::TopicListenerPoller(TopicListenerPoller&& rhs)
|
||||
: m_handle(rhs.m_handle) {
|
||||
rhs.m_handle = 0;
|
||||
}
|
||||
|
||||
inline TopicListenerPoller& TopicListenerPoller::operator=(
|
||||
TopicListenerPoller&& rhs) {
|
||||
std::swap(m_handle, rhs.m_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline TopicListenerPoller::~TopicListenerPoller() {
|
||||
if (m_handle != 0) {
|
||||
nt::DestroyTopicListenerPoller(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
inline NT_TopicListener TopicListenerPoller::Add(
|
||||
std::span<const std::string_view> prefixes, unsigned int mask) {
|
||||
return nt::AddPolledTopicListener(m_handle, prefixes, mask);
|
||||
}
|
||||
|
||||
inline NT_TopicListener TopicListenerPoller::Add(Topic topic,
|
||||
unsigned int mask) {
|
||||
return nt::AddPolledTopicListener(m_handle, topic.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_TopicListener TopicListenerPoller::Add(Subscriber& subscriber,
|
||||
unsigned int mask) {
|
||||
return nt::AddPolledTopicListener(m_handle, subscriber.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_TopicListener TopicListenerPoller::Add(MultiSubscriber& subscriber,
|
||||
unsigned int mask) {
|
||||
return nt::AddPolledTopicListener(m_handle, subscriber.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_TopicListener TopicListenerPoller::Add(NetworkTableEntry& entry,
|
||||
unsigned int mask) {
|
||||
return nt::AddPolledTopicListener(m_handle, entry.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline void TopicListenerPoller::Remove(NT_TopicListener listener) {
|
||||
nt::RemoveTopicListener(listener);
|
||||
}
|
||||
|
||||
inline std::vector<TopicNotification> TopicListenerPoller::ReadQueue() {
|
||||
return nt::ReadTopicListenerQueue(m_handle);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
@@ -1,203 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class MultiSubscriber;
|
||||
class NetworkTableEntry;
|
||||
class NetworkTableInstance;
|
||||
class Subscriber;
|
||||
|
||||
/**
|
||||
* Flag values for use with value listeners.
|
||||
*
|
||||
* The flags are a bitmask and must be OR'ed together to indicate the
|
||||
* combination of events desired to be received.
|
||||
*
|
||||
* By default, notifications are only generated for remote changes occurring
|
||||
* after the listener is created. The constants kImmediate and kLocal are
|
||||
* modifiers that cause notifications to be generated at other times.
|
||||
*/
|
||||
struct ValueListenerFlags {
|
||||
ValueListenerFlags() = delete;
|
||||
|
||||
/**
|
||||
* Initial listener addition.
|
||||
*
|
||||
* Set this flag to receive immediate notification of the current value.
|
||||
*/
|
||||
static constexpr unsigned int kImmediate = NT_VALUE_NOTIFY_IMMEDIATE;
|
||||
|
||||
/**
|
||||
* Changed locally.
|
||||
*
|
||||
* Set this flag to receive notification of both local changes and changes
|
||||
* coming from remote nodes. By default, notifications are only generated for
|
||||
* remote changes.
|
||||
*/
|
||||
static constexpr unsigned int kLocal = NT_VALUE_NOTIFY_LOCAL;
|
||||
};
|
||||
|
||||
/**
|
||||
* Value change listener. This calls back to a callback function when a value
|
||||
* change matching the specified mask occurs. The callback function is called
|
||||
* asynchronously on a separate thread, so it's important to use synchronization
|
||||
* or atomics when accessing any shared state from the callback function.
|
||||
*/
|
||||
class ValueListener final {
|
||||
public:
|
||||
ValueListener() = default;
|
||||
|
||||
/**
|
||||
* Create a listener for value changes on a subscriber. This does NOT keep the
|
||||
* subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
ValueListener(Subscriber& subscriber, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> listener);
|
||||
|
||||
/**
|
||||
* Create a listener for value changes on a subscriber. This does NOT keep the
|
||||
* subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
ValueListener(MultiSubscriber& subscriber, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> listener);
|
||||
|
||||
/**
|
||||
* Create a listener for value changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param mask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
ValueListener(NetworkTableEntry& entry, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> listener);
|
||||
|
||||
ValueListener(const ValueListener&) = delete;
|
||||
ValueListener& operator=(const ValueListener&) = delete;
|
||||
ValueListener(ValueListener&& rhs);
|
||||
ValueListener& operator=(ValueListener&& rhs);
|
||||
~ValueListener();
|
||||
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
NT_ValueListener GetHandle() const { return m_handle; }
|
||||
|
||||
/**
|
||||
* Wait for the value listener queue to be empty. This is primarily useful for
|
||||
* deterministic testing. This blocks until either the value listener queue is
|
||||
* empty (e.g. there are no more events that need to be passed along to
|
||||
* callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or
|
||||
* a negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForQueue(double timeout);
|
||||
|
||||
private:
|
||||
NT_ValueListener m_handle{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* Value change listener. This queues value change events matching the specified
|
||||
* mask. Code using the listener must periodically call readQueue() to read the
|
||||
* events.
|
||||
*/
|
||||
class ValueListenerPoller final {
|
||||
public:
|
||||
ValueListenerPoller() = default;
|
||||
|
||||
/**
|
||||
* Construct a value listener poller.
|
||||
*
|
||||
* @param inst Instance
|
||||
*/
|
||||
explicit ValueListenerPoller(NetworkTableInstance inst);
|
||||
|
||||
ValueListenerPoller(const ValueListenerPoller&) = delete;
|
||||
ValueListenerPoller& operator=(const ValueListenerPoller&) = delete;
|
||||
ValueListenerPoller(ValueListenerPoller&& rhs);
|
||||
ValueListenerPoller& operator=(ValueListenerPoller&& rhs);
|
||||
~ValueListenerPoller();
|
||||
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
NT_ValueListenerPoller GetHandle() const { return m_handle; }
|
||||
|
||||
/**
|
||||
* Start listening to value changes on a subscriber. This does NOT keep the
|
||||
* subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of ValueListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ValueListener Add(Subscriber& subscriber, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to value changes on a subscriber. This does NOT keep the
|
||||
* subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of ValueListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ValueListener Add(MultiSubscriber& subscriber, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to value changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param mask Bitmask of ValueListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ValueListener Add(NetworkTableEntry& entry, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Remove a listener.
|
||||
*
|
||||
* @param listener Listener handle
|
||||
*/
|
||||
void Remove(NT_ValueListener listener);
|
||||
|
||||
/**
|
||||
* Reads value listener queue (all value changes since last call).
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @return Array of value notifications.
|
||||
*/
|
||||
std::vector<ValueNotification> ReadQueue();
|
||||
|
||||
private:
|
||||
NT_ValueListenerPoller m_handle{0};
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#include "ValueListener.inc"
|
||||
@@ -1,101 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "networktables/MultiSubscriber.h"
|
||||
#include "networktables/NetworkTableEntry.h"
|
||||
#include "networktables/NetworkTableInstance.h"
|
||||
#include "networktables/Topic.h"
|
||||
#include "networktables/ValueListener.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
inline ValueListener::ValueListener(
|
||||
Subscriber& subscriber, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> listener)
|
||||
: m_handle{AddValueListener(subscriber.GetHandle(), mask, listener)} {}
|
||||
|
||||
inline ValueListener::ValueListener(
|
||||
MultiSubscriber& subscriber, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> listener)
|
||||
: m_handle{AddValueListener(subscriber.GetHandle(), mask, listener)} {}
|
||||
|
||||
inline ValueListener::ValueListener(
|
||||
NetworkTableEntry& entry, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> listener)
|
||||
: m_handle{AddValueListener(entry.GetHandle(), mask, listener)} {}
|
||||
|
||||
inline ValueListener::ValueListener(ValueListener&& rhs)
|
||||
: m_handle(rhs.m_handle) {
|
||||
rhs.m_handle = 0;
|
||||
}
|
||||
|
||||
inline ValueListener& ValueListener::operator=(ValueListener&& rhs) {
|
||||
std::swap(m_handle, rhs.m_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ValueListener::~ValueListener() {
|
||||
if (m_handle != 0) {
|
||||
nt::RemoveValueListener(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ValueListener::WaitForQueue(double timeout) {
|
||||
if (m_handle != 0) {
|
||||
return nt::WaitForValueListenerQueue(m_handle, timeout);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline ValueListenerPoller::ValueListenerPoller(NetworkTableInstance inst)
|
||||
: m_handle(nt::CreateValueListenerPoller(inst.GetHandle())) {}
|
||||
|
||||
inline ValueListenerPoller::ValueListenerPoller(ValueListenerPoller&& rhs)
|
||||
: m_handle(rhs.m_handle) {
|
||||
rhs.m_handle = 0;
|
||||
}
|
||||
|
||||
inline ValueListenerPoller& ValueListenerPoller::operator=(
|
||||
ValueListenerPoller&& rhs) {
|
||||
std::swap(m_handle, rhs.m_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ValueListenerPoller::~ValueListenerPoller() {
|
||||
if (m_handle != 0) {
|
||||
nt::DestroyValueListenerPoller(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
inline NT_ValueListener ValueListenerPoller::Add(Subscriber& subscriber,
|
||||
unsigned int mask) {
|
||||
return nt::AddPolledValueListener(m_handle, subscriber.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_ValueListener ValueListenerPoller::Add(MultiSubscriber& subscriber,
|
||||
unsigned int mask) {
|
||||
return nt::AddPolledValueListener(m_handle, subscriber.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_ValueListener ValueListenerPoller::Add(NetworkTableEntry& entry,
|
||||
unsigned int mask) {
|
||||
return nt::AddPolledValueListener(m_handle, entry.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline void ValueListenerPoller::Remove(NT_ValueListener listener) {
|
||||
nt::RemoveValueListener(listener);
|
||||
}
|
||||
|
||||
inline std::vector<ValueNotification> ValueListenerPoller::ReadQueue() {
|
||||
return nt::ReadValueListenerQueue(m_handle);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
@@ -29,21 +29,15 @@ typedef int NT_Bool;
|
||||
|
||||
typedef unsigned int NT_Handle;
|
||||
typedef NT_Handle NT_ConnectionDataLogger;
|
||||
typedef NT_Handle NT_ConnectionListener;
|
||||
typedef NT_Handle NT_ConnectionListenerPoller;
|
||||
typedef NT_Handle NT_DataLogger;
|
||||
typedef NT_Handle NT_Entry;
|
||||
typedef NT_Handle NT_Inst;
|
||||
typedef NT_Handle NT_Logger;
|
||||
typedef NT_Handle NT_LoggerPoller;
|
||||
typedef NT_Handle NT_Listener;
|
||||
typedef NT_Handle NT_ListenerPoller;
|
||||
typedef NT_Handle NT_MultiSubscriber;
|
||||
typedef NT_Handle NT_Topic;
|
||||
typedef NT_Handle NT_TopicListener;
|
||||
typedef NT_Handle NT_TopicListenerPoller;
|
||||
typedef NT_Handle NT_Subscriber;
|
||||
typedef NT_Handle NT_Publisher;
|
||||
typedef NT_Handle NT_ValueListener;
|
||||
typedef NT_Handle NT_ValueListenerPoller;
|
||||
|
||||
/** Default network tables port number (NT3) */
|
||||
#define NT_DEFAULT_PORT3 1735
|
||||
@@ -103,20 +97,33 @@ enum NT_PubSubOptionType {
|
||||
NT_PUBSUB_KEEPDUPLICATES, /* preserve duplicate values */
|
||||
};
|
||||
|
||||
/** Topic notification flags. */
|
||||
enum NT_TopicNotifyKind {
|
||||
NT_TOPIC_NOTIFY_NONE = 0,
|
||||
NT_TOPIC_NOTIFY_IMMEDIATE = 0x01, /* initial listener addition */
|
||||
NT_TOPIC_NOTIFY_PUBLISH = 0x02, /* initially published */
|
||||
NT_TOPIC_NOTIFY_UNPUBLISH = 0x04, /* no more publishers */
|
||||
NT_TOPIC_NOTIFY_PROPERTIES = 0x08, /* properties changed */
|
||||
};
|
||||
|
||||
/** Value notification flags. */
|
||||
enum NT_ValueNotifyKind {
|
||||
NT_VALUE_NOTIFY_NONE = 0,
|
||||
NT_VALUE_NOTIFY_IMMEDIATE = 0x01, /* initial listener addition */
|
||||
NT_VALUE_NOTIFY_LOCAL = 0x02, /* changed locally */
|
||||
/** Event notification flags. */
|
||||
enum NT_EventFlags {
|
||||
NT_EVENT_NONE = 0,
|
||||
/** Initial listener addition. */
|
||||
NT_EVENT_IMMEDIATE = 0x01,
|
||||
/** Client connected (on server, any client connected). */
|
||||
NT_EVENT_CONNECTED = 0x02,
|
||||
/** Client disconnected (on server, any client disconnected). */
|
||||
NT_EVENT_DISCONNECTED = 0x04,
|
||||
/** Any connection event (connect or disconnect). */
|
||||
NT_EVENT_CONNECTION = NT_EVENT_CONNECTED | NT_EVENT_DISCONNECTED,
|
||||
/** New topic published. */
|
||||
NT_EVENT_PUBLISH = 0x08,
|
||||
/** Topic unpublished. */
|
||||
NT_EVENT_UNPUBLISH = 0x10,
|
||||
/** Topic properties changed. */
|
||||
NT_EVENT_PROPERTIES = 0x20,
|
||||
/** Any topic event (publish, unpublish, or properties changed). */
|
||||
NT_EVENT_TOPIC = NT_EVENT_PUBLISH | NT_EVENT_UNPUBLISH | NT_EVENT_PROPERTIES,
|
||||
/** Topic value updated (via network). */
|
||||
NT_EVENT_VALUE_REMOTE = 0x40,
|
||||
/** Topic value updated (local). */
|
||||
NT_EVENT_VALUE_LOCAL = 0x80,
|
||||
/** Topic value updated (network or local). */
|
||||
NT_EVENT_VALUE_ALL = NT_EVENT_VALUE_REMOTE | NT_EVENT_VALUE_LOCAL,
|
||||
/** Log message. */
|
||||
NT_EVENT_LOGMESSAGE = 0x100,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -178,6 +185,7 @@ struct NT_Value {
|
||||
} data;
|
||||
};
|
||||
|
||||
/** NetworkTables Topic Information */
|
||||
struct NT_TopicInfo {
|
||||
/** Topic handle */
|
||||
NT_Topic topic;
|
||||
@@ -221,23 +229,8 @@ struct NT_ConnectionInfo {
|
||||
unsigned int protocol_version;
|
||||
};
|
||||
|
||||
/** NetworkTables Topic Notification */
|
||||
struct NT_TopicNotification {
|
||||
/** Listener that was triggered. */
|
||||
NT_TopicListener listener;
|
||||
|
||||
/** Topic info. */
|
||||
struct NT_TopicInfo info;
|
||||
|
||||
/** Update flags. */
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
/** NetworkTables Value Notification */
|
||||
struct NT_ValueNotification {
|
||||
/** Listener that was triggered. */
|
||||
NT_ValueListener listener;
|
||||
|
||||
/** NetworkTables value event data. */
|
||||
struct NT_ValueEventData {
|
||||
/** Topic handle. */
|
||||
NT_Topic topic;
|
||||
|
||||
@@ -246,31 +239,10 @@ struct NT_ValueNotification {
|
||||
|
||||
/** The new value. */
|
||||
struct NT_Value value;
|
||||
|
||||
/**
|
||||
* Update flags. For example, NT_NOTIFY_NEW if the key did not previously
|
||||
* exist.
|
||||
*/
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
/** NetworkTables Connection Notification */
|
||||
struct NT_ConnectionNotification {
|
||||
/** Listener that was triggered. */
|
||||
NT_ConnectionListener listener;
|
||||
|
||||
/** True if event is due to connection being established. */
|
||||
NT_Bool connected;
|
||||
|
||||
/** Connection info. */
|
||||
struct NT_ConnectionInfo conn;
|
||||
};
|
||||
|
||||
/** NetworkTables log message. */
|
||||
struct NT_LogMessage {
|
||||
/** The logger that generated the message. */
|
||||
NT_Logger logger;
|
||||
|
||||
/** Log level of the message. See NT_LogLevel. */
|
||||
unsigned int level;
|
||||
|
||||
@@ -284,6 +256,30 @@ struct NT_LogMessage {
|
||||
char* message;
|
||||
};
|
||||
|
||||
/** NetworkTables event */
|
||||
struct NT_Event {
|
||||
/** Listener that triggered this event. */
|
||||
NT_Handle listener;
|
||||
|
||||
/**
|
||||
* Event flags (NT_EventFlags). Also indicates the data included with the
|
||||
* event:
|
||||
* - NT_EVENT_CONNECTED or NT_EVENT_DISCONNECTED: connInfo
|
||||
* - NT_EVENT_PUBLISH, NT_EVENT_UNPUBLISH, or NT_EVENT_PROPERTIES: topicInfo
|
||||
* - NT_EVENT_VALUE_REMOTE, NT_NOTIFY_VALUE_LOCAL: valueData
|
||||
* - NT_EVENT_LOGMESSAGE: logMessage
|
||||
*/
|
||||
unsigned int flags;
|
||||
|
||||
/** Event data; content depends on flags. */
|
||||
union {
|
||||
struct NT_ConnectionInfo connInfo;
|
||||
struct NT_TopicInfo topicInfo;
|
||||
struct NT_ValueEventData valueData;
|
||||
struct NT_LogMessage logMessage;
|
||||
} data;
|
||||
};
|
||||
|
||||
/** NetworkTables publish/subscribe option. */
|
||||
struct NT_PubSubOption {
|
||||
/** Option type. */
|
||||
@@ -804,346 +800,184 @@ void NT_UnsubscribeMultiple(NT_MultiSubscriber sub);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup ntcore_topiclistener_cfunc Topic Listener Functions
|
||||
* @defgroup ntcore_listener_cfunc Listener Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Topic listener callback function.
|
||||
* Event listener callback function.
|
||||
*
|
||||
* @param data data pointer provided to callback creation function
|
||||
* @param event event info
|
||||
*/
|
||||
typedef void (*NT_TopicListenerCallback)(
|
||||
void* data, const struct NT_TopicNotification* event);
|
||||
typedef void (*NT_ListenerCallback)(void* data, const struct NT_Event* event);
|
||||
|
||||
/**
|
||||
* Creates a listener poller.
|
||||
*
|
||||
* A poller provides a single queue of poll events. Events linked to this
|
||||
* poller (using NT_AddPolledXListener()) will be stored in the queue and
|
||||
* must be collected by calling NT_ReadListenerQueue().
|
||||
* The returned handle must be destroyed with NT_DestroyListenerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_ListenerPoller NT_CreateListenerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroys a listener poller. This will abort any blocked polling
|
||||
* call and prevent additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void NT_DestroyListenerPoller(NT_ListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Read notifications.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param len length of returned array (output)
|
||||
* @return Array of events. Returns NULL and len=0 if no events since last
|
||||
* call.
|
||||
*/
|
||||
struct NT_Event* NT_ReadListenerQueue(NT_ListenerPoller poller, size_t* len);
|
||||
|
||||
/**
|
||||
* Removes a listener.
|
||||
*
|
||||
* @param listener Listener handle to remove
|
||||
*/
|
||||
void NT_RemoveListener(NT_Listener listener);
|
||||
|
||||
/**
|
||||
* Wait for the listener queue to be empty. This is primarily useful
|
||||
* for deterministic testing. This blocks until either the listener
|
||||
* queue is empty (e.g. there are no more events that need to be passed along to
|
||||
* callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param handle handle
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a
|
||||
* negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
NT_Bool NT_WaitForListenerQueue(NT_Handle handle, double timeout);
|
||||
|
||||
/**
|
||||
* Create a listener for changes to topics with names that start with
|
||||
* the given prefix.
|
||||
* the given prefix. This creates a corresponding internal subscriber with the
|
||||
* lifetime of the listener.
|
||||
*
|
||||
* @param inst Instance handle
|
||||
* @param prefix Topic name string prefix
|
||||
* @param prefix_len Length of topic name string prefix
|
||||
* @param mask Bitmask of NT_TopicListenerFlags values
|
||||
* @param mask Bitmask of NT_EventFlags values (only topic and value events will
|
||||
* be generated)
|
||||
* @param data Data passed to callback function
|
||||
* @param callback Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener NT_AddTopicListener(NT_Inst inst, const char* prefix,
|
||||
size_t prefix_len, unsigned int mask,
|
||||
void* data,
|
||||
NT_TopicListenerCallback callback);
|
||||
NT_Listener NT_AddListenerSingle(NT_Inst inst, const char* prefix,
|
||||
size_t prefix_len, unsigned int mask,
|
||||
void* data, NT_ListenerCallback callback);
|
||||
|
||||
/**
|
||||
* Create a listener for changes to topics with names that start with any of
|
||||
* the given prefixes.
|
||||
* the given prefixes. This creates a corresponding internal subscriber with the
|
||||
* lifetime of the listener.
|
||||
*
|
||||
* @param inst Instance handle
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param prefixes_len Number of elements in prefixes array
|
||||
* @param mask Bitmask of NT_TopicListenerFlags values
|
||||
* @param mask Bitmask of NT_EventFlags values (only topic and value events will
|
||||
* be generated)
|
||||
* @param data Data passed to callback function
|
||||
* @param callback Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener NT_AddTopicListenerMultiple(NT_Inst inst,
|
||||
const struct NT_String* prefixes,
|
||||
size_t prefixes_len,
|
||||
unsigned int mask, void* data,
|
||||
NT_TopicListenerCallback callback);
|
||||
NT_Listener NT_AddListenerMultiple(NT_Inst inst,
|
||||
const struct NT_String* prefixes,
|
||||
size_t prefixes_len, unsigned int mask,
|
||||
void* data, NT_ListenerCallback callback);
|
||||
|
||||
/**
|
||||
* Create a listener for changes on a particular topic.
|
||||
* Create a listener.
|
||||
*
|
||||
* @param topic Topic handle
|
||||
* @param mask Bitmask of NT_TopicListenerFlags values
|
||||
* Some combinations of handle and mask have no effect:
|
||||
* - connection and log message events are only generated on instances
|
||||
* - topic and value events are only generated on non-instances
|
||||
*
|
||||
* Adding value and topic events on a topic will create a corresponding internal
|
||||
* subscriber with the lifetime of the listener.
|
||||
*
|
||||
* Adding a log message listener through this function will only result in
|
||||
* events at NT_LOG_INFO or higher; for more customized settings, use
|
||||
* NT_AddLogger().
|
||||
*
|
||||
* @param handle Handle
|
||||
* @param mask Bitmask of NT_EventFlags values
|
||||
* @param data Data passed to callback function
|
||||
* @param callback Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener NT_AddTopicListenerSingle(NT_Topic topic, unsigned int mask,
|
||||
void* data,
|
||||
NT_TopicListenerCallback callback);
|
||||
NT_Listener NT_AddListener(NT_Handle handle, unsigned int mask, void* data,
|
||||
NT_ListenerCallback callback);
|
||||
|
||||
/**
|
||||
* Wait for the topic listener queue to be empty. This is primarily useful
|
||||
* for deterministic testing. This blocks until either the topic listener
|
||||
* queue is empty (e.g. there are no more events that need to be passed along to
|
||||
* callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param handle handle
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a
|
||||
* negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
NT_Bool NT_WaitForTopicListenerQueue(NT_Handle handle, double timeout);
|
||||
|
||||
/**
|
||||
* Creates a topic listener poller.
|
||||
*
|
||||
* A poller provides a single queue of poll events. Events linked to this
|
||||
* poller (using NT_AddPolledTopicListener()) will be stored in the queue and
|
||||
* must be collected by calling NT_ReadTopicListenerQueue().
|
||||
* The returned handle must be destroyed with NT_DestroyTopicListenerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_TopicListenerPoller NT_CreateTopicListenerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroys a topic listener poller. This will abort any blocked polling
|
||||
* call and prevent additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void NT_DestroyTopicListenerPoller(NT_TopicListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Read topic notifications.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param len length of returned array (output)
|
||||
* @return Array of topic notifications. Returns NULL and len=0 if no
|
||||
* notifications since last call.
|
||||
*/
|
||||
struct NT_TopicNotification* NT_ReadTopicListenerQueue(
|
||||
NT_TopicListenerPoller poller, size_t* len);
|
||||
|
||||
/**
|
||||
* Creates a polled topic listener.
|
||||
* The caller is responsible for calling NT_ReadTopicListenerQueue() to poll.
|
||||
* Creates a polled topic listener. This creates a corresponding internal
|
||||
* subscriber with the lifetime of the listener.
|
||||
* The caller is responsible for calling NT_ReadListenerQueue() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param prefix UTF-8 string prefix
|
||||
* @param prefix_len Length of UTF-8 string prefix
|
||||
* @param mask NT_NotifyKind bitmask
|
||||
* @param mask NT_EventFlags bitmask (only topic and value events
|
||||
* will be generated)
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener NT_AddPolledTopicListener(NT_TopicListenerPoller poller,
|
||||
const char* prefix,
|
||||
size_t prefix_len,
|
||||
unsigned int mask);
|
||||
NT_Listener NT_AddPolledListenerSingle(NT_ListenerPoller poller,
|
||||
const char* prefix, size_t prefix_len,
|
||||
unsigned int mask);
|
||||
|
||||
/**
|
||||
* Creates a polled topic listener.
|
||||
* The caller is responsible for calling NT_ReadTopicListenerQueue() to poll.
|
||||
* Creates a polled topic listener. This creates a corresponding internal
|
||||
* subscriber with the lifetime of the listener.
|
||||
* The caller is responsible for calling NT_ReadListenerQueue() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param prefixes array of UTF-8 string prefixes
|
||||
* @param prefixes_len Length of prefixes array
|
||||
* @param mask NT_EventFlags bitmask (only topic and value events
|
||||
* will be generated)
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener NT_AddPolledListenerMultiple(NT_ListenerPoller poller,
|
||||
const struct NT_String* prefixes,
|
||||
size_t prefixes_len,
|
||||
unsigned int mask);
|
||||
|
||||
/**
|
||||
* Creates a polled listener.
|
||||
* The caller is responsible for calling NT_ReadListenerQueue() to poll.
|
||||
*
|
||||
* Some combinations of handle and mask have no effect:
|
||||
* - connection and log message events are only generated on instances
|
||||
* - topic and value events are only generated on non-instances
|
||||
*
|
||||
* Adding value and topic events on a topic will create a corresponding internal
|
||||
* subscriber with the lifetime of the listener.
|
||||
*
|
||||
* Adding a log message listener through this function will only result in
|
||||
* events at NT_LOG_INFO or higher; for more customized settings, use
|
||||
* NT_AddPolledLogger().
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param handle handle
|
||||
* @param mask NT_NotifyKind bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener NT_AddPolledTopicListenerMultiple(
|
||||
NT_TopicListenerPoller poller, const struct NT_String* prefixes,
|
||||
size_t prefixes_len, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Creates a polled topic listener.
|
||||
* The caller is responsible for calling NT_ReadTopicListenerQueue() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param topic topic
|
||||
* @param mask NT_NotifyKind bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener NT_AddPolledTopicListenerSingle(NT_TopicListenerPoller poller,
|
||||
NT_Topic topic,
|
||||
unsigned int mask);
|
||||
|
||||
/**
|
||||
* Removes a topic listener.
|
||||
*
|
||||
* @param topic_listener Listener handle to remove
|
||||
*/
|
||||
void NT_RemoveTopicListener(NT_TopicListener topic_listener);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup ntcore_valuelistener_cfunc Value Listener Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Value listener callback function.
|
||||
*
|
||||
* @param data data pointer provided to callback creation function
|
||||
* @param event event info
|
||||
*/
|
||||
typedef void (*NT_ValueListenerCallback)(
|
||||
void* data, const struct NT_ValueNotification* event);
|
||||
|
||||
/**
|
||||
* Create a listener for value changes on a subscriber.
|
||||
*
|
||||
* @param subentry Subscriber/entry
|
||||
* @param mask Bitmask of NT_ValueListenerFlags values
|
||||
* @param data Data passed to listener function
|
||||
* @param callback Listener function
|
||||
*/
|
||||
NT_ValueListener NT_AddValueListener(NT_Handle subentry, unsigned int mask,
|
||||
void* data,
|
||||
NT_ValueListenerCallback callback);
|
||||
|
||||
/**
|
||||
* Wait for the value listener queue to be empty. This is primarily useful
|
||||
* for deterministic testing. This blocks until either the value listener
|
||||
* queue is empty (e.g. there are no more events that need to be passed along to
|
||||
* callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param handle handle
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a
|
||||
* negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
NT_Bool NT_WaitForValueListenerQueue(NT_Handle handle, double timeout);
|
||||
|
||||
/**
|
||||
* Create a value listener poller.
|
||||
*
|
||||
* A poller provides a single queue of poll events. Events linked to this
|
||||
* poller (using NT_AddPolledValueListener()) will be stored in the queue and
|
||||
* must be collected by calling NT_ReadValueListenerQueue().
|
||||
* The returned handle must be destroyed with NT_DestroyValueListenerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_ValueListenerPoller NT_CreateValueListenerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroy a value listener poller. This will abort any blocked polling
|
||||
* call and prevent additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void NT_DestroyValueListenerPoller(NT_ValueListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Reads value listener queue (all value changes since last call).
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param len length of returned array (output)
|
||||
* @return Array of value notifications. Returns NULL and len=0 if no
|
||||
* notifications since last call.
|
||||
*/
|
||||
struct NT_ValueNotification* NT_ReadValueListenerQueue(
|
||||
NT_ValueListenerPoller poller, size_t* len);
|
||||
|
||||
/**
|
||||
* Create a polled value listener.
|
||||
* The caller is responsible for calling NT_ReadValueListenerQueue() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param subentry subscriber or entry handle
|
||||
* @param flags NT_NotifyKind bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ValueListener NT_AddPolledValueListener(NT_ValueListenerPoller poller,
|
||||
NT_Handle subentry,
|
||||
unsigned int flags);
|
||||
|
||||
/**
|
||||
* Remove a value listener.
|
||||
*
|
||||
* @param value_listener Listener handle to remove
|
||||
*/
|
||||
void NT_RemoveValueListener(NT_ValueListener value_listener);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup ntcore_connectionlistener_cfunc Connection Listener Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Connection listener callback function.
|
||||
* Called when a network connection is made or lost.
|
||||
*
|
||||
* @param data data pointer provided to callback creation function
|
||||
* @param event event info
|
||||
*/
|
||||
typedef void (*NT_ConnectionListenerCallback)(
|
||||
void* data, const struct NT_ConnectionNotification* event);
|
||||
|
||||
/**
|
||||
* Add a connection listener.
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @param data data pointer to pass to callback
|
||||
* @param callback listener to add
|
||||
* @param immediate_notify notify listener of all existing connections
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ConnectionListener NT_AddConnectionListener(
|
||||
NT_Inst inst, NT_Bool immediate_notify, void* data,
|
||||
NT_ConnectionListenerCallback callback);
|
||||
|
||||
/**
|
||||
* Wait for the connection listener queue to be empty. This is primarily useful
|
||||
* for deterministic testing. This blocks until either the connection listener
|
||||
* queue is empty (e.g. there are no more events that need to be passed along to
|
||||
* callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param handle handle
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a
|
||||
* negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
NT_Bool NT_WaitForConnectionListenerQueue(NT_Handle handle, double timeout);
|
||||
|
||||
/**
|
||||
* Create a connection listener poller.
|
||||
* A poller provides a single queue of poll events. Events linked to this
|
||||
* poller (using NT_AddPolledConnectionListener()) will be stored in the queue
|
||||
* and must be collected by calling NT_PollConnectionListener().
|
||||
* The returned handle must be destroyed with
|
||||
* NT_DestroyConnectionListenerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_ConnectionListenerPoller NT_CreateConnectionListenerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroy a connection listener poller. This will abort any blocked polling
|
||||
* call and prevent additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void NT_DestroyConnectionListenerPoller(NT_ConnectionListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Create a polled connection listener.
|
||||
* The caller is responsible for calling NT_PollConnectionListener() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param immediate_notify notify listener of all existing connections
|
||||
*/
|
||||
NT_ConnectionListener NT_AddPolledConnectionListener(
|
||||
NT_ConnectionListenerPoller poller, NT_Bool immediate_notify);
|
||||
|
||||
/**
|
||||
* Get the next connection event. This blocks until the next connect or
|
||||
* disconnect occurs. This is intended to be used with
|
||||
* NT_AddPolledConnectionListener(); connection listeners created using
|
||||
* NT_AddConnectionListener() will not be serviced through this function.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param len length of returned array (output)
|
||||
* @return Array of information on the connection events. Only returns NULL
|
||||
* if an error occurred (e.g. the instance was invalid or is shutting
|
||||
* down).
|
||||
*/
|
||||
struct NT_ConnectionNotification* NT_ReadConnectionListenerQueue(
|
||||
NT_ConnectionListenerPoller poller, size_t* len);
|
||||
|
||||
/**
|
||||
* Remove a connection listener.
|
||||
*
|
||||
* @param conn_listener Listener handle to remove
|
||||
*/
|
||||
void NT_RemoveConnectionListener(NT_ConnectionListener conn_listener);
|
||||
NT_Listener NT_AddPolledListener(NT_ListenerPoller poller, NT_Handle handle,
|
||||
unsigned int mask);
|
||||
|
||||
/** @} */
|
||||
|
||||
@@ -1390,67 +1224,19 @@ void NT_DisposeTopicInfoArray(struct NT_TopicInfo* arr, size_t count);
|
||||
void NT_DisposeTopicInfo(struct NT_TopicInfo* info);
|
||||
|
||||
/**
|
||||
* Disposes an topic notification array.
|
||||
* Disposes an event array.
|
||||
*
|
||||
* @param arr pointer to the array to dispose
|
||||
* @param count number of elements in the array
|
||||
*/
|
||||
void NT_DisposeTopicNotificationArray(struct NT_TopicNotification* arr,
|
||||
size_t count);
|
||||
void NT_DisposeEventArray(struct NT_Event* arr, size_t count);
|
||||
|
||||
/**
|
||||
* Disposes a single topic notification.
|
||||
* Disposes a single event.
|
||||
*
|
||||
* @param info pointer to the info to dispose
|
||||
* @param event pointer to the event to dispose
|
||||
*/
|
||||
void NT_DisposeTopicNotification(struct NT_TopicNotification* info);
|
||||
|
||||
/**
|
||||
* Disposes an value notification array.
|
||||
*
|
||||
* @param arr pointer to the array to dispose
|
||||
* @param count number of elements in the array
|
||||
*/
|
||||
void NT_DisposeValueNotificationArray(struct NT_ValueNotification* arr,
|
||||
size_t count);
|
||||
|
||||
/**
|
||||
* Disposes a single value notification.
|
||||
*
|
||||
* @param info pointer to the info to dispose
|
||||
*/
|
||||
void NT_DisposeValueNotification(struct NT_ValueNotification* info);
|
||||
|
||||
/**
|
||||
* Disposes a connection notification array.
|
||||
*
|
||||
* @param arr pointer to the array to dispose
|
||||
* @param count number of elements in the array
|
||||
*/
|
||||
void NT_DisposeConnectionNotificationArray(
|
||||
struct NT_ConnectionNotification* arr, size_t count);
|
||||
|
||||
/**
|
||||
* Disposes a single connection notification.
|
||||
*
|
||||
* @param info pointer to the info to dispose
|
||||
*/
|
||||
void NT_DisposeConnectionNotification(struct NT_ConnectionNotification* info);
|
||||
|
||||
/**
|
||||
* Disposes a log message array.
|
||||
*
|
||||
* @param arr pointer to the array to dispose
|
||||
* @param count number of elements in the array
|
||||
*/
|
||||
void NT_DisposeLogMessageArray(struct NT_LogMessage* arr, size_t count);
|
||||
|
||||
/**
|
||||
* Disposes a single log message.
|
||||
*
|
||||
* @param info pointer to the info to dispose
|
||||
*/
|
||||
void NT_DisposeLogMessage(struct NT_LogMessage* info);
|
||||
void NT_DisposeEvent(struct NT_Event* event);
|
||||
|
||||
/**
|
||||
* Returns monotonic current time in 1 us increments.
|
||||
@@ -1481,14 +1267,6 @@ void NT_SetNow(uint64_t timestamp);
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Log function.
|
||||
*
|
||||
* @param data data pointer passed to NT_AddLogger()
|
||||
* @param msg message information
|
||||
*/
|
||||
typedef void (*NT_LogFunc)(void* data, const struct NT_LogMessage* msg);
|
||||
|
||||
/**
|
||||
* Add logger callback function. By default, log messages are sent to stderr;
|
||||
* this function sends log messages to the provided callback function instead.
|
||||
@@ -1497,61 +1275,28 @@ typedef void (*NT_LogFunc)(void* data, const struct NT_LogMessage* msg);
|
||||
* messages outside this range will be silently ignored.
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @param data data pointer to pass to func
|
||||
* @param func log callback function
|
||||
* @param min_level minimum log level
|
||||
* @param max_level maximum log level
|
||||
* @return Logger handle
|
||||
* @param data data pointer to pass to func
|
||||
* @param func listener callback function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Logger NT_AddLogger(NT_Inst inst, void* data, NT_LogFunc func,
|
||||
unsigned int min_level, unsigned int max_level);
|
||||
NT_Listener NT_AddLogger(NT_Inst inst, unsigned int min_level,
|
||||
unsigned int max_level, void* data,
|
||||
NT_ListenerCallback func);
|
||||
|
||||
/**
|
||||
* Create a log poller. A poller provides a single queue of poll events.
|
||||
* The returned handle must be destroyed with NT_DestroyLoggerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_LoggerPoller NT_CreateLoggerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroy a log poller. This will abort any blocked polling call and prevent
|
||||
* additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void NT_DestroyLoggerPoller(NT_LoggerPoller poller);
|
||||
|
||||
/**
|
||||
* Set the log level for a log poller. Events will only be generated for
|
||||
* Set the log level for a listener poller. Events will only be generated for
|
||||
* log messages with level greater than or equal to min_level and less than or
|
||||
* equal to max_level; messages outside this range will be silently ignored.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param min_level minimum log level
|
||||
* @param max_level maximum log level
|
||||
* @return Logger handle
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Logger NT_AddPolledLogger(NT_LoggerPoller poller, unsigned int min_level,
|
||||
unsigned int max_level);
|
||||
|
||||
/**
|
||||
* Get the next log event. This blocks until the next log occurs.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param len length of returned array (output)
|
||||
* @return Array of information on the log events. Only returns NULL if an
|
||||
* error occurred (e.g. the instance was invalid or is shutting down).
|
||||
*/
|
||||
struct NT_LogMessage* NT_ReadLoggerQueue(NT_LoggerPoller poller, size_t* len);
|
||||
|
||||
/**
|
||||
* Remove a logger.
|
||||
*
|
||||
* @param logger Logger handle to remove
|
||||
*/
|
||||
void NT_RemoveLogger(NT_Logger logger);
|
||||
NT_Listener NT_AddPolledLogger(NT_ListenerPoller poller, unsigned int min_level,
|
||||
unsigned int max_level);
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
@@ -40,6 +41,47 @@ namespace nt {
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Event notification flags.
|
||||
*
|
||||
* The flags are a bitmask and must be OR'ed together to indicate the
|
||||
* combination of events desired to be received.
|
||||
*
|
||||
*/
|
||||
struct EventFlags {
|
||||
EventFlags() = delete;
|
||||
|
||||
static constexpr unsigned int kNone = NT_EVENT_NONE;
|
||||
/**
|
||||
* Initial listener addition.
|
||||
* Set this flag to receive immediate notification of matches to the
|
||||
* flag criteria.
|
||||
*/
|
||||
static constexpr unsigned int kImmediate = NT_EVENT_IMMEDIATE;
|
||||
/** Client connected (on server, any client connected). */
|
||||
static constexpr unsigned int kConnected = NT_EVENT_CONNECTED;
|
||||
/** Client disconnected (on server, any client disconnected). */
|
||||
static constexpr unsigned int kDisconnected = NT_EVENT_DISCONNECTED;
|
||||
/** Any connection event (connect or disconnect). */
|
||||
static constexpr unsigned int kConnection = kConnected | kDisconnected;
|
||||
/** New topic published. */
|
||||
static constexpr unsigned int kPublish = NT_EVENT_PUBLISH;
|
||||
/** Topic unpublished. */
|
||||
static constexpr unsigned int kUnpublish = NT_EVENT_UNPUBLISH;
|
||||
/** Topic properties changed. */
|
||||
static constexpr unsigned int kProperties = NT_EVENT_PROPERTIES;
|
||||
/** Any topic event (publish, unpublish, or properties changed). */
|
||||
static constexpr unsigned int kTopic = kPublish | kUnpublish | kProperties;
|
||||
/** Topic value updated (via network). */
|
||||
static constexpr unsigned int kValueRemote = NT_EVENT_VALUE_REMOTE;
|
||||
/** Topic value updated (local). */
|
||||
static constexpr unsigned int kValueLocal = NT_EVENT_VALUE_LOCAL;
|
||||
/** Topic value updated (network or local). */
|
||||
static constexpr unsigned int kValueAll = kValueRemote | kValueLocal;
|
||||
/** Log message. */
|
||||
static constexpr unsigned int kLogMessage = NT_EVENT_LOGMESSAGE;
|
||||
};
|
||||
|
||||
/** NetworkTables Topic Information */
|
||||
struct TopicInfo {
|
||||
/** Topic handle */
|
||||
@@ -106,47 +148,12 @@ struct ConnectionInfo {
|
||||
}
|
||||
};
|
||||
|
||||
/** NetworkTables Topic Notification */
|
||||
class TopicNotification {
|
||||
/** NetworkTables Value Event Data */
|
||||
class ValueEventData {
|
||||
public:
|
||||
TopicNotification() = default;
|
||||
TopicNotification(NT_TopicListener listener_, TopicInfo info_,
|
||||
unsigned int flags_)
|
||||
: listener(listener_), info(std::move(info_)), flags(flags_) {}
|
||||
|
||||
/** Listener that was triggered. */
|
||||
NT_TopicListener listener{0};
|
||||
|
||||
/** Topic info. */
|
||||
TopicInfo info;
|
||||
|
||||
/**
|
||||
* Notification flags.
|
||||
*/
|
||||
unsigned int flags{0};
|
||||
|
||||
friend void swap(TopicNotification& first, TopicNotification& second) {
|
||||
using std::swap;
|
||||
swap(first.listener, second.listener);
|
||||
swap(first.info, second.info);
|
||||
swap(first.flags, second.flags);
|
||||
}
|
||||
};
|
||||
|
||||
/** NetworkTables Value Notification */
|
||||
class ValueNotification {
|
||||
public:
|
||||
ValueNotification() = default;
|
||||
ValueNotification(NT_ValueListener listener_, NT_Topic topic_,
|
||||
NT_Handle subentry_, Value value_, unsigned int flags_)
|
||||
: listener(listener_),
|
||||
topic(topic_),
|
||||
subentry(subentry_),
|
||||
value(std::move(value_)),
|
||||
flags(flags_) {}
|
||||
|
||||
/** Listener that was triggered. */
|
||||
NT_ValueListener listener{0};
|
||||
ValueEventData() = default;
|
||||
ValueEventData(NT_Topic topic, NT_Handle subentry, Value value)
|
||||
: topic{topic}, subentry{subentry}, value{std::move(value)} {}
|
||||
|
||||
/** Topic handle. */
|
||||
NT_Topic topic{0};
|
||||
@@ -156,63 +163,15 @@ class ValueNotification {
|
||||
|
||||
/** The new value. */
|
||||
Value value;
|
||||
|
||||
/**
|
||||
* Update flags. For example, NT_NOTIFY_NEW if the key did not previously
|
||||
* exist.
|
||||
*/
|
||||
unsigned int flags{0};
|
||||
|
||||
friend void swap(ValueNotification& first, ValueNotification& second) {
|
||||
using std::swap;
|
||||
swap(first.listener, second.listener);
|
||||
swap(first.topic, second.topic);
|
||||
swap(first.subentry, second.subentry);
|
||||
swap(first.value, second.value);
|
||||
swap(first.flags, second.flags);
|
||||
}
|
||||
};
|
||||
|
||||
/** NetworkTables Connection Notification */
|
||||
class ConnectionNotification {
|
||||
public:
|
||||
ConnectionNotification() = default;
|
||||
ConnectionNotification(NT_ConnectionListener listener_, bool connected_,
|
||||
ConnectionInfo conn_)
|
||||
: listener(listener_), connected(connected_), conn(std::move(conn_)) {}
|
||||
|
||||
/** Listener that was triggered. */
|
||||
NT_ConnectionListener listener{0};
|
||||
|
||||
/** True if event is due to connection being established. */
|
||||
bool connected = false;
|
||||
|
||||
/** Connection info. */
|
||||
ConnectionInfo conn;
|
||||
|
||||
friend void swap(ConnectionNotification& first,
|
||||
ConnectionNotification& second) {
|
||||
using std::swap;
|
||||
swap(first.listener, second.listener);
|
||||
swap(first.connected, second.connected);
|
||||
swap(first.conn, second.conn);
|
||||
}
|
||||
};
|
||||
|
||||
/** NetworkTables log message. */
|
||||
class LogMessage {
|
||||
public:
|
||||
LogMessage() = default;
|
||||
LogMessage(NT_Logger logger_, unsigned int level_, std::string_view filename_,
|
||||
unsigned int line_, std::string_view message_)
|
||||
: logger(logger_),
|
||||
level(level_),
|
||||
filename(filename_),
|
||||
line(line_),
|
||||
message(message_) {}
|
||||
|
||||
/** The logger that generated the message. */
|
||||
NT_Logger logger{0};
|
||||
LogMessage(unsigned int level, std::string_view filename, unsigned int line,
|
||||
std::string_view message)
|
||||
: level{level}, filename{filename}, line{line}, message{message} {}
|
||||
|
||||
/** Log level of the message. See NT_LogLevel. */
|
||||
unsigned int level{0};
|
||||
@@ -225,15 +184,71 @@ class LogMessage {
|
||||
|
||||
/** The message. */
|
||||
std::string message;
|
||||
};
|
||||
|
||||
friend void swap(LogMessage& first, LogMessage& second) {
|
||||
using std::swap;
|
||||
swap(first.logger, second.logger);
|
||||
swap(first.level, second.level);
|
||||
swap(first.filename, second.filename);
|
||||
swap(first.line, second.line);
|
||||
swap(first.message, second.message);
|
||||
/** NetworkTables event */
|
||||
class Event {
|
||||
public:
|
||||
Event() = default;
|
||||
Event(NT_Listener listener, unsigned int flags, ConnectionInfo info)
|
||||
: listener{listener}, flags{flags}, data{std::move(info)} {}
|
||||
Event(NT_Listener listener, unsigned int flags, TopicInfo info)
|
||||
: listener{listener}, flags{flags}, data{std::move(info)} {}
|
||||
Event(NT_Listener listener, unsigned int flags, ValueEventData data)
|
||||
: listener{listener}, flags{flags}, data{std::move(data)} {}
|
||||
Event(NT_Listener listener, unsigned int flags, LogMessage msg)
|
||||
: listener{listener}, flags{flags}, data{std::move(msg)} {}
|
||||
Event(NT_Listener listener, unsigned int flags, NT_Topic topic,
|
||||
NT_Handle subentry, Value value)
|
||||
: listener{listener},
|
||||
flags{flags},
|
||||
data{ValueEventData{topic, subentry, std::move(value)}} {}
|
||||
Event(NT_Listener listener, unsigned int flags, unsigned int level,
|
||||
std::string_view filename, unsigned int line, std::string_view message)
|
||||
: listener{listener},
|
||||
flags{flags},
|
||||
data{LogMessage{level, filename, line, message}} {}
|
||||
|
||||
/** Listener that triggered this event. */
|
||||
NT_Listener listener{0};
|
||||
|
||||
/**
|
||||
* Event flags (NT_EventFlags). Also indicates the data included with the
|
||||
* event:
|
||||
* - NT_NOTIFY_CONNECTED or NT_NOTIFY_DISCONNECTED: GetConnectionInfo()
|
||||
* - NT_NOTIFY_PUBLISH, NT_NOTIFY_UNPUBLISH, or NT_NOTIFY_PROPERTIES:
|
||||
* GetTopicInfo()
|
||||
* - NT_NOTIFY_VALUE, NT_NOTIFY_VALUE_LOCAL: GetValueData()
|
||||
* - NT_NOTIFY_LOGMESSAGE: GetLogMessage()
|
||||
*/
|
||||
unsigned int flags{0};
|
||||
|
||||
/** Event data; content depends on flags. */
|
||||
std::variant<ConnectionInfo, TopicInfo, ValueEventData, LogMessage> data;
|
||||
|
||||
const ConnectionInfo* GetConnectionInfo() const {
|
||||
return std::get_if<nt::ConnectionInfo>(&data);
|
||||
}
|
||||
ConnectionInfo* GetConnectionInfo() {
|
||||
return std::get_if<nt::ConnectionInfo>(&data);
|
||||
}
|
||||
|
||||
const TopicInfo* GetTopicInfo() const {
|
||||
return std::get_if<nt::TopicInfo>(&data);
|
||||
}
|
||||
TopicInfo* GetTopicInfo() { return std::get_if<nt::TopicInfo>(&data); }
|
||||
|
||||
const ValueEventData* GetValueEventData() const {
|
||||
return std::get_if<nt::ValueEventData>(&data);
|
||||
}
|
||||
ValueEventData* GetValueEventData() {
|
||||
return std::get_if<nt::ValueEventData>(&data);
|
||||
}
|
||||
|
||||
const LogMessage* GetLogMessage() const {
|
||||
return std::get_if<nt::LogMessage>(&data);
|
||||
}
|
||||
LogMessage* GetLogMessage() { return std::get_if<nt::LogMessage>(&data); }
|
||||
};
|
||||
|
||||
/** NetworkTables publish/subscribe option. */
|
||||
@@ -771,272 +786,134 @@ void UnsubscribeMultiple(NT_MultiSubscriber sub);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup ntcore_topiclistener_func Topic Listener Functions
|
||||
* @defgroup ntcore_listener_func Listener Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
using ListenerCallback = std::function<void(const Event&)>;
|
||||
|
||||
/**
|
||||
* Creates a listener poller.
|
||||
*
|
||||
* A poller provides a single queue of poll events. Events linked to this
|
||||
* poller (using AddPolledListener()) will be stored in the queue and
|
||||
* must be collected by calling ReadListenerQueue().
|
||||
* The returned handle must be destroyed with DestroyListenerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_ListenerPoller CreateListenerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroys a listener poller. This will abort any blocked polling
|
||||
* call and prevent additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void DestroyListenerPoller(NT_ListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Read notifications.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @return Array of events. Returns empty array if no events since last call.
|
||||
*/
|
||||
std::vector<Event> ReadListenerQueue(NT_ListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Removes a listener.
|
||||
*
|
||||
* @param listener Listener handle to remove
|
||||
*/
|
||||
void RemoveListener(NT_Listener listener);
|
||||
|
||||
/**
|
||||
* Wait for the listener queue to be empty. This is primarily useful
|
||||
* for deterministic testing. This blocks until either the listener
|
||||
* queue is empty (e.g. there are no more events that need to be passed along to
|
||||
* callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param handle handle
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a
|
||||
* negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForListenerQueue(NT_Handle handle, double timeout);
|
||||
|
||||
/**
|
||||
* Create a listener for changes to topics with names that start with any of
|
||||
* the given prefixes.
|
||||
* the given prefixes. This creates a corresponding internal subscriber with the
|
||||
* lifetime of the listener.
|
||||
*
|
||||
* @param inst Instance handle
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param mask Bitmask of NT_TopicListenerFlags values
|
||||
* @param mask Bitmask of NT_EventFlags values (only topic and value events will
|
||||
* be generated)
|
||||
* @param callback Listener function
|
||||
*/
|
||||
NT_TopicListener AddTopicListener(
|
||||
NT_Inst inst, std::span<const std::string_view> prefixes, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> callback);
|
||||
NT_Listener AddListener(NT_Inst inst,
|
||||
std::span<const std::string_view> prefixes,
|
||||
unsigned int mask, ListenerCallback callback);
|
||||
|
||||
/**
|
||||
* Create a listener for changes on a particular topic.
|
||||
* Create a listener.
|
||||
*
|
||||
* @param handle Topic, subscriber, multi-subscriber, or entry handle
|
||||
* @param mask Bitmask of NT_TopicListenerFlags values
|
||||
* Some combinations of handle and mask have no effect:
|
||||
* - connection and log message events are only generated on instances
|
||||
* - topic and value events are only generated on non-instances
|
||||
*
|
||||
* Adding value and topic events on a topic will create a corresponding internal
|
||||
* subscriber with the lifetime of the listener.
|
||||
*
|
||||
* Adding a log message listener through this function will only result in
|
||||
* events at NT_LOG_INFO or higher; for more customized settings, use
|
||||
* AddLogger().
|
||||
*
|
||||
* @param handle Instance, topic, subscriber, multi-subscriber, or entry handle
|
||||
* @param mask Bitmask of NT_EventFlags values
|
||||
* @param callback Listener function
|
||||
*/
|
||||
NT_TopicListener AddTopicListener(
|
||||
NT_Handle handle, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> callback);
|
||||
NT_Listener AddListener(NT_Handle handle, unsigned int mask,
|
||||
ListenerCallback callback);
|
||||
|
||||
/**
|
||||
* Wait for the topic listener queue to be empty. This is primarily useful
|
||||
* for deterministic testing. This blocks until either the topic listener
|
||||
* queue is empty (e.g. there are no more events that need to be passed along to
|
||||
* callbacks or poll queues) or the timeout expires.
|
||||
* Creates a polled listener. This creates a corresponding internal subscriber
|
||||
* with the lifetime of the listener.
|
||||
* The caller is responsible for calling ReadListenerQueue() to poll.
|
||||
*
|
||||
* @param handle handle
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a
|
||||
* negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForTopicListenerQueue(NT_Handle handle, double timeout);
|
||||
|
||||
/**
|
||||
* Creates a topic listener poller.
|
||||
*
|
||||
* A poller provides a single queue of poll events. Events linked to this
|
||||
* poller (using AddPolledTopicListener()) will be stored in the queue and
|
||||
* must be collected by calling ReadTopicListenerQueue().
|
||||
* The returned handle must be destroyed with DestroyTopicListenerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_TopicListenerPoller CreateTopicListenerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroys a topic listener poller. This will abort any blocked polling
|
||||
* call and prevent additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void DestroyTopicListenerPoller(NT_TopicListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Read topic notifications.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @return Array of topic notifications. Returns empty array if no
|
||||
* notifications since last call.
|
||||
*/
|
||||
std::vector<TopicNotification> ReadTopicListenerQueue(
|
||||
NT_TopicListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Creates a polled topic listener.
|
||||
* The caller is responsible for calling ReadTopicListenerQueue() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param prefixes array of UTF-8 string prefixes
|
||||
* @param mask NT_NotifyKind bitmask
|
||||
* @param poller poller handle
|
||||
* @param prefixes array of UTF-8 string prefixes
|
||||
* @param mask Bitmask of NT_EventFlags values (only topic and value events will
|
||||
* be generated)
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener AddPolledTopicListener(
|
||||
NT_TopicListenerPoller poller, std::span<const std::string_view> prefixes,
|
||||
unsigned int mask);
|
||||
NT_Listener AddPolledListener(NT_ListenerPoller poller,
|
||||
std::span<const std::string_view> prefixes,
|
||||
unsigned int mask);
|
||||
|
||||
/**
|
||||
* Creates a polled topic listener.
|
||||
* The caller is responsible for calling ReadTopicListenerQueue() to poll.
|
||||
* Creates a polled listener.
|
||||
* The caller is responsible for calling ReadListenerQueue() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param handle topic, subscriber, multi-subscriber, or entry handle
|
||||
* @param mask NT_NotifyKind bitmask
|
||||
* Some combinations of handle and mask have no effect:
|
||||
* - connection and log message events are only generated on instances
|
||||
* - topic and value events are only generated on non-instances
|
||||
*
|
||||
* Adding value and topic events on a topic will create a corresponding internal
|
||||
* subscriber with the lifetime of the listener.
|
||||
*
|
||||
* Adding a log message listener through this function will only result in
|
||||
* events at NT_LOG_INFO or higher; for more customized settings, use
|
||||
* AddPolledLogger().
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param handle instance, topic, subscriber, multi-subscriber, or entry handle
|
||||
* @param mask NT_EventFlags bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener AddPolledTopicListener(NT_TopicListenerPoller poller,
|
||||
NT_Handle handle, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Removes a topic listener.
|
||||
*
|
||||
* @param listener Listener handle to remove
|
||||
*/
|
||||
void RemoveTopicListener(NT_TopicListener listener);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup ntcore_valuelistener_func Value Listener Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a listener for value changes on a subscriber.
|
||||
*
|
||||
* @param subentry Subscriber/entry
|
||||
* @param mask Bitmask of NT_ValueListenerFlags values
|
||||
* @param callback Listener function
|
||||
*/
|
||||
NT_ValueListener AddValueListener(
|
||||
NT_Handle subentry, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> callback);
|
||||
|
||||
/**
|
||||
* Wait for the value listener queue to be empty. This is primarily useful
|
||||
* for deterministic testing. This blocks until either the value listener
|
||||
* queue is empty (e.g. there are no more events that need to be passed along to
|
||||
* callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param handle handle
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a
|
||||
* negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForValueListenerQueue(NT_Handle handle, double timeout);
|
||||
|
||||
/**
|
||||
* Create a value listener poller.
|
||||
*
|
||||
* A poller provides a single queue of poll events. Events linked to this
|
||||
* poller (using AddPolledValueListener()) will be stored in the queue and
|
||||
* must be collected by calling ReadValueListenerQueue().
|
||||
* The returned handle must be destroyed with DestroyValueListenerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_ValueListenerPoller CreateValueListenerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroy a value listener poller. This will abort any blocked polling
|
||||
* call and prevent additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void DestroyValueListenerPoller(NT_ValueListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Reads value listener queue (all value changes since last call).
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @return Array of value notifications.
|
||||
*/
|
||||
std::vector<ValueNotification> ReadValueListenerQueue(
|
||||
NT_ValueListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Create a polled value listener.
|
||||
* The caller is responsible for calling ReadValueListenerQueue() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param subentry subscriber or entry handle
|
||||
* @param mask NT_NotifyKind bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ValueListener AddPolledValueListener(NT_ValueListenerPoller poller,
|
||||
NT_Handle subentry, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Remove a value listener.
|
||||
*
|
||||
* @param listener Listener handle to remove
|
||||
*/
|
||||
void RemoveValueListener(NT_ValueListener listener);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup ntcore_connectionlistener_func Connection Listener Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add a connection listener.
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @param callback listener to add
|
||||
* @param immediate_notify notify listener of all existing connections
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ConnectionListener AddConnectionListener(
|
||||
NT_Inst inst, bool immediate_notify,
|
||||
std::function<void(const ConnectionNotification& event)> callback);
|
||||
|
||||
/**
|
||||
* Wait for the connection listener queue to be empty. This is primarily useful
|
||||
* for deterministic testing. This blocks until either the connection listener
|
||||
* queue is empty (e.g. there are no more events that need to be passed along to
|
||||
* callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param handle handle
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a
|
||||
* negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForConnectionListenerQueue(NT_Handle handle, double timeout);
|
||||
|
||||
/**
|
||||
* Create a connection listener poller.
|
||||
*
|
||||
* A poller provides a single queue of poll events. Events linked to this
|
||||
* poller (using AddPolledConnectionListener()) will be stored in the queue and
|
||||
* must be collected by calling PollConnectionListener().
|
||||
* The returned handle must be destroyed with DestroyConnectionListenerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_ConnectionListenerPoller CreateConnectionListenerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroy a connection listener poller. This will abort any blocked polling
|
||||
* call and prevent additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void DestroyConnectionListenerPoller(NT_ConnectionListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Create a polled connection listener.
|
||||
* The caller is responsible for calling PollConnectionListener() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param immediate_notify notify listener of all existing connections
|
||||
*/
|
||||
NT_ConnectionListener AddPolledConnectionListener(
|
||||
NT_ConnectionListenerPoller poller, bool immediate_notify);
|
||||
|
||||
/**
|
||||
* Get the next connection event. This blocks until the next connect or
|
||||
* disconnect occurs. This is intended to be used with
|
||||
* AddPolledConnectionListener(); connection listeners created using
|
||||
* AddConnectionListener() will not be serviced through this function.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @return Information on the connection events. Only returns empty if an
|
||||
* error occurred (e.g. the instance was invalid or is shutting down).
|
||||
*/
|
||||
std::vector<ConnectionNotification> ReadConnectionListenerQueue(
|
||||
NT_ConnectionListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Remove a connection listener.
|
||||
*
|
||||
* @param conn_listener Listener handle to remove
|
||||
*/
|
||||
void RemoveConnectionListener(NT_ConnectionListener conn_listener);
|
||||
NT_Listener AddPolledListener(NT_ListenerPoller poller, NT_Handle handle,
|
||||
unsigned int mask);
|
||||
|
||||
/** @} */
|
||||
|
||||
@@ -1313,31 +1190,13 @@ void StopConnectionDataLog(NT_ConnectionDataLogger logger);
|
||||
* messages outside this range will be silently ignored.
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @param func log callback function
|
||||
* @param min_level minimum log level
|
||||
* @param max_level maximum log level
|
||||
* @return Logger handle
|
||||
* @param func listener callback function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Logger AddLogger(NT_Inst inst,
|
||||
std::function<void(const LogMessage& msg)> func,
|
||||
unsigned int min_level, unsigned int max_level);
|
||||
|
||||
/**
|
||||
* Create a log poller. A poller provides a single queue of poll events.
|
||||
* The returned handle must be destroyed with DestroyLoggerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_LoggerPoller CreateLoggerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroy a log poller. This will abort any blocked polling call and prevent
|
||||
* additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void DestroyLoggerPoller(NT_LoggerPoller poller);
|
||||
NT_Listener AddLogger(NT_Inst inst, unsigned int min_level,
|
||||
unsigned int max_level, ListenerCallback func);
|
||||
|
||||
/**
|
||||
* Set the log level for a log poller. Events will only be generated for
|
||||
@@ -1349,24 +1208,8 @@ void DestroyLoggerPoller(NT_LoggerPoller poller);
|
||||
* @param max_level maximum log level
|
||||
* @return Logger handle
|
||||
*/
|
||||
NT_Logger AddPolledLogger(NT_LoggerPoller poller, unsigned int min_level,
|
||||
unsigned int max_level);
|
||||
|
||||
/**
|
||||
* Get the next log event. This blocks until the next log occurs.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @return Information on the log events. Only returns empty if an error
|
||||
* occurred (e.g. the instance was invalid or is shutting down).
|
||||
*/
|
||||
std::vector<LogMessage> ReadLoggerQueue(NT_LoggerPoller poller);
|
||||
|
||||
/**
|
||||
* Remove a logger.
|
||||
*
|
||||
* @param logger Logger handle to remove
|
||||
*/
|
||||
void RemoveLogger(NT_Logger logger);
|
||||
NT_Listener AddPolledLogger(NT_ListenerPoller poller, unsigned int min_level,
|
||||
unsigned int max_level);
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
||||
@@ -8,7 +8,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import edu.wpi.first.util.WPIUtilJNI;
|
||||
@@ -61,9 +60,11 @@ class ConnectionListenerTest {
|
||||
@Test
|
||||
void testJNI() {
|
||||
// set up the poller
|
||||
int poller = NetworkTablesJNI.createConnectionListenerPoller(m_serverInst.getHandle());
|
||||
int poller = NetworkTablesJNI.createListenerPoller(m_serverInst.getHandle());
|
||||
assertNotSame(poller, 0, "bad poller handle");
|
||||
int handle = NetworkTablesJNI.addPolledConnectionListener(poller, false);
|
||||
int handle =
|
||||
NetworkTablesJNI.addListener(
|
||||
poller, m_serverInst.getHandle(), NetworkTableEvent.kConnection);
|
||||
assertNotSame(handle, 0, "bad listener handle");
|
||||
|
||||
// trigger a connect event
|
||||
@@ -75,13 +76,13 @@ class ConnectionListenerTest {
|
||||
} catch (InterruptedException ex) {
|
||||
fail("interrupted while waiting for queue");
|
||||
}
|
||||
ConnectionNotification[] events =
|
||||
NetworkTablesJNI.readConnectionListenerQueue(m_serverInst, poller);
|
||||
NetworkTableEvent[] events = NetworkTablesJNI.readListenerQueue(m_serverInst, poller);
|
||||
|
||||
assertNotNull(events);
|
||||
assertEquals(1, events.length);
|
||||
assertEquals(handle, events[0].listener);
|
||||
assertTrue(events[0].connected);
|
||||
assertNotNull(events[0].connInfo);
|
||||
assertEquals(events[0].flags, NetworkTableEvent.kConnected);
|
||||
|
||||
// trigger a disconnect event
|
||||
m_clientInst.stopClient();
|
||||
@@ -97,12 +98,12 @@ class ConnectionListenerTest {
|
||||
} catch (InterruptedException ex) {
|
||||
fail("interrupted while waiting for queue");
|
||||
}
|
||||
events = NetworkTablesJNI.readConnectionListenerQueue(m_serverInst, poller);
|
||||
events = NetworkTablesJNI.readListenerQueue(m_serverInst, poller);
|
||||
|
||||
assertNotNull(events);
|
||||
assertEquals(1, events.length);
|
||||
assertEquals(handle, events[0].listener);
|
||||
assertFalse(events[0].connected);
|
||||
assertEquals(events[0].flags, NetworkTableEvent.kDisconnected);
|
||||
}
|
||||
|
||||
private static int threadedPort = 10001;
|
||||
@@ -111,7 +112,7 @@ class ConnectionListenerTest {
|
||||
@ValueSource(strings = {"127.0.0.1", "127.0.0.1 ", " 127.0.0.1 "})
|
||||
void testThreaded(String address) {
|
||||
m_serverInst.startServer("connectionlistenertest.json", address, 0, threadedPort);
|
||||
List<ConnectionNotification> events = new ArrayList<>();
|
||||
List<NetworkTableEvent> events = new ArrayList<>();
|
||||
final int handle =
|
||||
m_serverInst.addConnectionListener(
|
||||
false,
|
||||
@@ -147,13 +148,14 @@ class ConnectionListenerTest {
|
||||
}
|
||||
|
||||
// wait for thread
|
||||
m_serverInst.waitForConnectionListenerQueue(1.0);
|
||||
m_serverInst.waitForListenerQueue(1.0);
|
||||
|
||||
// get the event
|
||||
synchronized (events) {
|
||||
assertEquals(1, events.size());
|
||||
assertEquals(handle, events.get(0).listener);
|
||||
assertTrue(events.get(0).connected);
|
||||
assertNotNull(events.get(0).connInfo);
|
||||
assertEquals(events.get(0).flags, NetworkTableEvent.kConnected);
|
||||
events.clear();
|
||||
}
|
||||
|
||||
@@ -166,7 +168,7 @@ class ConnectionListenerTest {
|
||||
}
|
||||
|
||||
// wait for thread
|
||||
m_serverInst.waitForConnectionListenerQueue(1.0);
|
||||
m_serverInst.waitForListenerQueue(1.0);
|
||||
|
||||
// get the event
|
||||
try {
|
||||
@@ -177,7 +179,8 @@ class ConnectionListenerTest {
|
||||
synchronized (events) {
|
||||
assertEquals(1, events.size());
|
||||
assertEquals(handle, events.get(0).listener);
|
||||
assertFalse(events.get(0).connected);
|
||||
assertNotNull(events.get(0).connInfo);
|
||||
assertEquals(events.get(0).flags, NetworkTableEvent.kDisconnected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ class LoggerTest {
|
||||
|
||||
@Test
|
||||
void addMessageTest() {
|
||||
List<LogMessage> msgs = new ArrayList<>();
|
||||
m_clientInst.addLogger(msgs::add, LogMessage.kInfo, 100);
|
||||
List<NetworkTableEvent> msgs = new ArrayList<>();
|
||||
m_clientInst.addLogger(LogMessage.kInfo, 100, msgs::add);
|
||||
|
||||
m_clientInst.startClient4("client");
|
||||
m_clientInst.setServer("127.0.0.1", 10000);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import edu.wpi.first.util.WPIUtilJNI;
|
||||
@@ -35,8 +36,8 @@ class TopicListenerTest {
|
||||
m_clientInst.setServer("127.0.0.1", 10010);
|
||||
|
||||
// Use connection listener to ensure we've connected
|
||||
int poller = NetworkTablesJNI.createConnectionListenerPoller(m_clientInst.getHandle());
|
||||
NetworkTablesJNI.addPolledConnectionListener(poller, false);
|
||||
int poller = NetworkTablesJNI.createListenerPoller(m_clientInst.getHandle());
|
||||
NetworkTablesJNI.addListener(poller, m_clientInst.getHandle(), NetworkTableEvent.kConnected);
|
||||
try {
|
||||
if (WPIUtilJNI.waitForObjectTimeout(poller, 1.0)) {
|
||||
fail("client didn't connect to server");
|
||||
@@ -52,10 +53,9 @@ class TopicListenerTest {
|
||||
@Test
|
||||
void testPrefixNewRemote() {
|
||||
connect();
|
||||
final int poller = NetworkTablesJNI.createTopicListenerPoller(m_serverInst.getHandle());
|
||||
final int poller = NetworkTablesJNI.createListenerPoller(m_serverInst.getHandle());
|
||||
final int handle =
|
||||
NetworkTablesJNI.addPolledTopicListener(
|
||||
poller, new String[] {"/foo"}, TopicListenerFlags.kPublish);
|
||||
NetworkTablesJNI.addListener(poller, new String[] {"/foo"}, NetworkTableEvent.kPublish);
|
||||
|
||||
// Trigger an event
|
||||
m_clientInst.getEntry("/foo/bar").setDouble(1.0);
|
||||
@@ -75,13 +75,14 @@ class TopicListenerTest {
|
||||
Thread.currentThread().interrupt();
|
||||
fail("interrupted while waiting for signal");
|
||||
}
|
||||
TopicNotification[] events = NetworkTablesJNI.readTopicListenerQueue(m_serverInst, poller);
|
||||
NetworkTableEvent[] events = NetworkTablesJNI.readListenerQueue(m_serverInst, poller);
|
||||
|
||||
// Check the event
|
||||
assertEquals(1, events.length);
|
||||
assertEquals(handle, events[0].listener);
|
||||
assertEquals(m_serverInst.getTopic("/foo/bar"), events[0].info.getTopic());
|
||||
assertEquals("/foo/bar", events[0].info.name);
|
||||
assertEquals(TopicListenerFlags.kPublish, events[0].flags);
|
||||
assertNotNull(events[0].topicInfo);
|
||||
assertEquals(m_serverInst.getTopic("/foo/bar"), events[0].topicInfo.getTopic());
|
||||
assertEquals("/foo/bar", events[0].topicInfo.name);
|
||||
assertEquals(NetworkTableEvent.kPublish, events[0].flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,10 +49,10 @@ void ConnectionListenerTest::Connect(const char* address, unsigned int port3,
|
||||
|
||||
TEST_F(ConnectionListenerTest, Polled) {
|
||||
// set up the poller
|
||||
NT_ConnectionListenerPoller poller =
|
||||
nt::CreateConnectionListenerPoller(server_inst);
|
||||
NT_ListenerPoller poller = nt::CreateListenerPoller(server_inst);
|
||||
ASSERT_NE(poller, 0u);
|
||||
NT_ConnectionListener handle = nt::AddPolledConnectionListener(poller, false);
|
||||
NT_Listener handle =
|
||||
nt::AddPolledListener(poller, server_inst, nt::EventFlags::kConnection);
|
||||
ASSERT_NE(handle, 0u);
|
||||
|
||||
// trigger a connect event
|
||||
@@ -62,10 +62,11 @@ TEST_F(ConnectionListenerTest, Polled) {
|
||||
bool timed_out = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timed_out));
|
||||
ASSERT_FALSE(timed_out);
|
||||
auto result = nt::ReadConnectionListenerQueue(poller);
|
||||
auto result = nt::ReadListenerQueue(poller);
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_TRUE(result[0].connected);
|
||||
EXPECT_TRUE(result[0].GetConnectionInfo());
|
||||
EXPECT_EQ(result[0].flags, nt::EventFlags::kConnected);
|
||||
|
||||
// trigger a disconnect event
|
||||
nt::StopClient(client_inst);
|
||||
@@ -75,10 +76,11 @@ TEST_F(ConnectionListenerTest, Polled) {
|
||||
timed_out = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timed_out));
|
||||
ASSERT_FALSE(timed_out);
|
||||
result = nt::ReadConnectionListenerQueue(poller);
|
||||
result = nt::ReadListenerQueue(poller);
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_FALSE(result[0].connected);
|
||||
EXPECT_TRUE(result[0].GetConnectionInfo());
|
||||
EXPECT_EQ(result[0].flags, nt::EventFlags::kDisconnected);
|
||||
}
|
||||
|
||||
class ConnectionListenerVariantTest
|
||||
@@ -87,12 +89,12 @@ class ConnectionListenerVariantTest
|
||||
|
||||
TEST_P(ConnectionListenerVariantTest, Threaded) {
|
||||
wpi::mutex m;
|
||||
std::vector<nt::ConnectionNotification> result;
|
||||
auto handle = nt::AddConnectionListener(
|
||||
server_inst, false, [&](const nt::ConnectionNotification& event) {
|
||||
std::scoped_lock lock{m};
|
||||
result.push_back(event);
|
||||
});
|
||||
std::vector<nt::Event> result;
|
||||
auto handle = nt::AddListener(server_inst, nt::EventFlags::kConnection,
|
||||
[&](auto& event) {
|
||||
std::scoped_lock lock{m};
|
||||
result.push_back(event);
|
||||
});
|
||||
|
||||
// trigger a connect event
|
||||
Connect(GetParam().first, 0, 20001 + GetParam().second);
|
||||
@@ -106,7 +108,8 @@ TEST_P(ConnectionListenerVariantTest, Threaded) {
|
||||
std::scoped_lock lock{m};
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_TRUE(result[0].connected);
|
||||
EXPECT_TRUE(result[0].GetConnectionInfo());
|
||||
EXPECT_EQ(result[0].flags, nt::EventFlags::kConnected);
|
||||
result.clear();
|
||||
}
|
||||
|
||||
@@ -115,14 +118,15 @@ TEST_P(ConnectionListenerVariantTest, Threaded) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
// wait for thread
|
||||
nt::WaitForConnectionListenerQueue(server_inst, 1.0);
|
||||
nt::WaitForListenerQueue(server_inst, 1.0);
|
||||
|
||||
// get the event
|
||||
{
|
||||
std::scoped_lock lock{m};
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_FALSE(result[0].connected);
|
||||
EXPECT_TRUE(result[0].GetConnectionInfo());
|
||||
EXPECT_EQ(result[0].flags, nt::EventFlags::kDisconnected);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "LocalStorage.h"
|
||||
#include "MockListenerStorage.h"
|
||||
#include "MockLogger.h"
|
||||
#include "PubSubOptionsMatcher.h"
|
||||
#include "SpanMatcher.h"
|
||||
@@ -36,7 +37,8 @@ class LocalStorageTest : public ::testing::Test {
|
||||
::testing::StrictMock<net::MockNetworkStartupInterface> startup;
|
||||
::testing::StrictMock<net::MockNetworkInterface> network;
|
||||
wpi::MockLogger logger;
|
||||
LocalStorage storage{0, logger};
|
||||
MockListenerStorage listenerStorage;
|
||||
LocalStorage storage{0, listenerStorage, logger};
|
||||
NT_Topic fooTopic{storage.GetTopic("foo")};
|
||||
NT_Topic barTopic{storage.GetTopic("bar")};
|
||||
NT_Topic bazTopic{storage.GetTopic("baz")};
|
||||
|
||||
96
ntcore/src/test/native/cpp/LoggerTest.cpp
Normal file
96
ntcore/src/test/native/cpp/LoggerTest.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <wpi/Synchronization.h>
|
||||
|
||||
#include "Handle.h"
|
||||
#include "TestPrinters.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
class LoggerTest : public ::testing::Test {
|
||||
public:
|
||||
LoggerTest() : m_inst(nt::CreateInstance()) {}
|
||||
|
||||
~LoggerTest() override { nt::DestroyInstance(m_inst); }
|
||||
|
||||
void Generate();
|
||||
void Check(const std::vector<nt::Event>& events, NT_Listener handle,
|
||||
bool infoMsg, bool errMsg);
|
||||
|
||||
protected:
|
||||
NT_Inst m_inst;
|
||||
};
|
||||
|
||||
void LoggerTest::Generate() {
|
||||
// generate info message
|
||||
nt::StartClient4(m_inst, "");
|
||||
|
||||
// generate error message
|
||||
nt::Publish(nt::Handle(nt::Handle{m_inst}.GetInst(), 5, nt::Handle::kTopic),
|
||||
NT_DOUBLE, "");
|
||||
}
|
||||
|
||||
void LoggerTest::Check(const std::vector<nt::Event>& events, NT_Listener handle,
|
||||
bool infoMsg, bool errMsg) {
|
||||
size_t count = (infoMsg ? 1u : 0u) + (errMsg ? 1u : 0u);
|
||||
ASSERT_EQ(events.size(), count);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
ASSERT_EQ(events[i].listener, handle);
|
||||
ASSERT_EQ(events[i].flags & nt::EventFlags::kLogMessage,
|
||||
nt::EventFlags::kLogMessage);
|
||||
auto log = events[i].GetLogMessage();
|
||||
ASSERT_TRUE(log);
|
||||
if (infoMsg) {
|
||||
ASSERT_EQ(log->message, "starting network client");
|
||||
ASSERT_EQ(log->level, NT_LOG_INFO);
|
||||
infoMsg = false;
|
||||
} else if (errMsg) {
|
||||
ASSERT_EQ(log->message,
|
||||
"trying to publish invalid topic handle (386924549)");
|
||||
ASSERT_EQ(log->level, NT_LOG_ERROR);
|
||||
errMsg = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(LoggerTest, DefaultLogRange) {
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto handle =
|
||||
nt::AddPolledListener(poller, m_inst, nt::EventFlags::kLogMessage);
|
||||
|
||||
Generate();
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
|
||||
Check(events, handle, true, true);
|
||||
}
|
||||
|
||||
TEST_F(LoggerTest, InfoOnly) {
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto handle = nt::AddPolledLogger(poller, NT_LOG_INFO, NT_LOG_INFO);
|
||||
|
||||
Generate();
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
|
||||
Check(events, handle, true, false);
|
||||
}
|
||||
|
||||
TEST_F(LoggerTest, Error) {
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto handle = nt::AddPolledLogger(poller, NT_LOG_ERROR, 100);
|
||||
|
||||
Generate();
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
|
||||
Check(events, handle, false, true);
|
||||
}
|
||||
41
ntcore/src/test/native/cpp/MockListenerStorage.h
Normal file
41
ntcore/src/test/native/cpp/MockListenerStorage.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
#include "IListenerStorage.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class MockListenerStorage : public IListenerStorage {
|
||||
public:
|
||||
MOCK_METHOD(void, Activate,
|
||||
(NT_Listener listenerHandle, unsigned int mask,
|
||||
FinishEventFunc finishEvent),
|
||||
(override));
|
||||
MOCK_METHOD(void, Notify,
|
||||
(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
std::span<ConnectionInfo const* const> infos),
|
||||
(override));
|
||||
MOCK_METHOD(void, Notify,
|
||||
(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
std::span<const TopicInfo> infos),
|
||||
(override));
|
||||
MOCK_METHOD(void, Notify,
|
||||
(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
NT_Topic topic, NT_Handle subentry, const Value& value),
|
||||
(override));
|
||||
MOCK_METHOD(void, Notify,
|
||||
(unsigned int flags, unsigned int level,
|
||||
std::string_view filename, unsigned int line,
|
||||
std::string_view message),
|
||||
(override));
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
@@ -20,44 +20,32 @@ void PrintTo(const json& val, ::std::ostream* os) {
|
||||
} // namespace wpi
|
||||
|
||||
namespace nt {
|
||||
#if 0
|
||||
void PrintTo(const EntryNotification& event, std::ostream* os) {
|
||||
*os << "EntryNotification{listener=";
|
||||
|
||||
void PrintTo(const Event& event, std::ostream* os) {
|
||||
*os << "Event{listener=";
|
||||
PrintTo(Handle{event.listener}, os);
|
||||
*os << ", entry=";
|
||||
PrintTo(Handle{event.entry}, os);
|
||||
*os << ", name=\"" << event.name << "\", flags=" << event.flags << ", value=";
|
||||
PrintTo(event.value, os);
|
||||
*os << ", flags=" << event.flags;
|
||||
// *os << ", name=\"" << event.name << "\", flags=" << event.flags
|
||||
// << "value=";
|
||||
// PrintTo(event.value, os);
|
||||
*os << '}';
|
||||
}
|
||||
#endif
|
||||
|
||||
void PrintTo(const Handle& handle, std::ostream* os) {
|
||||
*os << "Handle{";
|
||||
switch (handle.GetType()) {
|
||||
case Handle::kConnectionListener:
|
||||
*os << "kConnectionListener";
|
||||
case Handle::kListener:
|
||||
*os << "kListener";
|
||||
break;
|
||||
case Handle::kConnectionListenerPoller:
|
||||
*os << "kConnectionListenerPoller";
|
||||
case Handle::kListenerPoller:
|
||||
*os << "kListenerPoller";
|
||||
break;
|
||||
case Handle::kEntry:
|
||||
*os << "kEntry";
|
||||
break;
|
||||
case Handle::kEntryListener:
|
||||
*os << "kEntryListener";
|
||||
break;
|
||||
case Handle::kEntryListenerPoller:
|
||||
*os << "kEntryListenerPoller";
|
||||
break;
|
||||
case Handle::kInstance:
|
||||
*os << "kInstance";
|
||||
break;
|
||||
case Handle::kLogger:
|
||||
*os << "kLogger";
|
||||
break;
|
||||
case Handle::kLoggerPoller:
|
||||
*os << "kLoggerPoller";
|
||||
break;
|
||||
case Handle::kTopic:
|
||||
*os << "kTopic";
|
||||
break;
|
||||
|
||||
@@ -49,12 +49,12 @@ struct ClientMessage;
|
||||
struct ServerMessage;
|
||||
} // namespace net
|
||||
|
||||
// class EntryNotification;
|
||||
class Event;
|
||||
class Handle;
|
||||
class PubSubOptions;
|
||||
class Value;
|
||||
|
||||
// void PrintTo(const EntryNotification& event, std::ostream* os);
|
||||
void PrintTo(const Event& event, std::ostream* os);
|
||||
void PrintTo(const Handle& handle, std::ostream* os);
|
||||
void PrintTo(const net3::Message3& msg, std::ostream* os);
|
||||
void PrintTo(const net::ClientMessage& msg, std::ostream* os);
|
||||
|
||||
@@ -19,16 +19,16 @@ class TopicListenerTest : public ::testing::Test {
|
||||
TopicListenerTest()
|
||||
: m_serverInst(nt::CreateInstance()), m_clientInst(nt::CreateInstance()) {
|
||||
#if 0
|
||||
nt::AddLogger(server_inst,
|
||||
[](const nt::LogMessage& msg) {
|
||||
std::fprintf(stderr, "SERVER: %s\n", msg.message.c_str());
|
||||
},
|
||||
0, UINT_MAX);
|
||||
nt::AddLogger(client_inst,
|
||||
[](const nt::LogMessage& msg) {
|
||||
std::fprintf(stderr, "CLIENT: %s\n", msg.message.c_str());
|
||||
},
|
||||
0, UINT_MAX);
|
||||
nt::AddLogger(m_serverInst, 0, UINT_MAX, [](auto& event) {
|
||||
if (auto msg = event.GetLogMessage()) {
|
||||
std::fprintf(stderr, "SERVER: %s\n", msg->message.c_str());
|
||||
}
|
||||
});
|
||||
nt::AddLogger(m_clientInst, 0, UINT_MAX, [](auto& event) {
|
||||
if (auto msg = event.GetLogMessage()) {
|
||||
std::fprintf(stderr, "CLIENT: %s\n", msg.message.c_str());
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -39,9 +39,8 @@ class TopicListenerTest : public ::testing::Test {
|
||||
|
||||
void Connect(unsigned int port);
|
||||
static void PublishTopics(NT_Inst inst);
|
||||
void CheckEvents(const std::vector<nt::TopicNotification>& events,
|
||||
NT_TopicListener handle, unsigned int flags,
|
||||
std::string_view topicName = "/foo/bar");
|
||||
void CheckEvents(const std::vector<nt::Event>& events, NT_Listener handle,
|
||||
unsigned int flags, std::string_view topicName = "/foo/bar");
|
||||
|
||||
protected:
|
||||
NT_Inst m_serverInst;
|
||||
@@ -54,9 +53,8 @@ void TopicListenerTest::Connect(unsigned int port) {
|
||||
nt::SetServer(m_clientInst, "127.0.0.1", port);
|
||||
|
||||
// Use connection listener to ensure we've connected
|
||||
NT_ConnectionListenerPoller poller =
|
||||
nt::CreateConnectionListenerPoller(m_clientInst);
|
||||
nt::AddPolledConnectionListener(poller, false);
|
||||
NT_ListenerPoller poller = nt::CreateListenerPoller(m_clientInst);
|
||||
nt::AddPolledListener(poller, m_clientInst, nt::EventFlags::kConnected);
|
||||
bool timedOut = false;
|
||||
if (!wpi::WaitForObject(poller, 1.0, &timedOut)) {
|
||||
FAIL() << "client didn't connect to server";
|
||||
@@ -69,27 +67,29 @@ void TopicListenerTest::PublishTopics(NT_Inst inst) {
|
||||
nt::Publish(nt::GetTopic(inst, "/baz"), NT_DOUBLE, "double");
|
||||
}
|
||||
|
||||
void TopicListenerTest::CheckEvents(
|
||||
const std::vector<nt::TopicNotification>& events, NT_TopicListener handle,
|
||||
unsigned int flags, std::string_view topicName) {
|
||||
void TopicListenerTest::CheckEvents(const std::vector<nt::Event>& events,
|
||||
NT_Listener handle, unsigned int flags,
|
||||
std::string_view topicName) {
|
||||
ASSERT_EQ(events.size(), 1u);
|
||||
ASSERT_EQ(events[0].listener, handle);
|
||||
ASSERT_EQ(events[0].info.topic, nt::GetTopic(m_serverInst, topicName));
|
||||
ASSERT_EQ(events[0].info.name, topicName);
|
||||
ASSERT_EQ(events[0].flags, flags);
|
||||
auto topicInfo = events[0].GetTopicInfo();
|
||||
ASSERT_TRUE(topicInfo);
|
||||
ASSERT_EQ(topicInfo->topic, nt::GetTopic(m_serverInst, topicName));
|
||||
ASSERT_EQ(topicInfo->name, topicName);
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, TopicNewLocal) {
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), NT_TOPIC_NOTIFY_PUBLISH);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), nt::EventFlags::kPublish);
|
||||
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PUBLISH, "/foo");
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle, nt::EventFlags::kPublish, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, DISABLED_TopicNewRemote) {
|
||||
@@ -97,9 +97,9 @@ TEST_F(TopicListenerTest, DISABLED_TopicNewRemote) {
|
||||
if (HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), NT_TOPIC_NOTIFY_PUBLISH);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), nt::EventFlags::kPublish);
|
||||
|
||||
PublishTopics(m_clientInst);
|
||||
|
||||
@@ -108,54 +108,54 @@ TEST_F(TopicListenerTest, DISABLED_TopicNewRemote) {
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PUBLISH, "/foo");
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle, nt::EventFlags::kPublish, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, TopicPublishImm) {
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"),
|
||||
NT_TOPIC_NOTIFY_PUBLISH | NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
nt::EventFlags::kPublish | nt::EventFlags::kImmediate);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle,
|
||||
NT_TOPIC_NOTIFY_PUBLISH | NT_TOPIC_NOTIFY_IMMEDIATE, "/foo");
|
||||
nt::EventFlags::kPublish | nt::EventFlags::kImmediate, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, TopicUnpublishPropsImm) {
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
nt::AddPolledTopicListener(poller, nt::GetTopic(m_serverInst, "/foo"),
|
||||
NT_TOPIC_NOTIFY_UNPUBLISH |
|
||||
NT_TOPIC_NOTIFY_PROPERTIES |
|
||||
NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
nt::AddPolledListener(poller, nt::GetTopic(m_serverInst, "/foo"),
|
||||
nt::EventFlags::kUnpublish |
|
||||
nt::EventFlags::kProperties |
|
||||
nt::EventFlags::kImmediate);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_FALSE(wpi::WaitForObject(poller, 0.02, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
ASSERT_TRUE(events.empty());
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, TopicUnpublishLocal) {
|
||||
auto topic = nt::GetTopic(m_serverInst, "/foo");
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle =
|
||||
nt::AddPolledTopicListener(poller, topic, NT_TOPIC_NOTIFY_UNPUBLISH);
|
||||
nt::AddPolledListener(poller, topic, nt::EventFlags::kUnpublish);
|
||||
|
||||
auto pub = nt::Publish(topic, NT_DOUBLE, "double");
|
||||
nt::Unpublish(pub);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_UNPUBLISH, "/foo");
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle, nt::EventFlags::kUnpublish, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, DISABLED_TopicUnpublishRemote) {
|
||||
@@ -163,9 +163,9 @@ TEST_F(TopicListenerTest, DISABLED_TopicUnpublishRemote) {
|
||||
if (HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), NT_TOPIC_NOTIFY_UNPUBLISH);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), nt::EventFlags::kUnpublish);
|
||||
|
||||
auto pub =
|
||||
nt::Publish(nt::GetTopic(m_clientInst, "/foo"), NT_DOUBLE, "double");
|
||||
@@ -179,23 +179,23 @@ TEST_F(TopicListenerTest, DISABLED_TopicUnpublishRemote) {
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_UNPUBLISH, "/foo");
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle, nt::EventFlags::kUnpublish, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, TopicPropertiesLocal) {
|
||||
auto topic = nt::GetTopic(m_serverInst, "/foo");
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle =
|
||||
nt::AddPolledTopicListener(poller, topic, NT_TOPIC_NOTIFY_PROPERTIES);
|
||||
nt::AddPolledListener(poller, topic, nt::EventFlags::kProperties);
|
||||
|
||||
nt::SetTopicProperty(topic, "foo", 5);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PROPERTIES, "/foo");
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle, nt::EventFlags::kProperties, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, DISABLED_TopicPropertiesRemote) {
|
||||
@@ -206,9 +206,9 @@ TEST_F(TopicListenerTest, DISABLED_TopicPropertiesRemote) {
|
||||
// the topic needs to actually exist
|
||||
nt::Publish(nt::GetTopic(m_serverInst, "/foo"), NT_BOOLEAN, "boolean");
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), NT_TOPIC_NOTIFY_PROPERTIES);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), nt::EventFlags::kProperties);
|
||||
nt::FlushLocal(m_serverInst);
|
||||
|
||||
nt::SetTopicProperty(nt::GetTopic(m_clientInst, "/foo"), "foo", 5);
|
||||
@@ -217,21 +217,21 @@ TEST_F(TopicListenerTest, DISABLED_TopicPropertiesRemote) {
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PROPERTIES, "/foo");
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle, nt::EventFlags::kProperties, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, PrefixPublishLocal) {
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle =
|
||||
nt::AddPolledTopicListener(poller, {{"/foo/"}}, NT_TOPIC_NOTIFY_PUBLISH);
|
||||
nt::AddPolledListener(poller, {{"/foo/"}}, nt::EventFlags::kPublish);
|
||||
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PUBLISH);
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle, nt::EventFlags::kPublish);
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, DISABLED_PrefixPublishRemote) {
|
||||
@@ -239,9 +239,9 @@ TEST_F(TopicListenerTest, DISABLED_PrefixPublishRemote) {
|
||||
if (HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle =
|
||||
nt::AddPolledTopicListener(poller, {{"/foo/"}}, NT_TOPIC_NOTIFY_PUBLISH);
|
||||
nt::AddPolledListener(poller, {{"/foo/"}}, nt::EventFlags::kPublish);
|
||||
|
||||
PublishTopics(m_clientInst);
|
||||
|
||||
@@ -250,35 +250,36 @@ TEST_F(TopicListenerTest, DISABLED_PrefixPublishRemote) {
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PUBLISH);
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle, nt::EventFlags::kPublish);
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, PrefixPublishImm) {
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
poller, {{"/foo/"}}, NT_TOPIC_NOTIFY_PUBLISH | NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledListener(
|
||||
poller, {{"/foo/"}},
|
||||
nt::EventFlags::kPublish | nt::EventFlags::kImmediate);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle,
|
||||
NT_TOPIC_NOTIFY_PUBLISH | NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
nt::EventFlags::kPublish | nt::EventFlags::kImmediate);
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, PrefixUnpublishPropsImm) {
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
nt::AddPolledTopicListener(poller, {{"/foo/"}},
|
||||
NT_TOPIC_NOTIFY_UNPUBLISH |
|
||||
NT_TOPIC_NOTIFY_PROPERTIES |
|
||||
NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
nt::AddPolledListener(poller, {{"/foo/"}},
|
||||
nt::EventFlags::kUnpublish |
|
||||
nt::EventFlags::kProperties |
|
||||
nt::EventFlags::kImmediate);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_FALSE(wpi::WaitForObject(poller, 0.02, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
ASSERT_TRUE(events.empty());
|
||||
}
|
||||
|
||||
@@ -34,12 +34,12 @@ TEST_F(ValueListenerTest, MultiPollSub) {
|
||||
auto pub = nt::Publish(topic, NT_DOUBLE, "double");
|
||||
auto sub = nt::Subscribe(topic, NT_DOUBLE, "double");
|
||||
|
||||
auto poller1 = nt::CreateValueListenerPoller(m_inst);
|
||||
auto poller2 = nt::CreateValueListenerPoller(m_inst);
|
||||
auto poller3 = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h1 = nt::AddPolledValueListener(poller1, sub, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto h2 = nt::AddPolledValueListener(poller2, sub, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto h3 = nt::AddPolledValueListener(poller3, sub, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto poller1 = nt::CreateListenerPoller(m_inst);
|
||||
auto poller2 = nt::CreateListenerPoller(m_inst);
|
||||
auto poller3 = nt::CreateListenerPoller(m_inst);
|
||||
auto h1 = nt::AddPolledListener(poller1, sub, nt::EventFlags::kValueLocal);
|
||||
auto h2 = nt::AddPolledListener(poller2, sub, nt::EventFlags::kValueLocal);
|
||||
auto h3 = nt::AddPolledListener(poller3, sub, nt::EventFlags::kValueLocal);
|
||||
|
||||
nt::SetDouble(pub, 0);
|
||||
|
||||
@@ -50,30 +50,36 @@ TEST_F(ValueListenerTest, MultiPollSub) {
|
||||
ASSERT_FALSE(timedOut);
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller3, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results1 = nt::ReadValueListenerQueue(poller1);
|
||||
auto results2 = nt::ReadValueListenerQueue(poller2);
|
||||
auto results3 = nt::ReadValueListenerQueue(poller3);
|
||||
auto results1 = nt::ReadListenerQueue(poller1);
|
||||
auto results2 = nt::ReadListenerQueue(poller2);
|
||||
auto results3 = nt::ReadListenerQueue(poller3);
|
||||
|
||||
ASSERT_EQ(results1.size(), 1u);
|
||||
EXPECT_EQ(results1[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results1[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results1[0].listener, h1);
|
||||
EXPECT_EQ(results1[0].subentry, sub);
|
||||
EXPECT_EQ(results1[0].topic, topic);
|
||||
EXPECT_EQ(results1[0].value, nt::Value::MakeDouble(0.0));
|
||||
auto valueData = results1[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub);
|
||||
EXPECT_EQ(valueData->topic, topic);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
ASSERT_EQ(results2.size(), 1u);
|
||||
EXPECT_EQ(results2[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results2[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results2[0].listener, h2);
|
||||
EXPECT_EQ(results2[0].subentry, sub);
|
||||
EXPECT_EQ(results2[0].topic, topic);
|
||||
EXPECT_EQ(results2[0].value, nt::Value::MakeDouble(0.0));
|
||||
valueData = results2[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub);
|
||||
EXPECT_EQ(valueData->topic, topic);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
ASSERT_EQ(results3.size(), 1u);
|
||||
EXPECT_EQ(results3[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results3[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results3[0].listener, h3);
|
||||
EXPECT_EQ(results3[0].subentry, sub);
|
||||
EXPECT_EQ(results3[0].topic, topic);
|
||||
EXPECT_EQ(results3[0].value, nt::Value::MakeDouble(0.0));
|
||||
valueData = results3[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub);
|
||||
EXPECT_EQ(valueData->topic, topic);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollMultiSub) {
|
||||
@@ -82,29 +88,33 @@ TEST_F(ValueListenerTest, PollMultiSub) {
|
||||
auto sub1 = nt::Subscribe(topic, NT_DOUBLE, "double");
|
||||
auto sub2 = nt::Subscribe(topic, NT_DOUBLE, "double");
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h1 = nt::AddPolledValueListener(poller, sub1, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto h2 = nt::AddPolledValueListener(poller, sub2, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto h1 = nt::AddPolledListener(poller, sub1, nt::EventFlags::kValueLocal);
|
||||
auto h2 = nt::AddPolledListener(poller, sub2, nt::EventFlags::kValueLocal);
|
||||
|
||||
nt::SetDouble(pub, 0);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
auto results = nt::ReadListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 2u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[0].listener, h1);
|
||||
EXPECT_EQ(results[0].subentry, sub1);
|
||||
EXPECT_EQ(results[0].topic, topic);
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
auto valueData = results[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub1);
|
||||
EXPECT_EQ(valueData->topic, topic);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
EXPECT_EQ(results[1].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[1].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[1].listener, h2);
|
||||
EXPECT_EQ(results[1].subentry, sub2);
|
||||
EXPECT_EQ(results[1].topic, topic);
|
||||
EXPECT_EQ(results[1].value, nt::Value::MakeDouble(0.0));
|
||||
valueData = results[1].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub2);
|
||||
EXPECT_EQ(valueData->topic, topic);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollMultiSubTopic) {
|
||||
@@ -115,9 +125,9 @@ TEST_F(ValueListenerTest, PollMultiSubTopic) {
|
||||
auto sub1 = nt::Subscribe(topic1, NT_DOUBLE, "double");
|
||||
auto sub2 = nt::Subscribe(topic2, NT_DOUBLE, "double");
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h1 = nt::AddPolledValueListener(poller, sub1, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto h2 = nt::AddPolledValueListener(poller, sub2, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto h1 = nt::AddPolledListener(poller, sub1, nt::EventFlags::kValueLocal);
|
||||
auto h2 = nt::AddPolledListener(poller, sub2, nt::EventFlags::kValueLocal);
|
||||
|
||||
nt::SetDouble(pub1, 0);
|
||||
nt::SetDouble(pub2, 1);
|
||||
@@ -125,20 +135,24 @@ TEST_F(ValueListenerTest, PollMultiSubTopic) {
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
auto results = nt::ReadListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 2u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[0].listener, h1);
|
||||
EXPECT_EQ(results[0].subentry, sub1);
|
||||
EXPECT_EQ(results[0].topic, topic1);
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
auto valueData = results[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub1);
|
||||
EXPECT_EQ(valueData->topic, topic1);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
EXPECT_EQ(results[1].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[1].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[1].listener, h2);
|
||||
EXPECT_EQ(results[1].subentry, sub2);
|
||||
EXPECT_EQ(results[1].topic, topic2);
|
||||
EXPECT_EQ(results[1].value, nt::Value::MakeDouble(1.0));
|
||||
valueData = results[1].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub2);
|
||||
EXPECT_EQ(valueData->topic, topic2);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(1.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollSubMultiple) {
|
||||
@@ -148,8 +162,8 @@ TEST_F(ValueListenerTest, PollSubMultiple) {
|
||||
auto pub2 = nt::Publish(topic2, NT_DOUBLE, "double");
|
||||
auto sub = nt::SubscribeMultiple(m_inst, {{"foo"}});
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledValueListener(poller, sub, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledListener(poller, sub, nt::EventFlags::kValueLocal);
|
||||
|
||||
nt::SetDouble(pub1, 0);
|
||||
nt::SetDouble(pub2, 1);
|
||||
@@ -157,90 +171,139 @@ TEST_F(ValueListenerTest, PollSubMultiple) {
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
auto results = nt::ReadListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 2u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[0].listener, h);
|
||||
EXPECT_EQ(results[0].subentry, sub);
|
||||
EXPECT_EQ(results[0].topic, topic1);
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
auto valueData = results[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub);
|
||||
EXPECT_EQ(valueData->topic, topic1);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
EXPECT_EQ(results[1].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[1].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[1].listener, h);
|
||||
EXPECT_EQ(results[1].subentry, sub);
|
||||
EXPECT_EQ(results[1].topic, topic2);
|
||||
EXPECT_EQ(results[1].value, nt::Value::MakeDouble(1.0));
|
||||
valueData = results[1].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub);
|
||||
EXPECT_EQ(valueData->topic, topic2);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(1.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollSubPrefixCreated) {
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto h =
|
||||
nt::AddPolledListener(poller, {{"foo"}}, nt::EventFlags::kValueLocal);
|
||||
|
||||
auto topic1 = nt::GetTopic(m_inst, "foo/1");
|
||||
auto topic2 = nt::GetTopic(m_inst, "foo/2");
|
||||
auto topic3 = nt::GetTopic(m_inst, "bar/3");
|
||||
auto pub1 = nt::Publish(topic1, NT_DOUBLE, "double");
|
||||
auto pub2 = nt::Publish(topic2, NT_DOUBLE, "double");
|
||||
auto pub3 = nt::Publish(topic3, NT_DOUBLE, "double");
|
||||
|
||||
nt::SetDouble(pub1, 0);
|
||||
nt::SetDouble(pub2, 1);
|
||||
nt::SetDouble(pub3, 1);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 2u);
|
||||
EXPECT_EQ(results[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[0].listener, h);
|
||||
auto valueData = results[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->topic, topic1);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
EXPECT_EQ(results[1].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[1].listener, h);
|
||||
valueData = results[1].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->topic, topic2);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(1.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollEntry) {
|
||||
auto entry = nt::GetEntry(m_inst, "foo");
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledValueListener(poller, entry, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledListener(poller, entry, nt::EventFlags::kValueLocal);
|
||||
|
||||
ASSERT_TRUE(nt::SetDouble(entry, 0));
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
auto results = nt::ReadListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 1u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[0].listener, h);
|
||||
EXPECT_EQ(results[0].subentry, entry);
|
||||
EXPECT_EQ(results[0].topic, nt::GetTopic(m_inst, "foo"));
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
auto valueData = results[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, entry);
|
||||
EXPECT_EQ(valueData->topic, nt::GetTopic(m_inst, "foo"));
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollImmediate) {
|
||||
auto entry = nt::GetEntry(m_inst, "foo");
|
||||
ASSERT_TRUE(nt::SetDouble(entry, 0));
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledValueListener(
|
||||
poller, entry, NT_VALUE_NOTIFY_LOCAL | NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledListener(
|
||||
poller, entry, nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
auto results = nt::ReadListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 1u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
EXPECT_EQ(results[0].flags &
|
||||
(nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate),
|
||||
nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate);
|
||||
EXPECT_EQ(results[0].listener, h);
|
||||
EXPECT_EQ(results[0].subentry, entry);
|
||||
EXPECT_EQ(results[0].topic, nt::GetTopic(m_inst, "foo"));
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
auto valueData = results[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, entry);
|
||||
EXPECT_EQ(valueData->topic, nt::GetTopic(m_inst, "foo"));
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollImmediateNoValue) {
|
||||
auto entry = nt::GetEntry(m_inst, "foo");
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledValueListener(
|
||||
poller, entry, NT_VALUE_NOTIFY_LOCAL | NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledListener(
|
||||
poller, entry, nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_FALSE(wpi::WaitForObject(poller, 0.02, &timedOut));
|
||||
ASSERT_TRUE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
auto results = nt::ReadListenerQueue(poller);
|
||||
ASSERT_TRUE(results.empty());
|
||||
|
||||
// now set a value
|
||||
ASSERT_TRUE(nt::SetDouble(entry, 0));
|
||||
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
results = nt::ReadValueListenerQueue(poller);
|
||||
results = nt::ReadListenerQueue(poller);
|
||||
ASSERT_FALSE(timedOut);
|
||||
|
||||
ASSERT_EQ(results.size(), 1u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[0].listener, h);
|
||||
EXPECT_EQ(results[0].subentry, entry);
|
||||
EXPECT_EQ(results[0].topic, nt::GetTopic(m_inst, "foo"));
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
auto valueData = results[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, entry);
|
||||
EXPECT_EQ(valueData->topic, nt::GetTopic(m_inst, "foo"));
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollImmediateSubMultiple) {
|
||||
@@ -252,27 +315,35 @@ TEST_F(ValueListenerTest, PollImmediateSubMultiple) {
|
||||
nt::SetDouble(pub1, 0);
|
||||
nt::SetDouble(pub2, 1);
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledValueListener(
|
||||
poller, sub, NT_VALUE_NOTIFY_LOCAL | NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledListener(
|
||||
poller, sub, nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
auto results = nt::ReadListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 2u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
EXPECT_EQ(results[0].flags &
|
||||
(nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate),
|
||||
nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate);
|
||||
EXPECT_EQ(results[0].listener, h);
|
||||
EXPECT_EQ(results[0].subentry, sub);
|
||||
EXPECT_EQ(results[0].topic, topic1);
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
auto valueData = results[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub);
|
||||
EXPECT_EQ(valueData->topic, topic1);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
EXPECT_EQ(results[1].flags, NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
EXPECT_EQ(results[1].flags &
|
||||
(nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate),
|
||||
nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate);
|
||||
EXPECT_EQ(results[1].listener, h);
|
||||
EXPECT_EQ(results[1].subentry, sub);
|
||||
EXPECT_EQ(results[1].topic, topic2);
|
||||
EXPECT_EQ(results[1].value, nt::Value::MakeDouble(1.0));
|
||||
valueData = results[1].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub);
|
||||
EXPECT_EQ(valueData->topic, topic2);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(1.0));
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
@@ -8,13 +8,12 @@
|
||||
#include "ntcore.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
nt::AddLogger(
|
||||
nt::GetDefaultInstance(),
|
||||
[](const nt::LogMessage& msg) {
|
||||
std::fputs(msg.message.c_str(), stderr);
|
||||
std::fputc('\n', stderr);
|
||||
},
|
||||
0, UINT_MAX);
|
||||
nt::AddLogger(nt::GetDefaultInstance(), 0, UINT_MAX, [](auto& event) {
|
||||
if (auto msg = event.GetLogMessage()) {
|
||||
std::fputs(msg->message.c_str(), stderr);
|
||||
std::fputc('\n', stderr);
|
||||
}
|
||||
});
|
||||
::testing::InitGoogleMock(&argc, argv);
|
||||
int ret = RUN_ALL_TESTS();
|
||||
return ret;
|
||||
|
||||
@@ -1,37 +1,25 @@
|
||||
NT_AddConnectionListener
|
||||
NT_AddListener
|
||||
NT_AddListenerMultiple
|
||||
NT_AddListenerSingle
|
||||
NT_AddLogger
|
||||
NT_AddPolledConnectionListener
|
||||
NT_AddPolledListener
|
||||
NT_AddPolledListenerMultiple
|
||||
NT_AddPolledListenerSingle
|
||||
NT_AddPolledLogger
|
||||
NT_AddPolledTopicListener
|
||||
NT_AddPolledTopicListenerMultiple
|
||||
NT_AddPolledTopicListenerSingle
|
||||
NT_AddPolledValueListener
|
||||
NT_AddTopicListener
|
||||
NT_AddTopicListenerMultiple
|
||||
NT_AddTopicListenerSingle
|
||||
NT_AddValueListener
|
||||
NT_AllocateBooleanArray
|
||||
NT_AllocateCharArray
|
||||
NT_AllocateDoubleArray
|
||||
NT_AllocateFloatArray
|
||||
NT_AllocateIntegerArray
|
||||
NT_AllocateStringArray
|
||||
NT_CreateConnectionListenerPoller
|
||||
NT_CreateInstance
|
||||
NT_CreateLoggerPoller
|
||||
NT_CreateTopicListenerPoller
|
||||
NT_CreateValueListenerPoller
|
||||
NT_CreateListenerPoller
|
||||
NT_DeleteTopicProperty
|
||||
NT_DestroyConnectionListenerPoller
|
||||
NT_DestroyInstance
|
||||
NT_DestroyLoggerPoller
|
||||
NT_DestroyTopicListenerPoller
|
||||
NT_DestroyValueListenerPoller
|
||||
NT_DestroyListenerPoller
|
||||
NT_DisposeConnectionInfoArray
|
||||
NT_DisposeConnectionNotification
|
||||
NT_DisposeConnectionNotificationArray
|
||||
NT_DisposeLogMessage
|
||||
NT_DisposeLogMessageArray
|
||||
NT_DisposeEvent
|
||||
NT_DisposeEventArray
|
||||
NT_DisposeString
|
||||
NT_DisposeTimestampedBoolean
|
||||
NT_DisposeTimestampedBooleanArray
|
||||
@@ -46,12 +34,8 @@ NT_DisposeTimestampedString
|
||||
NT_DisposeTimestampedStringArray
|
||||
NT_DisposeTopicInfo
|
||||
NT_DisposeTopicInfoArray
|
||||
NT_DisposeTopicNotification
|
||||
NT_DisposeTopicNotificationArray
|
||||
NT_DisposeValue
|
||||
NT_DisposeValueArray
|
||||
NT_DisposeValueNotification
|
||||
NT_DisposeValueNotificationArray
|
||||
NT_Flush
|
||||
NT_FlushLocal
|
||||
NT_FreeBooleanArray
|
||||
@@ -149,8 +133,7 @@ NT_IsConnected
|
||||
NT_Now
|
||||
NT_Publish
|
||||
NT_PublishEx
|
||||
NT_ReadConnectionListenerQueue
|
||||
NT_ReadLoggerQueue
|
||||
NT_ReadListenerQueue
|
||||
NT_ReadQueueBoolean
|
||||
NT_ReadQueueBooleanArray
|
||||
NT_ReadQueueDouble
|
||||
@@ -167,14 +150,9 @@ NT_ReadQueueValuesBoolean
|
||||
NT_ReadQueueValuesDouble
|
||||
NT_ReadQueueValuesFloat
|
||||
NT_ReadQueueValuesInteger
|
||||
NT_ReadTopicListenerQueue
|
||||
NT_ReadValueListenerQueue
|
||||
NT_Release
|
||||
NT_ReleaseEntry
|
||||
NT_RemoveConnectionListener
|
||||
NT_RemoveLogger
|
||||
NT_RemoveTopicListener
|
||||
NT_RemoveValueListener
|
||||
NT_RemoveListener
|
||||
NT_SetBoolean
|
||||
NT_SetBooleanArray
|
||||
NT_SetDefaultBoolean
|
||||
@@ -220,9 +198,7 @@ NT_StopServer
|
||||
NT_Subscribe
|
||||
NT_Unpublish
|
||||
NT_Unsubscribe
|
||||
NT_WaitForConnectionListenerQueue
|
||||
NT_WaitForTopicListenerQueue
|
||||
NT_WaitForValueListenerQueue
|
||||
NT_WaitForListenerQueue
|
||||
WPI_CreateEvent
|
||||
WPI_CreateSemaphore
|
||||
WPI_CreateSignalObject
|
||||
|
||||
@@ -39,50 +39,48 @@ static glass::NetworkTablesFlagsSettings gFlagsSettings;
|
||||
static glass::MainMenuBar gMainMenu;
|
||||
|
||||
static void NtInitialize() {
|
||||
// update window title when connection status changes
|
||||
auto inst = nt::GetDefaultInstance();
|
||||
auto poller = nt::CreateConnectionListenerPoller(inst);
|
||||
nt::AddPolledConnectionListener(poller, true);
|
||||
auto poller = nt::CreateListenerPoller(inst);
|
||||
nt::AddPolledListener(
|
||||
poller, inst,
|
||||
NT_EVENT_CONNECTION | NT_EVENT_IMMEDIATE | NT_EVENT_LOGMESSAGE);
|
||||
gui::AddEarlyExecute([inst, poller] {
|
||||
auto win = gui::GetSystemWindow();
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
for (auto&& event : nt::ReadConnectionListenerQueue(poller)) {
|
||||
if ((nt::GetNetworkMode(inst) & NT_NET_MODE_SERVER) != 0) {
|
||||
// for server mode, just print number of clients connected
|
||||
glfwSetWindowTitle(win,
|
||||
fmt::format("OutlineViewer - {} Clients Connected",
|
||||
nt::GetConnections(inst).size())
|
||||
.c_str());
|
||||
} else if (event.connected) {
|
||||
glfwSetWindowTitle(win, fmt::format("OutlineViewer - Connected ({})",
|
||||
event.conn.remote_ip)
|
||||
.c_str());
|
||||
} else {
|
||||
glfwSetWindowTitle(win, "OutlineViewer - DISCONNECTED");
|
||||
for (auto&& event : nt::ReadListenerQueue(poller)) {
|
||||
if (auto connInfo = event.GetConnectionInfo()) {
|
||||
// update window title when connection status changes
|
||||
if ((nt::GetNetworkMode(inst) & NT_NET_MODE_SERVER) != 0) {
|
||||
// for server mode, just print number of clients connected
|
||||
glfwSetWindowTitle(win,
|
||||
fmt::format("OutlineViewer - {} Clients Connected",
|
||||
nt::GetConnections(inst).size())
|
||||
.c_str());
|
||||
} else if ((event.flags & NT_EVENT_CONNECTED) != 0) {
|
||||
glfwSetWindowTitle(win, fmt::format("OutlineViewer - Connected ({})",
|
||||
connInfo->remote_ip)
|
||||
.c_str());
|
||||
} else {
|
||||
glfwSetWindowTitle(win, "OutlineViewer - DISCONNECTED");
|
||||
}
|
||||
} else if (auto msg = event.GetLogMessage()) {
|
||||
// handle NetworkTables log messages
|
||||
const char* level = "";
|
||||
if (msg->level >= NT_LOG_CRITICAL) {
|
||||
level = "CRITICAL: ";
|
||||
} else if (msg->level >= NT_LOG_ERROR) {
|
||||
level = "ERROR: ";
|
||||
} else if (msg->level >= NT_LOG_WARNING) {
|
||||
level = "WARNING: ";
|
||||
}
|
||||
gLog.Append(fmt::format("{}{} ({}:{})\n", level, msg->message,
|
||||
msg->filename, msg->line));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// handle NetworkTables log messages
|
||||
auto logPoller = nt::CreateLoggerPoller(inst);
|
||||
nt::AddPolledLogger(logPoller, NT_LOG_INFO, 100);
|
||||
gui::AddEarlyExecute([logPoller] {
|
||||
for (auto&& msg : nt::ReadLoggerQueue(logPoller)) {
|
||||
const char* level = "";
|
||||
if (msg.level >= NT_LOG_CRITICAL) {
|
||||
level = "CRITICAL: ";
|
||||
} else if (msg.level >= NT_LOG_ERROR) {
|
||||
level = "ERROR: ";
|
||||
} else if (msg.level >= NT_LOG_WARNING) {
|
||||
level = "WARNING: ";
|
||||
}
|
||||
gLog.Append(fmt::format("{}{} ({}:{})\n", level, msg.message,
|
||||
msg.filename, msg.line));
|
||||
}
|
||||
});
|
||||
|
||||
// NetworkTables table window
|
||||
gModel = std::make_unique<glass::NetworkTablesModel>();
|
||||
gui::AddEarlyExecute([] { gModel->Update(); });
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
#include <networktables/MultiSubscriber.h>
|
||||
#include <networktables/NetworkTable.h>
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#include <networktables/NetworkTableListener.h>
|
||||
#include <networktables/StringTopic.h>
|
||||
#include <networktables/TopicListener.h>
|
||||
|
||||
using namespace frc;
|
||||
|
||||
@@ -28,7 +28,7 @@ struct Instance {
|
||||
nt::StringPublisher typePublisher{table->GetStringTopic(".type").Publish()};
|
||||
nt::MultiSubscriber tableSubscriber{nt::NetworkTableInstance::GetDefault(),
|
||||
{{fmt::format("{}/", table->GetPath())}}};
|
||||
nt::TopicListener listener;
|
||||
nt::NetworkTableListener listener;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@@ -166,13 +166,14 @@ void Preferences::RemoveAll() {
|
||||
|
||||
Instance::Instance() {
|
||||
typePublisher.Set("RobotPreferences");
|
||||
listener = nt::TopicListener{
|
||||
tableSubscriber, NT_TOPIC_NOTIFY_IMMEDIATE | NT_TOPIC_NOTIFY_PUBLISH,
|
||||
[typeTopic = typePublisher.GetTopic().GetHandle()](
|
||||
const nt::TopicNotification& event) {
|
||||
if (event.info.topic != typeTopic) {
|
||||
nt::SetTopicPersistent(event.info.topic, true);
|
||||
listener = nt::NetworkTableListener::CreateListener(
|
||||
tableSubscriber, NT_EVENT_PUBLISH | NT_EVENT_IMMEDIATE,
|
||||
[typeTopic = typePublisher.GetTopic().GetHandle()](auto& event) {
|
||||
if (auto topicInfo = event.GetTopicInfo()) {
|
||||
if (topicInfo->topic != typeTopic) {
|
||||
nt::SetTopicPersistent(topicInfo->topic, true);
|
||||
}
|
||||
}
|
||||
}};
|
||||
});
|
||||
HAL_Report(HALUsageReporting::kResourceType_Preferences, 0);
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@ import edu.wpi.first.hal.HAL;
|
||||
import edu.wpi.first.networktables.MultiSubscriber;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.networktables.NetworkTableEvent;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.networktables.NetworkTableListener;
|
||||
import edu.wpi.first.networktables.StringPublisher;
|
||||
import edu.wpi.first.networktables.Topic;
|
||||
import edu.wpi.first.networktables.TopicListener;
|
||||
import edu.wpi.first.networktables.TopicListenerFlags;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
@@ -39,7 +39,7 @@ public final class Preferences {
|
||||
|
||||
private static StringPublisher m_typePublisher;
|
||||
private static MultiSubscriber m_tableSubscriber;
|
||||
private static TopicListener m_listener;
|
||||
private static NetworkTableListener m_listener;
|
||||
|
||||
/** Creates a preference class. */
|
||||
private Preferences() {}
|
||||
@@ -75,13 +75,15 @@ public final class Preferences {
|
||||
m_listener.close();
|
||||
}
|
||||
m_listener =
|
||||
new TopicListener(
|
||||
NetworkTableListener.createListener(
|
||||
m_tableSubscriber,
|
||||
TopicListenerFlags.kImmediate | TopicListenerFlags.kPublish,
|
||||
NetworkTableEvent.kImmediate | NetworkTableEvent.kPublish,
|
||||
event -> {
|
||||
Topic topic = event.info.getTopic();
|
||||
if (!topic.equals(m_typePublisher.getTopic())) {
|
||||
event.info.getTopic().setPersistent(true);
|
||||
if (event.topicInfo != null) {
|
||||
Topic topic = event.topicInfo.getTopic();
|
||||
if (!topic.equals(m_typePublisher.getTopic())) {
|
||||
event.topicInfo.getTopic().setPersistent(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user