[ntcore] Split LocalStorage implementation into separate files

This commit is contained in:
Peter Johnson
2024-10-18 23:08:52 -07:00
parent 0921054a28
commit 4a43ddbacf
17 changed files with 2360 additions and 1907 deletions

View File

@@ -0,0 +1,24 @@
// 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 "LocalDataLogger.h"
#include <fmt/format.h>
#include <wpi/DataLog.h>
#include <wpi/StringExtras.h>
using namespace nt::local;
int LocalDataLogger::Start(std::string_view name, std::string_view typeStr,
std::string_view metadata, int64_t time) {
// NT and DataLog use different standard representations for int and int[]
if (typeStr == "int") {
typeStr = "int64";
} else if (typeStr == "int[]") {
typeStr = "int64[]";
}
return log.Start(fmt::format("{}{}", logPrefix,
wpi::remove_prefix(name, prefix).value_or(name)),
typeStr, metadata, time);
}

View File

@@ -0,0 +1,39 @@
// 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 <stdint.h>
#include <string>
#include <string_view>
#include "Handle.h"
#include "ntcore_c.h"
namespace wpi::log {
class DataLog;
} // namespace wpi::log
namespace nt::local {
struct LocalTopic;
struct LocalDataLogger {
static constexpr auto kType = Handle::kDataLogger;
LocalDataLogger(NT_DataLogger handle, wpi::log::DataLog& log,
std::string_view prefix, std::string_view logPrefix)
: handle{handle}, log{log}, prefix{prefix}, logPrefix{logPrefix} {}
int Start(std::string_view name, std::string_view typeStr,
std::string_view metadata, int64_t time);
NT_DataLogger handle;
wpi::log::DataLog& log;
std::string prefix;
std::string logPrefix;
};
} // namespace nt::local

View File

@@ -0,0 +1,64 @@
// 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 "LocalDataLoggerEntry.h"
#include <string>
#include <string_view>
#include <fmt/format.h>
#include <wpi/StringExtras.h>
#include "networktables/NetworkTableValue.h"
using namespace nt::local;
std::string LocalDataLoggerEntry::MakeMetadata(std::string_view properties) {
return fmt::format("{{\"properties\":{},\"source\":\"NT\"}}", properties);
}
void LocalDataLoggerEntry::Append(const Value& v) {
auto time = v.time();
switch (v.type()) {
case NT_BOOLEAN:
log->AppendBoolean(entry, v.GetBoolean(), time);
break;
case NT_INTEGER:
log->AppendInteger(entry, v.GetInteger(), time);
break;
case NT_FLOAT:
log->AppendFloat(entry, v.GetFloat(), time);
break;
case NT_DOUBLE:
log->AppendDouble(entry, v.GetDouble(), time);
break;
case NT_STRING:
log->AppendString(entry, v.GetString(), time);
break;
case NT_RAW: {
auto val = v.GetRaw();
log->AppendRaw(entry,
{reinterpret_cast<const uint8_t*>(val.data()), val.size()},
time);
break;
}
case NT_BOOLEAN_ARRAY:
log->AppendBooleanArray(entry, v.GetBooleanArray(), time);
break;
case NT_INTEGER_ARRAY:
log->AppendIntegerArray(entry, v.GetIntegerArray(), time);
break;
case NT_FLOAT_ARRAY:
log->AppendFloatArray(entry, v.GetFloatArray(), time);
break;
case NT_DOUBLE_ARRAY:
log->AppendDoubleArray(entry, v.GetDoubleArray(), time);
break;
case NT_STRING_ARRAY:
log->AppendStringArray(entry, v.GetStringArray(), time);
break;
default:
break;
}
}

View File

