mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-27 02:01:42 +00:00
[ntcore] Split LocalStorage implementation into separate files
This commit is contained in:
24
ntcore/src/main/native/cpp/local/LocalDataLogger.cpp
Normal file
24
ntcore/src/main/native/cpp/local/LocalDataLogger.cpp
Normal 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);
|
||||
}
|
||||
39
ntcore/src/main/native/cpp/local/LocalDataLogger.h
Normal file
39
ntcore/src/main/native/cpp/local/LocalDataLogger.h
Normal 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
|
||||
64
ntcore/src/main/native/cpp/local/LocalDataLoggerEntry.cpp
Normal file
64
ntcore/src/main/native/cpp/local/LocalDataLoggerEntry.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
45
ntcore/src/main/native/cpp/local/LocalDataLoggerEntry.h
Normal file
45
ntcore/src/main/native/cpp/local/LocalDataLoggerEntry.h
Normal 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
|
||||
31
ntcore/src/main/native/cpp/local/LocalEntry.h
Normal file
31
ntcore/src/main/native/cpp/local/LocalEntry.h
Normal 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
|
||||
35
ntcore/src/main/native/cpp/local/LocalListener.h
Normal file
35
ntcore/src/main/native/cpp/local/LocalListener.h
Normal 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
|
||||
59
ntcore/src/main/native/cpp/local/LocalMultiSubscriber.h
Normal file
59
ntcore/src/main/native/cpp/local/LocalMultiSubscriber.h
Normal 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
|
||||
36
ntcore/src/main/native/cpp/local/LocalPublisher.h
Normal file
36
ntcore/src/main/native/cpp/local/LocalPublisher.h
Normal 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
|
||||
1100
ntcore/src/main/native/cpp/local/LocalStorageImpl.cpp
Normal file
1100
ntcore/src/main/native/cpp/local/LocalStorageImpl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
320
ntcore/src/main/native/cpp/local/LocalStorageImpl.h
Normal file
320
ntcore/src/main/native/cpp/local/LocalStorageImpl.h
Normal 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
|
||||
53
ntcore/src/main/native/cpp/local/LocalSubscriber.h
Normal file
53
ntcore/src/main/native/cpp/local/LocalSubscriber.h
Normal 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
|
||||
226
ntcore/src/main/native/cpp/local/LocalTopic.cpp
Normal file
226
ntcore/src/main/native/cpp/local/LocalTopic.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
111
ntcore/src/main/native/cpp/local/LocalTopic.h
Normal file
111
ntcore/src/main/native/cpp/local/LocalTopic.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 <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
|
||||
27
ntcore/src/main/native/cpp/local/PubSubConfig.h
Normal file
27
ntcore/src/main/native/cpp/local/PubSubConfig.h
Normal 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
|
||||
Reference in New Issue
Block a user