diff --git a/ntcore/src/main/native/cpp/LocalStorage.h b/ntcore/src/main/native/cpp/LocalStorage.h index a155d2a77d6..5a3bd31aadb 100644 --- a/ntcore/src/main/native/cpp/LocalStorage.h +++ b/ntcore/src/main/native/cpp/LocalStorage.h @@ -134,7 +134,7 @@ class LocalStorage final : public net::ILocalStorage { bool GetTopicPersistent(NT_Topic topicHandle) { std::scoped_lock lock{m_mutex}; if (auto topic = m_impl.GetTopicByHandle(topicHandle)) { - return (topic->flags & NT_PERSISTENT) != 0; + return (topic->GetFlags() & NT_PERSISTENT) != 0; } else { return false; } @@ -150,7 +150,7 @@ class LocalStorage final : public net::ILocalStorage { bool GetTopicRetained(NT_Topic topicHandle) { std::scoped_lock lock{m_mutex}; if (auto topic = m_impl.GetTopicByHandle(topicHandle)) { - return (topic->flags & NT_RETAINED) != 0; + return (topic->GetFlags() & NT_RETAINED) != 0; } else { return false; } @@ -166,7 +166,7 @@ class LocalStorage final : public net::ILocalStorage { bool GetTopicCached(NT_Topic topicHandle) { std::scoped_lock lock{m_mutex}; if (auto topic = m_impl.GetTopicByHandle(topicHandle)) { - return (topic->flags & NT_UNCACHED) == 0; + return (topic->GetFlags() & NT_UNCACHED) == 0; } else { return false; } @@ -392,7 +392,7 @@ class LocalStorage final : public net::ILocalStorage { unsigned int GetEntryFlags(NT_Entry entryHandle) { std::scoped_lock lock{m_mutex}; if (auto entry = m_impl.GetEntryByHandle(entryHandle)) { - return entry->subscriber->topic->flags; + return entry->subscriber->topic->GetFlags(); } else { return 0; } diff --git a/ntcore/src/main/native/cpp/local/LocalDataLogger.cpp b/ntcore/src/main/native/cpp/local/LocalDataLogger.cpp index 86028b2f37b..121c99ab39b 100644 --- a/ntcore/src/main/native/cpp/local/LocalDataLogger.cpp +++ b/ntcore/src/main/native/cpp/local/LocalDataLogger.cpp @@ -8,22 +8,17 @@ #include #include -#include "local/LocalDataLoggerEntry.h" -#include "local/LocalTopic.h" - using namespace nt::local; -int LocalDataLogger::Start(LocalTopic* topic, int64_t time) { - std::string_view typeStr = topic->typeStr; +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(topic->name, prefix).value_or(topic->name)), - typeStr, LocalDataLoggerEntry::MakeMetadata(topic->propertiesStr), time); + return log.Start(fmt::format("{}{}", logPrefix, + wpi::remove_prefix(name, prefix).value_or(name)), + typeStr, metadata, time); } diff --git a/ntcore/src/main/native/cpp/local/LocalDataLogger.h b/ntcore/src/main/native/cpp/local/LocalDataLogger.h index 66d32724300..c92e9ce337e 100644 --- a/ntcore/src/main/native/cpp/local/LocalDataLogger.h +++ b/ntcore/src/main/native/cpp/local/LocalDataLogger.h @@ -27,7 +27,8 @@ struct LocalDataLogger { std::string_view prefix, std::string_view logPrefix) : handle{handle}, log{log}, prefix{prefix}, logPrefix{logPrefix} {} - int Start(LocalTopic* topic, int64_t time); + int Start(std::string_view name, std::string_view typeStr, + std::string_view metadata, int64_t time); NT_DataLogger handle; wpi::log::DataLog& log; diff --git a/ntcore/src/main/native/cpp/local/LocalDataLoggerEntry.cpp b/ntcore/src/main/native/cpp/local/LocalDataLoggerEntry.cpp index aaa3acf00fd..bd0fb47aedf 100644 --- a/ntcore/src/main/native/cpp/local/LocalDataLoggerEntry.cpp +++ b/ntcore/src/main/native/cpp/local/LocalDataLoggerEntry.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include "networktables/NetworkTableValue.h" diff --git a/ntcore/src/main/native/cpp/local/LocalDataLoggerEntry.h b/ntcore/src/main/native/cpp/local/LocalDataLoggerEntry.h index 605635f9ab7..53ff480b19b 100644 --- a/ntcore/src/main/native/cpp/local/LocalDataLoggerEntry.h +++ b/ntcore/src/main/native/cpp/local/LocalDataLoggerEntry.h @@ -4,9 +4,13 @@ #pragma once +#include + #include #include +#include + #include "ntcore_c.h" namespace wpi::log { @@ -28,6 +32,10 @@ struct LocalDataLoggerEntry { 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; diff --git a/ntcore/src/main/native/cpp/local/LocalStorageImpl.cpp b/ntcore/src/main/native/cpp/local/LocalStorageImpl.cpp index 316eb7069cd..7c515c5291a 100644 --- a/ntcore/src/main/native/cpp/local/LocalStorageImpl.cpp +++ b/ntcore/src/main/native/cpp/local/LocalStorageImpl.cpp @@ -4,7 +4,6 @@ #include "LocalStorageImpl.h" -#include #include #include #include @@ -69,20 +68,7 @@ void StorageImpl::NetworkAnnounce(LocalTopic* topic, std::string_view typeStr, // may be properties update, but need to compare to see if it actually // changed to determine whether to update string / send event - wpi::json update = wpi::json::object(); - // added/changed - for (auto&& prop : properties.items()) { - auto it = topic->properties.find(prop.key()); - if (it == topic->properties.end() || *it != prop.value()) { - update[prop.key()] = prop.value(); - } - } - // removed - for (auto&& prop : topic->properties.items()) { - if (properties.find(prop.key()) == properties.end()) { - update[prop.key()] = wpi::json(); - } - } + wpi::json update = topic->CompareProperties(properties); if (!update.empty()) { topic->properties = properties; PropertiesUpdated(topic, update, event, false); @@ -99,7 +85,7 @@ void StorageImpl::RemoveNetworkPublisher(LocalTopic* topic) { topic->onNetwork = false; if (didExist && !topic->Exists()) { DEBUG4("Unpublished {}", topic->name); - CheckReset(topic); + topic->ResetIfDoesNotExist(); NotifyTopic(topic, NT_EVENT_UNPUBLISH); } @@ -199,119 +185,48 @@ LocalTopic* StorageImpl::GetOrCreateTopic(std::string_view name) { // void StorageImpl::SetFlags(LocalTopic* topic, unsigned int flags) { - wpi::json update = wpi::json::object(); - if ((flags & NT_PERSISTENT) != 0) { - topic->properties["persistent"] = true; - update["persistent"] = true; - } else { - topic->properties.erase("persistent"); - update["persistent"] = wpi::json(); - } - if ((flags & NT_RETAINED) != 0) { - topic->properties["retained"] = true; - update["retained"] = true; - } else { - topic->properties.erase("retained"); - update["retained"] = wpi::json(); - } - if ((flags & NT_UNCACHED) != 0) { - topic->properties["cached"] = false; - update["cached"] = false; - } else { - topic->properties.erase("cached"); - update["cached"] = wpi::json(); - } - if ((flags & NT_UNCACHED) != 0) { - topic->lastValue = {}; - topic->lastValueNetwork = {}; - topic->lastValueFromNetwork = false; - } + wpi::json update = topic->SetFlags(flags); if ((flags & NT_UNCACHED) != 0 && (flags & NT_PERSISTENT) != 0) { WARN("topic {}: disabling cached property disables persistent storage", topic->name); } - topic->flags = flags; if (!update.empty()) { PropertiesUpdated(topic, update, NT_EVENT_NONE, true, false); } } void StorageImpl::SetPersistent(LocalTopic* topic, bool value) { - wpi::json update = wpi::json::object(); - if (value) { - topic->flags |= NT_PERSISTENT; - topic->properties["persistent"] = true; - update["persistent"] = true; - } else { - topic->flags &= ~NT_PERSISTENT; - topic->properties.erase("persistent"); - update["persistent"] = wpi::json(); - } - PropertiesUpdated(topic, update, NT_EVENT_NONE, true, false); + PropertiesUpdated(topic, topic->SetPersistent(value), NT_EVENT_NONE, true, + false); } void StorageImpl::SetRetained(LocalTopic* topic, bool value) { - wpi::json update = wpi::json::object(); - if (value) { - topic->flags |= NT_RETAINED; - topic->properties["retained"] = true; - update["retained"] = true; - } else { - topic->flags &= ~NT_RETAINED; - topic->properties.erase("retained"); - update["retained"] = wpi::json(); - } - PropertiesUpdated(topic, update, NT_EVENT_NONE, true, false); + PropertiesUpdated(topic, topic->SetRetained(value), NT_EVENT_NONE, true, + false); } void StorageImpl::SetCached(LocalTopic* topic, bool value) { - wpi::json update = wpi::json::object(); - if (value) { - topic->flags &= ~NT_UNCACHED; - topic->properties.erase("cached"); - update["cached"] = wpi::json(); - } else { - topic->flags |= NT_UNCACHED; - topic->properties["cached"] = false; - update["cached"] = false; - } - PropertiesUpdated(topic, update, NT_EVENT_NONE, true, false); + PropertiesUpdated(topic, topic->SetCached(value), NT_EVENT_NONE, true, false); } void StorageImpl::SetProperty(LocalTopic* topic, std::string_view name, const wpi::json& value) { - if (value.is_null()) { - topic->properties.erase(name); - } else { - topic->properties[name] = value; - } - wpi::json update = wpi::json::object(); - update[name] = value; - PropertiesUpdated(topic, update, NT_EVENT_NONE, true); + PropertiesUpdated(topic, topic->SetProperty(name, value), NT_EVENT_NONE, + true); } bool StorageImpl::SetProperties(LocalTopic* topic, const wpi::json& update, bool sendNetwork) { - if (!update.is_object()) { - return false; - } DEBUG4("SetProperties({},{})", topic->name, sendNetwork); - for (auto&& change : update.items()) { - if (change.value().is_null()) { - topic->properties.erase(change.key()); - } else { - topic->properties[change.key()] = change.value(); - } + if (!topic->SetProperties(update)) { + return false; } PropertiesUpdated(topic, update, NT_EVENT_NONE, sendNetwork); return true; } void StorageImpl::DeleteProperty(LocalTopic* topic, std::string_view name) { - topic->properties.erase(name); - wpi::json update = wpi::json::object(); - update[name] = wpi::json(); - PropertiesUpdated(topic, update, NT_EVENT_NONE, true); + PropertiesUpdated(topic, topic->DeleteProperty(name), NT_EVENT_NONE, true); } // @@ -485,7 +400,7 @@ std::unique_ptr StorageImpl::RemoveLocalPublisher( bool didExist = topic->Exists(); topic->localPublishers.Remove(publisher.get()); if (didExist && !topic->Exists()) { - CheckReset(topic); + topic->ResetIfDoesNotExist(); NotifyTopic(topic, NT_EVENT_UNPUBLISH); } @@ -808,10 +723,7 @@ LocalDataLogger* StorageImpl::StartDataLog(wpi::log::DataLog& log, topic->type == NT_UNASSIGNED || topic->typeStr.empty()) { continue; } - topic->datalogs.emplace_back(log, datalogger->Start(topic.get(), now), - datalogger->handle); - topic->datalogType = topic->type; - + topic->StartStopDataLog(datalogger, now, true); // log current value, if any if (topic->lastValue) { topic->datalogs.back().Append(topic->lastValue); @@ -826,13 +738,7 @@ void StorageImpl::StopDataLog(NT_DataLogger logger) { // finish any active entries auto now = Now(); for (auto&& topic : m_topics) { - auto it = - std::find_if(topic->datalogs.begin(), topic->datalogs.end(), - [&](const auto& elem) { return elem.logger == logger; }); - if (it != topic->datalogs.end()) { - it->log->Finish(it->entry, now); - topic->datalogs.erase(it); - } + topic->StartStopDataLog(datalogger.get(), now, false); } } } @@ -908,47 +814,14 @@ void StorageImpl::NotifyTopic(LocalTopic* topic, unsigned int eventFlags) { auto now = Now(); for (auto&& datalogger : m_dataloggers) { if (PrefixMatch(topic->name, datalogger->prefix, topic->special)) { - auto it = std::find_if(topic->datalogs.begin(), topic->datalogs.end(), - [&](const auto& elem) { - return elem.logger == datalogger->handle; - }); - if ((eventFlags & NT_EVENT_PUBLISH) != 0 && - it == topic->datalogs.end()) { - topic->datalogs.emplace_back(datalogger->log, - datalogger->Start(topic, now), - datalogger->handle); - topic->datalogType = topic->type; - } else if ((eventFlags & NT_EVENT_UNPUBLISH) != 0 && - it != topic->datalogs.end()) { - it->log->Finish(it->entry, now); - topic->datalogType = NT_UNASSIGNED; - topic->datalogs.erase(it); - } + topic->StartStopDataLog(datalogger.get(), now, + (eventFlags & NT_EVENT_PUBLISH) != 0); } } } } else if ((eventFlags & NT_EVENT_PROPERTIES) != 0) { - if (!topic->datalogs.empty()) { - auto metadata = LocalDataLoggerEntry::MakeMetadata(topic->propertiesStr); - for (auto&& datalog : topic->datalogs) { - datalog.log->SetMetadata(datalog.entry, metadata); - } - } - } -} - -void StorageImpl::CheckReset(LocalTopic* topic) { - if (topic->Exists()) { - return; + topic->UpdateDataLogProperties(); } - topic->lastValue = {}; - topic->lastValueNetwork = {}; - topic->lastValueFromNetwork = false; - topic->type = NT_UNASSIGNED; - topic->typeStr.clear(); - topic->flags = 0; - topic->properties = wpi::json::object(); - topic->propertiesStr = "{}"; } bool StorageImpl::SetValue(LocalTopic* topic, const Value& value, @@ -1018,53 +891,14 @@ void StorageImpl::PropertiesUpdated(LocalTopic* topic, const wpi::json& update, bool updateFlags) { DEBUG4("PropertiesUpdated({}, {}, {}, {}, {})", topic->name, update.dump(), eventFlags, sendNetwork, updateFlags); - if (updateFlags) { - // set flags from properties - auto it = topic->properties.find("persistent"); - if (it != topic->properties.end()) { - if (auto val = it->get_ptr()) { - if (*val) { - topic->flags |= NT_PERSISTENT; - } else { - topic->flags &= ~NT_PERSISTENT; - } - } - } - it = topic->properties.find("retained"); - if (it != topic->properties.end()) { - if (auto val = it->get_ptr()) { - if (*val) { - topic->flags |= NT_RETAINED; - } else { - topic->flags &= ~NT_RETAINED; - } - } - } - it = topic->properties.find("cached"); - if (it != topic->properties.end()) { - if (auto val = it->get_ptr()) { - if (*val) { - topic->flags &= ~NT_UNCACHED; - } else { - topic->flags |= NT_UNCACHED; - } - } - } - - if ((topic->flags & NT_UNCACHED) != 0) { - topic->lastValue = {}; - topic->lastValueNetwork = {}; - topic->lastValueFromNetwork = false; - } - - if ((topic->flags & NT_UNCACHED) != 0 && - (topic->flags & NT_PERSISTENT) != 0) { - WARN("topic {}: disabling cached property disables persistent storage", - topic->name); - } + topic->RefreshProperties(updateFlags); + unsigned int flags = topic->GetFlags(); + if (updateFlags && (flags & NT_UNCACHED) != 0 && + (flags & NT_PERSISTENT) != 0) { + WARN("topic {}: disabling cached property disables persistent storage", + topic->name); } - topic->propertiesStr = topic->properties.dump(); NotifyTopic(topic, eventFlags | NT_EVENT_PROPERTIES); // check local flag so we don't echo back received properties changes if (m_network && sendNetwork) { diff --git a/ntcore/src/main/native/cpp/local/LocalStorageImpl.h b/ntcore/src/main/native/cpp/local/LocalStorageImpl.h index c3115a70c48..a44c74c256f 100644 --- a/ntcore/src/main/native/cpp/local/LocalStorageImpl.h +++ b/ntcore/src/main/native/cpp/local/LocalStorageImpl.h @@ -243,8 +243,6 @@ class StorageImpl { // topic functions void NotifyTopic(LocalTopic* topic, unsigned int eventFlags); - void CheckReset(LocalTopic* topic); - bool SetValue(LocalTopic* topic, const Value& value, unsigned int eventFlags, bool suppressIfDuplicate, const LocalPublisher* publisher); void NotifyValue(LocalTopic* topic, const Value& value, diff --git a/ntcore/src/main/native/cpp/local/LocalTopic.cpp b/ntcore/src/main/native/cpp/local/LocalTopic.cpp new file mode 100644 index 00000000000..2803dd5a31f --- /dev/null +++ b/ntcore/src/main/native/cpp/local/LocalTopic.cpp @@ -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 + +#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()) { + 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()) { + 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()) { + if (*val) { + m_flags &= ~NT_UNCACHED; + } else { + m_flags |= NT_UNCACHED; + } + } + } + + if ((m_flags & NT_UNCACHED) != 0) { + lastValue = {}; + lastValueNetwork = {}; + lastValueFromNetwork = false; + } +} diff --git a/ntcore/src/main/native/cpp/local/LocalTopic.h b/ntcore/src/main/native/cpp/local/LocalTopic.h index 1ab6dda264f..278248ea15a 100644 --- a/ntcore/src/main/native/cpp/local/LocalTopic.h +++ b/ntcore/src/main/native/cpp/local/LocalTopic.h @@ -4,6 +4,8 @@ #pragma once +#include + #include #include @@ -13,11 +15,13 @@ #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; @@ -35,7 +39,30 @@ struct LocalTopic { bool Exists() const { return onNetwork || !localPublishers.empty(); } - bool IsCached() const { return (flags & NT_UNCACHED) == 0; } + 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; @@ -43,10 +70,12 @@ struct LocalTopic { info.name = name; info.type = type; info.type_str = typeStr; - info.properties = propertiesStr; + info.properties = m_propertiesStr; return info; } + void ResetIfDoesNotExist(); + // invariants wpi::SignalObject handle; std::string name; @@ -56,8 +85,6 @@ struct LocalTopic { Value lastValueNetwork; NT_Type type{NT_UNASSIGNED}; std::string typeStr; - unsigned int flags{0}; // for NT3 APIs - std::string propertiesStr{"{}"}; // cached string for GetTopicInfo() et al wpi::json properties = wpi::json::object(); LocalEntry* entry{nullptr}; // cached entry for GetEntry() @@ -72,6 +99,13 @@ struct LocalTopic { VectorSet multiSubscribers; VectorSet entries; VectorSet 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