@@ -0,0 +1,45 @@
// 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 <stdint.h>
#include <string>
#include <string_view>
#include <wpi/DataLog.h>
#include "ntcore_c.h"
namespace wpi::log {
class DataLog;
} // namespace wpi::log
namespace nt {
class Value;
} // namespace nt
namespace nt::local {
struct LocalTopic;
struct LocalDataLoggerEntry {
LocalDataLoggerEntry(wpi::log::DataLog& log, int entry, NT_DataLogger logger)
: log{&log}, entry{entry}, logger{logger} {}
static std::string MakeMetadata(std::string_view properties);
void Append(const Value& v);
void Finish(int64_t timestamp) { log->Finish(entry, timestamp); }
void SetMetadata(std::string_view metadata, int64_t timestamp) {
log->SetMetadata(entry, metadata, timestamp);
}
wpi::log::DataLog* log;
int entry;
NT_DataLogger logger;
};
} // namespace nt::local

View File

@@ -0,0 +1,31 @@
// 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 <wpi/Synchronization.h>
#include "Handle.h"
#include "local/LocalSubscriber.h"
namespace nt::local {
struct LocalPublisher;
struct LocalEntry {
static constexpr auto kType = Handle::kEntry;
LocalEntry(NT_Entry handle, LocalSubscriber* subscriber)
: handle{handle}, topic{subscriber->topic}, subscriber{subscriber} {}
// invariants
wpi::SignalObject<NT_Entry> handle;
LocalTopic* topic;
LocalSubscriber* subscriber;
// the publisher (created on demand)
LocalPublisher* publisher{nullptr};
};
} // namespace nt::local

View File

@@ -0,0 +1,35 @@
// 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 "ntcore_c.h"
namespace nt::local {
struct LocalMultiSubscriber;
struct LocalSubscriber;
struct LocalListener {
LocalListener(NT_Listener handle, LocalSubscriber* subscriber,
unsigned int eventMask, bool subscriberOwned)
: handle{handle},
eventMask{eventMask},
subscriber{subscriber},
subscriberOwned{subscriberOwned} {}
LocalListener(NT_Listener handle, LocalMultiSubscriber* subscriber,
unsigned int eventMask, bool subscriberOwned)
: handle{handle},
eventMask{eventMask},
multiSubscriber{subscriber},
subscriberOwned{subscriberOwned} {}
NT_Listener handle;
unsigned int eventMask;
LocalSubscriber* subscriber{nullptr};
LocalMultiSubscriber* multiSubscriber{nullptr};
bool subscriberOwned;
};
} // namespace nt::local

View File

@@ -0,0 +1,59 @@
// 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>
#include <string_view>
#include <vector>
#include <wpi/StringExtras.h>
#include <wpi/Synchronization.h>
#include "Handle.h"
#include "PubSubOptions.h"
#include "VectorSet.h"
#include "ntcore_c.h"
namespace nt::local {
constexpr bool PrefixMatch(std::string_view name, std::string_view prefix,
bool special) {
return (!special || !prefix.empty()) && wpi::starts_with(name, prefix);
}
struct LocalMultiSubscriber {
static constexpr auto kType = Handle::kMultiSubscriber;
LocalMultiSubscriber(NT_MultiSubscriber handle,
std::span<const std::string_view> prefixes,
const PubSubOptionsImpl& options)
: handle{handle}, options{options} {
this->options.prefixMatch = true;
this->prefixes.reserve(prefixes.size());
for (auto&& prefix : prefixes) {
this->prefixes.emplace_back(prefix);
}
}
bool Matches(std::string_view name, bool special) {
for (auto&& prefix : prefixes) {
if (PrefixMatch(name, prefix, special)) {
return true;
}
}
return false;
}
// invariants
wpi::SignalObject<NT_MultiSubscriber> handle;
std::vector<std::string> prefixes;
PubSubOptionsImpl options;
// value listeners
VectorSet<NT_Listener> valueListeners;
};
} // namespace nt::local

View File

@@ -0,0 +1,36 @@
// 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 <wpi/Synchronization.h>
#include "Handle.h"
#include "local/LocalTopic.h"
#include "local/PubSubConfig.h"
namespace nt::local {
struct LocalPublisher {
static constexpr auto kType = Handle::kPublisher;
LocalPublisher(NT_Publisher handle, LocalTopic* topic, PubSubConfig config)
: handle{handle}, topic{topic}, config{std::move(config)} {}
void UpdateActive() {
active = config.type == topic->type && config.typeStr == topic->typeStr;
}
// invariants
wpi::SignalObject<NT_Publisher> handle;
LocalTopic* topic;
PubSubConfig config;
// whether or not the publisher should actually publish values
bool active{false};
};
} // namespace nt::local

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,320 @@
// 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 <concepts>
#include <memory>
#include <string_view>
#include <wpi/DenseMap.h>
#include <wpi/StringExtras.h>
#include <wpi/StringMap.h>
#include <wpi/Synchronization.h>
#include <wpi/json.h>
#include "HandleMap.h"
#include "local/LocalDataLogger.h"
#include "local/LocalEntry.h"
#include "local/LocalListener.h"
#include "local/LocalMultiSubscriber.h"
#include "local/LocalPublisher.h"
#include "local/LocalSubscriber.h"
#include "local/LocalTopic.h"
#include "ntcore_c.h"
#include "ntcore_cpp.h"
namespace wpi {
class Logger;
} // namespace wpi
namespace nt {
class IListenerStorage;
} // namespace nt
namespace nt::net {
class ClientMessageHandler;
} // namespace nt::net
namespace nt::local {
// inner struct to protect against accidentally deadlocking on the mutex
class StorageImpl {
public:
StorageImpl(int inst, IListenerStorage& listenerStorage, wpi::Logger& logger);
wpi::Logger& GetLogger() { return m_logger; }
//
// Network interface functions
//
void NetworkAnnounce(LocalTopic* topic, std::string_view typeStr,
const wpi::json& properties, std::optional<int> pubuid);
void RemoveNetworkPublisher(LocalTopic* topic);
void NetworkPropertiesUpdate(LocalTopic* topic, const wpi::json& update,
bool ack);
void ServerSetValue(LocalTopic* topic, const Value& value) {
if (SetValue(topic, value, NT_EVENT_VALUE_REMOTE, false, nullptr)) {
if (topic->IsCached()) {
topic->lastValueNetwork = value;
topic->lastValueFromNetwork = true;
}
}
}
void StartNetwork(net::ClientMessageHandler* network);
void ClearNetwork();
//
// Topic functions
//
LocalTopic* GetOrCreateTopic(std::string_view name);
template <std::invocable<const LocalTopic&> F>
void ForEachTopic(std::string_view prefix, unsigned int types,
F&& func) const {
for (auto&& topic : m_topics) {
if (!topic->Exists()) {
continue;
}
if (!wpi::starts_with(topic->name, prefix)) {
continue;
}
if (types != 0 && (types & topic->type) == 0) {
continue;
}
func(*topic);
}
}
template <std::invocable<const LocalTopic&> F>
void ForEachTopic(std::string_view prefix,
std::span<const std::string_view> types, F&& func) const {
for (auto&& topic : m_topics) {
if (!topic->Exists()) {
continue;
}
if (!wpi::starts_with(topic->name, prefix)) {
continue;
}
if (!types.empty()) {
bool match = false;
for (auto&& type : types) {
if (topic->typeStr == type) {
match = true;
break;
}
}
if (!match) {
continue;
}
}
func(*topic);
}
}
//
// Topic property functions
//
void SetFlags(LocalTopic* topic, unsigned int flags);
void SetPersistent(LocalTopic* topic, bool value);
void SetRetained(LocalTopic* topic, bool value);
void SetCached(LocalTopic* topic, bool value);
void SetProperty(LocalTopic* topic, std::string_view name,
const wpi::json& value);
bool SetProperties(LocalTopic* topic, const wpi::json& update,
bool sendNetwork);
void DeleteProperty(LocalTopic* topic, std::string_view name);
//
// Value functions
//
bool SetEntryValue(NT_Handle pubentryHandle, const Value& value);
bool SetDefaultEntryValue(NT_Handle pubsubentryHandle, const Value& value);
Value* GetSubEntryValue(NT_Handle subentryHandle) {
if (auto subscriber = GetSubEntry(subentryHandle)) {
return &subscriber->topic->lastValue;
} else {
return nullptr;
}
}
//
// Publish/Subscribe/Entry functions
//
LocalSubscriber* Subscribe(LocalTopic* topic, NT_Type type,
std::string_view typeString,
const PubSubOptions& options);
LocalPublisher* Publish(LocalTopic* topic, NT_Type type,
std::string_view typeStr, const wpi::json& properties,
const PubSubOptions& options);
LocalEntry* GetEntry(LocalTopic* topicHandle, NT_Type type,
std::string_view typeStr, const PubSubOptions& options);
LocalEntry* GetEntry(std::string_view name);
void RemoveSubEntry(NT_Handle subentryHandle);
void Unpublish(NT_Handle pubentryHandle);
//
// Multi-subscriber functions
//
LocalMultiSubscriber* AddMultiSubscriber(
std::span<const std::string_view> prefixes, const PubSubOptions& options);
std::unique_ptr<LocalMultiSubscriber> RemoveMultiSubscriber(
NT_MultiSubscriber subHandle);
//
// Lookup functions
//
LocalTopic* GetTopic(NT_Handle handle);
LocalTopic* GetTopicByHandle(NT_Topic topicHandle) {
return m_topics.Get(topicHandle);
}
LocalTopic* GetTopicByName(std::string_view name) {
auto it = m_nameTopics.find(name);
if (it == m_nameTopics.end()) {
return nullptr;
}
return it->second;
}
LocalTopic* GetTopicById(int topicId) {
return m_topics.Get(Handle{m_inst, topicId, Handle::kTopic});
}
LocalSubscriber* GetSubEntry(NT_Handle subentryHandle);
LocalEntry* GetEntryByHandle(NT_Entry entryHandle) {
return m_entries.Get(entryHandle);
}
LocalMultiSubscriber* GetMultiSubscriberByHandle(NT_MultiSubscriber handle) {
return m_multiSubscribers.Get(handle);
}
LocalSubscriber* GetSubscriberByHandle(NT_Subscriber handle) {
return m_subscribers.Get(handle);
}
//
// Listener functions
//
void AddListenerImpl(NT_Listener listenerHandle, LocalTopic* topic,
unsigned int eventMask);
void AddListenerImpl(NT_Listener listenerHandle, LocalSubscriber* subscriber,
unsigned int eventMask, NT_Handle subentryHandle,
bool subscriberOwned);
void AddListenerImpl(NT_Listener listenerHandle,
LocalMultiSubscriber* subscriber, unsigned int eventMask,
bool subscriberOwned);
void RemoveListener(NT_Listener listener, unsigned int mask);
//
// Data log functions
//
LocalDataLogger* StartDataLog(wpi::log::DataLog& log, std::string_view prefix,
std::string_view logPrefix);
void StopDataLog(NT_DataLogger logger);
//
// Schema functions
//
bool HasSchema(std::string_view name);
void AddSchema(std::string_view name, std::string_view type,
std::span<const uint8_t> schema);
void Reset();
private:
// topic functions
void NotifyTopic(LocalTopic* topic, unsigned int eventFlags);
bool SetValue(LocalTopic* topic, const Value& value, unsigned int eventFlags,
bool suppressIfDuplicate, const LocalPublisher* publisher);
void NotifyValue(LocalTopic* topic, const Value& value,
unsigned int eventFlags, bool isDuplicate,
const LocalPublisher* publisher);
void PropertiesUpdated(LocalTopic* topic, const wpi::json& update,
unsigned int eventFlags, bool sendNetwork,
bool updateFlags = true);
void RefreshPubSubActive(LocalTopic* topic, bool warnOnSubMismatch);
LocalPublisher* AddLocalPublisher(LocalTopic* topic,
const wpi::json& properties,
const PubSubConfig& options);
std::unique_ptr<LocalPublisher> RemoveLocalPublisher(NT_Publisher pubHandle);
LocalSubscriber* AddLocalSubscriber(LocalTopic* topic,
const PubSubConfig& options);
std::unique_ptr<LocalSubscriber> RemoveLocalSubscriber(
NT_Subscriber subHandle);
LocalEntry* AddEntry(LocalSubscriber* subscriber) {
auto entry = m_entries.Add(m_inst, subscriber);
subscriber->topic->entries.Add(entry);
return entry;
}
std::unique_ptr<LocalEntry> RemoveEntry(NT_Entry entryHandle) {
auto entry = m_entries.Remove(entryHandle);
if (entry) {
entry->topic->entries.Remove(entry.get());
}
return entry;
}
LocalPublisher* PublishEntry(LocalEntry* entry, NT_Type type);
bool PublishLocalValue(LocalPublisher* publisher, const Value& value,
bool force = false);
private:
int m_inst;
IListenerStorage& m_listenerStorage;
wpi::Logger& m_logger;
net::ClientMessageHandler* m_network{nullptr};
// handle mappings
HandleMap<LocalTopic, 16> m_topics;
HandleMap<LocalPublisher, 16> m_publishers;
HandleMap<LocalSubscriber, 16> m_subscribers;
HandleMap<LocalEntry, 16> m_entries;
HandleMap<LocalMultiSubscriber, 16> m_multiSubscribers;
HandleMap<LocalDataLogger, 16> m_dataloggers;
// name mappings
wpi::StringMap<LocalTopic*> m_nameTopics;
// listeners
wpi::DenseMap<NT_Listener, std::unique_ptr<LocalListener>> m_listeners;
// string-based listeners
VectorSet<LocalListener*> m_topicPrefixListeners;
// schema publishers
wpi::StringMap<NT_Publisher> m_schemas;
};
} // namespace nt::local

View File

@@ -0,0 +1,53 @@
// 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 <wpi/Synchronization.h>
#include "Handle.h"
#include "Types_internal.h"
#include "ValueCircularBuffer.h"
#include "VectorSet.h"
#include "local/LocalTopic.h"
#include "local/PubSubConfig.h"
#include "ntcore_c.h"
namespace nt::local {
struct LocalSubscriber {
static constexpr auto kType = Handle::kSubscriber;
LocalSubscriber(NT_Subscriber handle, LocalTopic* topic, PubSubConfig config)
: handle{handle},
topic{topic},
config{std::move(config)},
pollStorage{config.pollStorage} {}
void UpdateActive() {
// for subscribers, unassigned is a wildcard
// also allow numerically compatible subscribers
active = config.type == NT_UNASSIGNED ||
(config.type == topic->type && config.typeStr == topic->typeStr) ||
IsNumericCompatible(config.type, topic->type);
}
// invariants
wpi::SignalObject<NT_Subscriber> handle;
LocalTopic* topic;
PubSubConfig config;
// whether or not the subscriber should actually receive values
bool active{false};
// polling storage
ValueCircularBuffer pollStorage;
// value listeners
VectorSet<NT_Listener> valueListeners;
};
} // namespace nt::local

View File

@@ -0,0 +1,226 @@
// 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 "LocalTopic.h"
#include <algorithm>
#include "local/LocalDataLogger.h"
using namespace nt::local;
void LocalTopic::StartStopDataLog(LocalDataLogger* logger, int64_t timestamp,
bool publish) {
auto it = std::find_if(
datalogs.begin(), datalogs.end(),
[&](const auto& elem) { return elem.logger == logger->handle; });
if (publish && it == datalogs.end()) {
datalogs.emplace_back(
logger->log,
logger->Start(name, typeStr,
LocalDataLoggerEntry::MakeMetadata(m_propertiesStr),
timestamp),
logger->handle);
datalogType = type;
} else if (!publish && it != datalogs.end()) {
it->Finish(timestamp);
datalogType = NT_UNASSIGNED;
datalogs.erase(it);
}
}
void LocalTopic::UpdateDataLogProperties() {
if (!datalogs.empty()) {
auto now = Now();
auto metadata = LocalDataLoggerEntry::MakeMetadata(m_propertiesStr);
for (auto&& datalog : datalogs) {
datalog.SetMetadata(metadata, now);
}
}
}
wpi::json LocalTopic::SetFlags(unsigned int flags) {
wpi::json update = wpi::json::object();
if ((flags & NT_PERSISTENT) != 0) {
properties["persistent"] = true;
update["persistent"] = true;
} else {
properties.erase("persistent");
update["persistent"] = wpi::json();
}
if ((flags & NT_RETAINED) != 0) {
properties["retained"] = true;
update["retained"] = true;
} else {
properties.erase("retained");
update["retained"] = wpi::json();
}
if ((flags & NT_UNCACHED) != 0) {
properties["cached"] = false;
update["cached"] = false;
} else {
properties.erase("cached");
update["cached"] = wpi::json();
}
if ((flags & NT_UNCACHED) != 0) {
lastValue = {};
lastValueNetwork = {};
lastValueFromNetwork = false;
}
this->m_flags = flags;
return update;
}
wpi::json LocalTopic::SetPersistent(bool value) {
wpi::json update = wpi::json::object();
if (value) {
m_flags |= NT_PERSISTENT;
properties["persistent"] = true;
update["persistent"] = true;
} else {
m_flags &= ~NT_PERSISTENT;
properties.erase("persistent");
update["persistent"] = wpi::json();
}
return update;
}
wpi::json LocalTopic::SetRetained(bool value) {
wpi::json update = wpi::json::object();
if (value) {
m_flags |= NT_RETAINED;
properties["retained"] = true;
update["retained"] = true;
} else {
m_flags &= ~NT_RETAINED;
properties.erase("retained");
update["retained"] = wpi::json();
}
return update;
}
wpi::json LocalTopic::SetCached(bool value) {
wpi::json update = wpi::json::object();
if (value) {
m_flags &= ~NT_UNCACHED;
properties.erase("cached");
update["cached"] = wpi::json();
} else {
m_flags |= NT_UNCACHED;
properties["cached"] = false;
update["cached"] = false;
}
return update;
}
wpi::json LocalTopic::SetProperty(std::string_view name,
const wpi::json& value) {
if (value.is_null()) {
properties.erase(name);
} else {
properties[name] = value;
}
wpi::json update = wpi::json::object();
update[name] = value;
return update;
}
wpi::json LocalTopic::DeleteProperty(std::string_view name) {
properties.erase(name);
wpi::json update = wpi::json::object();
update[name] = wpi::json();
return update;
}
bool LocalTopic::SetProperties(const wpi::json& update) {
if (!update.is_object()) {
return false;
}
for (auto&& change : update.items()) {
if (change.value().is_null()) {
properties.erase(change.key());
} else {
properties[change.key()] = change.value();
}
}
return true;
}
void LocalTopic::RefreshProperties(bool updateFlags) {
if (updateFlags) {
RefreshFlags();
}
m_propertiesStr = properties.dump();
}
wpi::json LocalTopic::CompareProperties(const wpi::json props) {
wpi::json update = wpi::json::object();
// added/changed
for (auto&& prop : props.items()) {
auto it = properties.find(prop.key());
if (it == properties.end() || *it != prop.value()) {
update[prop.key()] = prop.value();
}
}
// removed
for (auto&& prop : properties.items()) {
if (props.find(prop.key()) == props.end()) {
update[prop.key()] = wpi::json();
}
}
return update;
}
void LocalTopic::ResetIfDoesNotExist() {
if (Exists()) {
return;
}
lastValue = {};
lastValueNetwork = {};
lastValueFromNetwork = false;
type = NT_UNASSIGNED;
typeStr.clear();
m_flags = 0;
properties = wpi::json::object();
m_propertiesStr = "{}";
}
void LocalTopic::RefreshFlags() {
auto it = properties.find("persistent");
if (it != properties.end()) {
if (auto val = it->get_ptr<bool*>()) {
if (*val) {
m_flags |= NT_PERSISTENT;
} else {
m_flags &= ~NT_PERSISTENT;
}
}
}
it = properties.find("retained");
if (it != properties.end()) {
if (auto val = it->get_ptr<bool*>()) {
if (*val) {
m_flags |= NT_RETAINED;
} else {
m_flags &= ~NT_RETAINED;
}
}
}
it = properties.find("cached");
if (it != properties.end()) {
if (auto val = it->get_ptr<bool*>()) {
if (*val) {
m_flags &= ~NT_UNCACHED;
} else {
m_flags |= NT_UNCACHED;
}
}
}
if ((m_flags & NT_UNCACHED) != 0) {
lastValue = {};
lastValueNetwork = {};
lastValueFromNetwork = false;
}
}

View 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 <stdint.h>
#include <string>
#include <string_view>
#include <wpi/SmallVector.h>
#include <wpi/Synchronization.h>
#include <wpi/json.h>
#include "Handle.h"
#include "VectorSet.h"
#include "local/LocalDataLogger.h"
#include "local/LocalDataLoggerEntry.h"
#include "ntcore_cpp.h"
namespace nt::local {
struct LocalDataLogger;
struct LocalEntry;
struct LocalMultiSubscriber;
struct LocalPublisher;
struct LocalSubscriber;
constexpr bool IsSpecial(std::string_view name) {
return name.empty() ? false : name.front() == '$';
}
struct LocalTopic {
static constexpr auto kType = Handle::kTopic;
LocalTopic(NT_Topic handle, std::string_view name)
: handle{handle}, name{name}, special{IsSpecial(name)} {}
bool Exists() const { return onNetwork || !localPublishers.empty(); }
bool IsCached() const { return (m_flags & NT_UNCACHED) == 0; }
// starts if publish is true, stops if false
void StartStopDataLog(LocalDataLogger* logger, int64_t timestamp,
bool publish);
void UpdateDataLogProperties();
unsigned int GetFlags() const { return m_flags; }
// these return update json
wpi::json SetFlags(unsigned int flags);
wpi::json SetPersistent(bool value);
wpi::json SetRetained(bool value);
wpi::json SetCached(bool value);
wpi::json SetProperty(std::string_view name, const wpi::json& value);
wpi::json DeleteProperty(std::string_view name);
// returns false if not object
bool SetProperties(const wpi::json& update);
void RefreshProperties(bool updateFlags);
// returns update json
wpi::json CompareProperties(const wpi::json props);
TopicInfo GetTopicInfo() const {
TopicInfo info;
info.topic = handle;
info.name = name;
info.type = type;
info.type_str = typeStr;
info.properties = m_propertiesStr;
return info;
}
void ResetIfDoesNotExist();
// invariants
wpi::SignalObject<NT_Topic> handle;
std::string name;
bool special;
Value lastValue; // also stores timestamp
Value lastValueNetwork;
NT_Type type{NT_UNASSIGNED};
std::string typeStr;
wpi::json properties = wpi::json::object();
LocalEntry* entry{nullptr}; // cached entry for GetEntry()
bool onNetwork{false}; // true if there are any remote publishers
bool lastValueFromNetwork{false};
wpi::SmallVector<LocalDataLoggerEntry, 1> datalogs;
NT_Type datalogType{NT_UNASSIGNED};
VectorSet<LocalPublisher*> localPublishers;
VectorSet<LocalSubscriber*> localSubscribers;
VectorSet<LocalMultiSubscriber*> multiSubscribers;
VectorSet<LocalEntry*> entries;
VectorSet<NT_Listener> listeners;
private:
// update flags from properties
void RefreshFlags();
unsigned int m_flags{0}; // for NT3 APIs
std::string m_propertiesStr{"{}"}; // cached string for GetTopicInfo() et al
};
} // namespace nt::local

View File

@@ -0,0 +1,27 @@
// 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 <string>
#include <string_view>
#include "PubSubOptions.h"
#include "ntcore_c.h"
namespace nt::local {
struct PubSubConfig : public PubSubOptionsImpl {
PubSubConfig() = default;
PubSubConfig(NT_Type type, std::string_view typeStr,
const PubSubOptions& options)
: PubSubOptionsImpl{options}, type{type}, typeStr{typeStr} {
prefixMatch = false;
}
NT_Type type{NT_UNASSIGNED};
std::string typeStr;
};
} // namespace nt::local