mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-02 02:51:42 +00:00
Prepare ntcore for merge into allwpilib.
This commit is contained in:
340
ntcore/src/main/native/cpp/CallbackManager.h
Normal file
340
ntcore/src/main/native/cpp/CallbackManager.h
Normal file
@@ -0,0 +1,340 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_CALLBACKMANAGER_H_
|
||||
#define NTCORE_CALLBACKMANAGER_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <climits>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <llvm/raw_ostream.h>
|
||||
#include <support/SafeThread.h>
|
||||
#include <support/UidVector.h>
|
||||
#include <support/condition_variable.h>
|
||||
#include <support/mutex.h>
|
||||
|
||||
namespace nt {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template <typename Callback>
|
||||
class ListenerData {
|
||||
public:
|
||||
ListenerData() = default;
|
||||
explicit ListenerData(Callback callback_) : callback(callback_) {}
|
||||
explicit ListenerData(unsigned int poller_uid_) : poller_uid(poller_uid_) {}
|
||||
|
||||
explicit operator bool() const { return callback || poller_uid != UINT_MAX; }
|
||||
|
||||
Callback callback;
|
||||
unsigned int poller_uid = UINT_MAX;
|
||||
};
|
||||
|
||||
// CRTP callback manager thread
|
||||
// @tparam Derived derived class
|
||||
// @tparam NotifierData data buffered for each callback
|
||||
// @tparam ListenerData data stored for each listener
|
||||
// Derived must define the following functions:
|
||||
// bool Matches(const ListenerData& listener, const NotifierData& data);
|
||||
// void SetListener(NotifierData* data, unsigned int listener_uid);
|
||||
// void DoCallback(Callback callback, const NotifierData& data);
|
||||
template <typename Derived, typename TUserInfo,
|
||||
typename TListenerData =
|
||||
ListenerData<std::function<void(const TUserInfo& info)>>,
|
||||
typename TNotifierData = TUserInfo>
|
||||
class CallbackThread : public wpi::SafeThread {
|
||||
public:
|
||||
typedef TUserInfo UserInfo;
|
||||
typedef TNotifierData NotifierData;
|
||||
typedef TListenerData ListenerData;
|
||||
|
||||
~CallbackThread() {
|
||||
// Wake up any blocked pollers
|
||||
for (size_t i = 0; i < m_pollers.size(); ++i) {
|
||||
if (auto poller = m_pollers[i]) poller->Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void Main() override;
|
||||
|
||||
wpi::UidVector<ListenerData, 64> m_listeners;
|
||||
|
||||
std::queue<std::pair<unsigned int, NotifierData>> m_queue;
|
||||
wpi::condition_variable m_queue_empty;
|
||||
|
||||
struct Poller {
|
||||
void Terminate() {
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(poll_mutex);
|
||||
terminating = true;
|
||||
}
|
||||
poll_cond.notify_all();
|
||||
}
|
||||
std::queue<NotifierData> poll_queue;
|
||||
wpi::mutex poll_mutex;
|
||||
wpi::condition_variable poll_cond;
|
||||
bool terminating = false;
|
||||
bool cancelling = false;
|
||||
};
|
||||
wpi::UidVector<std::shared_ptr<Poller>, 64> m_pollers;
|
||||
|
||||
// Must be called with m_mutex held
|
||||
template <typename... Args>
|
||||
void SendPoller(unsigned int poller_uid, Args&&... args) {
|
||||
if (poller_uid > m_pollers.size()) return;
|
||||
auto poller = m_pollers[poller_uid];
|
||||
if (!poller) return;
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(poller->poll_mutex);
|
||||
poller->poll_queue.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
poller->poll_cond.notify_one();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Derived, typename TUserInfo, typename TListenerData,
|
||||
typename TNotifierData>
|
||||
void CallbackThread<Derived, TUserInfo, TListenerData, TNotifierData>::Main() {
|
||||
std::unique_lock<wpi::mutex> lock(m_mutex);
|
||||
while (m_active) {
|
||||
while (m_queue.empty()) {
|
||||
m_cond.wait(lock);
|
||||
if (!m_active) return;
|
||||
}
|
||||
|
||||
while (!m_queue.empty()) {
|
||||
if (!m_active) return;
|
||||
auto item = std::move(m_queue.front());
|
||||
|
||||
if (item.first != UINT_MAX) {
|
||||
if (item.first < m_listeners.size()) {
|
||||
auto& listener = m_listeners[item.first];
|
||||
if (listener &&
|
||||
static_cast<Derived*>(this)->Matches(listener, item.second)) {
|
||||
static_cast<Derived*>(this)->SetListener(&item.second, item.first);
|
||||
if (listener.callback) {
|
||||
lock.unlock();
|
||||
static_cast<Derived*>(this)->DoCallback(listener.callback,
|
||||
item.second);
|
||||
lock.lock();
|
||||
} else if (listener.poller_uid != UINT_MAX) {
|
||||
SendPoller(listener.poller_uid, std::move(item.second));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Use index because iterator might get invalidated.
|
||||
for (size_t i = 0; i < m_listeners.size(); ++i) {
|
||||
auto& listener = m_listeners[i];
|
||||
if (!listener) continue;
|
||||
if (!static_cast<Derived*>(this)->Matches(listener, item.second))
|
||||
continue;
|
||||
static_cast<Derived*>(this)->SetListener(&item.second, i);
|
||||
if (listener.callback) {
|
||||
lock.unlock();
|
||||
static_cast<Derived*>(this)->DoCallback(listener.callback,
|
||||
item.second);
|
||||
lock.lock();
|
||||
} else if (listener.poller_uid != UINT_MAX) {
|
||||
SendPoller(listener.poller_uid, item.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_queue.pop();
|
||||
}
|
||||
|
||||
m_queue_empty.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
// CRTP callback manager
|
||||
// @tparam Derived derived class
|
||||
// @tparam Thread custom thread (must be derived from impl::CallbackThread)
|
||||
//
|
||||
// Derived must define the following functions:
|
||||
// void Start();
|
||||
template <typename Derived, typename Thread>
|
||||
class CallbackManager {
|
||||
friend class RpcServerTest;
|
||||
|
||||
public:
|
||||
void Stop() { m_owner.Stop(); }
|
||||
|
||||
void Remove(unsigned int listener_uid) {
|
||||
auto thr = m_owner.GetThread();
|
||||
if (!thr) return;
|
||||
thr->m_listeners.erase(listener_uid);
|
||||
}
|
||||
|
||||
unsigned int CreatePoller() {
|
||||
static_cast<Derived*>(this)->Start();
|
||||
auto thr = m_owner.GetThread();
|
||||
return thr->m_pollers.emplace_back(
|
||||
std::make_shared<typename Thread::Poller>());
|
||||
}
|
||||
|
||||
void RemovePoller(unsigned int poller_uid) {
|
||||
auto thr = m_owner.GetThread();
|
||||
if (!thr) return;
|
||||
|
||||
// Remove any listeners that are associated with this poller
|
||||
for (size_t i = 0; i < thr->m_listeners.size(); ++i) {
|
||||
if (thr->m_listeners[i].poller_uid == poller_uid)
|
||||
thr->m_listeners.erase(i);
|
||||
}
|
||||
|
||||
// Wake up any blocked pollers
|
||||
if (poller_uid >= thr->m_pollers.size()) return;
|
||||
auto poller = thr->m_pollers[poller_uid];
|
||||
if (!poller) return;
|
||||
poller->Terminate();
|
||||
return thr->m_pollers.erase(poller_uid);
|
||||
}
|
||||
|
||||
bool WaitForQueue(double timeout) {
|
||||
auto thr = m_owner.GetThread();
|
||||
if (!thr) return true;
|
||||
|
||||
auto& lock = thr.GetLock();
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
auto timeout_time = std::chrono::steady_clock::now() +
|
||||
std::chrono::duration<int64_t, std::nano>(
|
||||
static_cast<int64_t>(timeout * 1e9));
|
||||
#else
|
||||
auto timeout_time = std::chrono::steady_clock::now() +
|
||||
std::chrono::duration<double>(timeout);
|
||||
#endif
|
||||
while (!thr->m_queue.empty()) {
|
||||
if (!thr->m_active) return true;
|
||||
if (timeout == 0) return false;
|
||||
if (timeout < 0) {
|
||||
thr->m_queue_empty.wait(lock);
|
||||
} else {
|
||||
auto cond_timed_out = thr->m_queue_empty.wait_until(lock, timeout_time);
|
||||
if (cond_timed_out == std::cv_status::timeout) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<typename Thread::UserInfo> Poll(unsigned int poller_uid) {
|
||||
bool timed_out = false;
|
||||
return Poll(poller_uid, -1, &timed_out);
|
||||
}
|
||||
|
||||
std::vector<typename Thread::UserInfo> Poll(unsigned int poller_uid,
|
||||
double timeout, bool* timed_out) {
|
||||
std::vector<typename Thread::UserInfo> infos;
|
||||
std::shared_ptr<typename Thread::Poller> poller;
|
||||
{
|
||||
auto thr = m_owner.GetThread();
|
||||
if (!thr) return infos;
|
||||
if (poller_uid > thr->m_pollers.size()) return infos;
|
||||
poller = thr->m_pollers[poller_uid];
|
||||
if (!poller) return infos;
|
||||
}
|
||||
|
||||
std::unique_lock<wpi::mutex> lock(poller->poll_mutex);
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
auto timeout_time = std::chrono::steady_clock::now() +
|
||||
std::chrono::duration<int64_t, std::nano>(
|
||||
static_cast<int64_t>(timeout * 1e9));
|
||||
#else
|
||||
auto timeout_time = std::chrono::steady_clock::now() +
|
||||
std::chrono::duration<double>(timeout);
|
||||
#endif
|
||||
*timed_out = false;
|
||||
while (poller->poll_queue.empty()) {
|
||||
if (poller->terminating) return infos;
|
||||
if (poller->cancelling) {
|
||||
// Note: this only works if there's a single thread calling this
|
||||
// function for any particular poller, but that's the intended use.
|
||||
poller->cancelling = false;
|
||||
return infos;
|
||||
}
|
||||
if (timeout == 0) {
|
||||
*timed_out = true;
|
||||
return infos;
|
||||
}
|
||||
if (timeout < 0) {
|
||||
poller->poll_cond.wait(lock);
|
||||
} else {
|
||||
auto cond_timed_out = poller->poll_cond.wait_until(lock, timeout_time);
|
||||
if (cond_timed_out == std::cv_status::timeout) {
|
||||
*timed_out = true;
|
||||
return infos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (!poller->poll_queue.empty()) {
|
||||
infos.emplace_back(std::move(poller->poll_queue.front()));
|
||||
poller->poll_queue.pop();
|
||||
}
|
||||
return infos;
|
||||
}
|
||||
|
||||
void CancelPoll(unsigned int poller_uid) {
|
||||
std::shared_ptr<typename Thread::Poller> poller;
|
||||
{
|
||||
auto thr = m_owner.GetThread();
|
||||
if (!thr) return;
|
||||
if (poller_uid > thr->m_pollers.size()) return;
|
||||
poller = thr->m_pollers[poller_uid];
|
||||
if (!poller) return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(poller->poll_mutex);
|
||||
poller->cancelling = true;
|
||||
}
|
||||
poller->poll_cond.notify_one();
|
||||
}
|
||||
|
||||
protected:
|
||||
template <typename... Args>
|
||||
void DoStart(Args&&... args) {
|
||||
auto thr = m_owner.GetThread();
|
||||
if (!thr) m_owner.Start(new Thread(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
unsigned int DoAdd(Args&&... args) {
|
||||
static_cast<Derived*>(this)->Start();
|
||||
auto thr = m_owner.GetThread();
|
||||
return thr->m_listeners.emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void Send(unsigned int only_listener, Args&&... args) {
|
||||
auto thr = m_owner.GetThread();
|
||||
if (!thr || thr->m_listeners.empty()) return;
|
||||
thr->m_queue.emplace(std::piecewise_construct,
|
||||
std::make_tuple(only_listener),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...));
|
||||
thr->m_cond.notify_one();
|
||||
}
|
||||
|
||||
typename wpi::SafeThreadOwner<Thread>::Proxy GetThread() const {
|
||||
return m_owner.GetThread();
|
||||
}
|
||||
|
||||
private:
|
||||
wpi::SafeThreadOwner<Thread> m_owner;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_CALLBACKMANAGER_H_
|
||||
29
ntcore/src/main/native/cpp/ConnectionNotifier.cpp
Normal file
29
ntcore/src/main/native/cpp/ConnectionNotifier.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "ConnectionNotifier.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
ConnectionNotifier::ConnectionNotifier(int inst) : m_inst(inst) {}
|
||||
|
||||
void ConnectionNotifier::Start() { DoStart(m_inst); }
|
||||
|
||||
unsigned int ConnectionNotifier::Add(
|
||||
std::function<void(const ConnectionNotification& event)> callback) {
|
||||
return DoAdd(callback);
|
||||
}
|
||||
|
||||
unsigned int ConnectionNotifier::AddPolled(unsigned int poller_uid) {
|
||||
return DoAdd(poller_uid);
|
||||
}
|
||||
|
||||
void ConnectionNotifier::NotifyConnection(bool connected,
|
||||
const ConnectionInfo& conn_info,
|
||||
unsigned int only_listener) {
|
||||
Send(only_listener, 0, connected, conn_info);
|
||||
}
|
||||
72
ntcore/src/main/native/cpp/ConnectionNotifier.h
Normal file
72
ntcore/src/main/native/cpp/ConnectionNotifier.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_CONNECTIONNOTIFIER_H_
|
||||
#define NTCORE_CONNECTIONNOTIFIER_H_
|
||||
|
||||
#include "CallbackManager.h"
|
||||
#include "Handle.h"
|
||||
#include "IConnectionNotifier.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
namespace impl {
|
||||
|
||||
class ConnectionNotifierThread
|
||||
: public CallbackThread<ConnectionNotifierThread, ConnectionNotification> {
|
||||
public:
|
||||
explicit ConnectionNotifierThread(int inst) : m_inst(inst) {}
|
||||
|
||||
bool Matches(const ListenerData& listener,
|
||||
const ConnectionNotification& data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetListener(ConnectionNotification* data, unsigned int listener_uid) {
|
||||
data->listener =
|
||||
Handle(m_inst, listener_uid, Handle::kConnectionListener).handle();
|
||||
}
|
||||
|
||||
void DoCallback(
|
||||
std::function<void(const ConnectionNotification& event)> callback,
|
||||
const ConnectionNotification& data) {
|
||||
callback(data);
|
||||
}
|
||||
|
||||
int m_inst;
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
class ConnectionNotifier
|
||||
: public IConnectionNotifier,
|
||||
public CallbackManager<ConnectionNotifier,
|
||||
impl::ConnectionNotifierThread> {
|
||||
friend class ConnectionNotifierTest;
|
||||
friend class CallbackManager<ConnectionNotifier,
|
||||
impl::ConnectionNotifierThread>;
|
||||
|
||||
public:
|
||||
explicit ConnectionNotifier(int inst);
|
||||
|
||||
void Start();
|
||||
|
||||
unsigned int Add(std::function<void(const ConnectionNotification& event)>
|
||||
callback) override;
|
||||
unsigned int AddPolled(unsigned int poller_uid) override;
|
||||
|
||||
void NotifyConnection(bool connected, const ConnectionInfo& conn_info,
|
||||
unsigned int only_listener = UINT_MAX) override;
|
||||
|
||||
private:
|
||||
int m_inst;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_CONNECTIONNOTIFIER_H_
|
||||
639
ntcore/src/main/native/cpp/Dispatcher.cpp
Normal file
639
ntcore/src/main/native/cpp/Dispatcher.cpp
Normal file
@@ -0,0 +1,639 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "Dispatcher.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#include "IConnectionNotifier.h"
|
||||
#include "IStorage.h"
|
||||
#include "Log.h"
|
||||
#include "NetworkConnection.h"
|
||||
#include "tcpsockets/TCPAcceptor.h"
|
||||
#include "tcpsockets/TCPConnector.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
void Dispatcher::StartServer(const Twine& persist_filename,
|
||||
const char* listen_address, unsigned int port) {
|
||||
DispatcherBase::StartServer(
|
||||
persist_filename,
|
||||
std::unique_ptr<wpi::NetworkAcceptor>(new wpi::TCPAcceptor(
|
||||
static_cast<int>(port), listen_address, m_logger)));
|
||||
}
|
||||
|
||||
void Dispatcher::SetServer(const char* server_name, unsigned int port) {
|
||||
std::string server_name_copy(server_name);
|
||||
SetConnector([=]() -> std::unique_ptr<wpi::NetworkStream> {
|
||||
return wpi::TCPConnector::connect(server_name_copy.c_str(),
|
||||
static_cast<int>(port), m_logger, 1);
|
||||
});
|
||||
}
|
||||
|
||||
void Dispatcher::SetServer(
|
||||
ArrayRef<std::pair<StringRef, unsigned int>> servers) {
|
||||
llvm::SmallVector<std::pair<std::string, int>, 16> servers_copy;
|
||||
for (const auto& server : servers)
|
||||
servers_copy.emplace_back(std::string{server.first},
|
||||
static_cast<int>(server.second));
|
||||
|
||||
SetConnector([=]() -> std::unique_ptr<wpi::NetworkStream> {
|
||||
llvm::SmallVector<std::pair<const char*, int>, 16> servers_copy2;
|
||||
for (const auto& server : servers_copy)
|
||||
servers_copy2.emplace_back(server.first.c_str(), server.second);
|
||||
return wpi::TCPConnector::connect_parallel(servers_copy2, m_logger, 1);
|
||||
});
|
||||
}
|
||||
|
||||
void Dispatcher::SetServerTeam(unsigned int team, unsigned int port) {
|
||||
std::pair<StringRef, unsigned int> servers[5];
|
||||
|
||||
// 10.te.am.2
|
||||
llvm::SmallString<32> fixed;
|
||||
{
|
||||
llvm::raw_svector_ostream oss{fixed};
|
||||
oss << "10." << static_cast<int>(team / 100) << '.'
|
||||
<< static_cast<int>(team % 100) << ".2";
|
||||
servers[0] = std::make_pair(oss.str(), port);
|
||||
}
|
||||
|
||||
// 172.22.11.2
|
||||
servers[1] = std::make_pair("172.22.11.2", port);
|
||||
|
||||
// roboRIO-<team>-FRC.local
|
||||
llvm::SmallString<32> mdns;
|
||||
{
|
||||
llvm::raw_svector_ostream oss{mdns};
|
||||
oss << "roboRIO-" << team << "-FRC.local";
|
||||
servers[2] = std::make_pair(oss.str(), port);
|
||||
}
|
||||
|
||||
// roboRIO-<team>-FRC.lan
|
||||
llvm::SmallString<32> mdns_lan;
|
||||
{
|
||||
llvm::raw_svector_ostream oss{mdns_lan};
|
||||
oss << "roboRIO-" << team << "-FRC.lan";
|
||||
servers[3] = std::make_pair(oss.str(), port);
|
||||
}
|
||||
|
||||
// roboRIO-<team>-FRC.frc-field.local
|
||||
llvm::SmallString<64> field_local;
|
||||
{
|
||||
llvm::raw_svector_ostream oss{field_local};
|
||||
oss << "roboRIO-" << team << "-FRC.frc-field.local";
|
||||
servers[4] = std::make_pair(oss.str(), port);
|
||||
}
|
||||
|
||||
SetServer(servers);
|
||||
}
|
||||
|
||||
void Dispatcher::SetServerOverride(const char* server_name, unsigned int port) {
|
||||
std::string server_name_copy(server_name);
|
||||
SetConnectorOverride([=]() -> std::unique_ptr<wpi::NetworkStream> {
|
||||
return wpi::TCPConnector::connect(server_name_copy.c_str(),
|
||||
static_cast<int>(port), m_logger, 1);
|
||||
});
|
||||
}
|
||||
|
||||
void Dispatcher::ClearServerOverride() { ClearConnectorOverride(); }
|
||||
|
||||
DispatcherBase::DispatcherBase(IStorage& storage, IConnectionNotifier& notifier,
|
||||
wpi::Logger& logger)
|
||||
: m_storage(storage), m_notifier(notifier), m_logger(logger) {
|
||||
m_active = false;
|
||||
m_update_rate = 100;
|
||||
}
|
||||
|
||||
DispatcherBase::~DispatcherBase() { Stop(); }
|
||||
|
||||
unsigned int DispatcherBase::GetNetworkMode() const { return m_networkMode; }
|
||||
|
||||
void DispatcherBase::StartServer(
|
||||
const Twine& persist_filename,
|
||||
std::unique_ptr<wpi::NetworkAcceptor> acceptor) {
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(m_user_mutex);
|
||||
if (m_active) return;
|
||||
m_active = true;
|
||||
}
|
||||
m_networkMode = NT_NET_MODE_SERVER | NT_NET_MODE_STARTING;
|
||||
m_persist_filename = persist_filename.str();
|
||||
m_server_acceptor = std::move(acceptor);
|
||||
|
||||
// Load persistent file. Ignore errors, but pass along warnings.
|
||||
if (!persist_filename.isTriviallyEmpty() &&
|
||||
(!persist_filename.isSingleStringRef() ||
|
||||
!persist_filename.getSingleStringRef().empty())) {
|
||||
bool first = true;
|
||||
m_storage.LoadPersistent(
|
||||
persist_filename, [&](size_t line, const char* msg) {
|
||||
if (first) {
|
||||
first = false;
|
||||
WARNING("When reading initial persistent values from '"
|
||||
<< persist_filename << "':");
|
||||
}
|
||||
WARNING(persist_filename << ":" << line << ": " << msg);
|
||||
});
|
||||
}
|
||||
|
||||
m_storage.SetDispatcher(this, true);
|
||||
|
||||
m_dispatch_thread = std::thread(&Dispatcher::DispatchThreadMain, this);
|
||||
m_clientserver_thread = std::thread(&Dispatcher::ServerThreadMain, this);
|
||||
}
|
||||
|
||||
void DispatcherBase::StartClient() {
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(m_user_mutex);
|
||||
if (m_active) return;
|
||||
m_active = true;
|
||||
}
|
||||
m_networkMode = NT_NET_MODE_CLIENT | NT_NET_MODE_STARTING;
|
||||
m_storage.SetDispatcher(this, false);
|
||||
|
||||
m_dispatch_thread = std::thread(&Dispatcher::DispatchThreadMain, this);
|
||||
m_clientserver_thread = std::thread(&Dispatcher::ClientThreadMain, this);
|
||||
}
|
||||
|
||||
void DispatcherBase::Stop() {
|
||||
m_active = false;
|
||||
|
||||
// wake up dispatch thread with a flush
|
||||
m_flush_cv.notify_one();
|
||||
|
||||
// wake up client thread with a reconnect
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(m_user_mutex);
|
||||
m_client_connector = nullptr;
|
||||
}
|
||||
ClientReconnect();
|
||||
|
||||
// wake up server thread by shutting down the socket
|
||||
if (m_server_acceptor) m_server_acceptor->shutdown();
|
||||
|
||||
// join threads, with timeout
|
||||
if (m_dispatch_thread.joinable()) m_dispatch_thread.join();
|
||||
if (m_clientserver_thread.joinable()) m_clientserver_thread.join();
|
||||
|
||||
std::vector<std::shared_ptr<INetworkConnection>> conns;
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(m_user_mutex);
|
||||
conns.swap(m_connections);
|
||||
}
|
||||
|
||||
// close all connections
|
||||
conns.resize(0);
|
||||
}
|
||||
|
||||
void DispatcherBase::SetUpdateRate(double interval) {
|
||||
// don't allow update rates faster than 10 ms or slower than 1 second
|
||||
if (interval < 0.01)
|
||||
interval = 0.01;
|
||||
else if (interval > 1.0)
|
||||
interval = 1.0;
|
||||
m_update_rate = static_cast<unsigned int>(interval * 1000);
|
||||
}
|
||||
|
||||
void DispatcherBase::SetIdentity(const Twine& name) {
|
||||
std::lock_guard<wpi::mutex> lock(m_user_mutex);
|
||||
m_identity = name.str();
|
||||
}
|
||||
|
||||
void DispatcherBase::Flush() {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(m_flush_mutex);
|
||||
// don't allow flushes more often than every 10 ms
|
||||
if ((now - m_last_flush) < std::chrono::milliseconds(10)) return;
|
||||
m_last_flush = now;
|
||||
m_do_flush = true;
|
||||
}
|
||||
m_flush_cv.notify_one();
|
||||
}
|
||||
|
||||
std::vector<ConnectionInfo> DispatcherBase::GetConnections() const {
|
||||
std::vector<ConnectionInfo> conns;
|
||||
if (!m_active) return conns;
|
||||
|
||||
std::lock_guard<wpi::mutex> lock(m_user_mutex);
|
||||
for (auto& conn : m_connections) {
|
||||
if (conn->state() != NetworkConnection::kActive) continue;
|
||||
conns.emplace_back(conn->info());
|
||||
}
|
||||
|
||||
return conns;
|
||||
}
|
||||
|
||||
bool DispatcherBase::IsConnected() const {
|
||||
if (!m_active) return false;
|
||||
|
||||
std::lock_guard<wpi::mutex> lock(m_user_mutex);
|
||||
for (auto& conn : m_connections) {
|
||||
if (conn->state() == NetworkConnection::kActive) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int DispatcherBase::AddListener(
|
||||
std::function<void(const ConnectionNotification& event)> callback,
|
||||
bool immediate_notify) const {
|
||||
std::lock_guard<wpi::mutex> lock(m_user_mutex);
|
||||
unsigned int uid = m_notifier.Add(callback);
|
||||
// perform immediate notifications
|
||||
if (immediate_notify) {
|
||||
for (auto& conn : m_connections) {
|
||||
if (conn->state() != NetworkConnection::kActive) continue;
|
||||
m_notifier.NotifyConnection(true, conn->info(), uid);
|
||||
}
|
||||
}
|
||||
return uid;
|
||||
}
|
||||
|
||||
unsigned int DispatcherBase::AddPolledListener(unsigned int poller_uid,
|
||||
bool immediate_notify) const {
|
||||
std::lock_guard<wpi::mutex> lock(m_user_mutex);
|
||||
unsigned int uid = m_notifier.AddPolled(poller_uid);
|
||||
// perform immediate notifications
|
||||
if (immediate_notify) {
|
||||
for (auto& conn : m_connections) {
|
||||
if (conn->state() != NetworkConnection::kActive) continue;
|
||||
m_notifier.NotifyConnection(true, conn->info(), uid);
|
||||
}
|
||||
}
|
||||
return uid;
|
||||
}
|
||||
|
||||
void DispatcherBase::SetConnector(Connector connector) {
|
||||
std::lock_guard<wpi::mutex> lock(m_user_mutex);
|
||||
m_client_connector = std::move(connector);
|
||||
}
|
||||
|
||||
void DispatcherBase::SetConnectorOverride(Connector connector) {
|
||||
std::lock_guard<wpi::mutex> lock(m_user_mutex);
|
||||
m_client_connector_override = std::move(connector);
|
||||
}
|
||||
|
||||
void DispatcherBase::ClearConnectorOverride() {
|
||||
std::lock_guard<wpi::mutex> lock(m_user_mutex);
|
||||
m_client_connector_override = nullptr;
|
||||
}
|
||||
|
||||
void DispatcherBase::DispatchThreadMain() {
|
||||
auto timeout_time = std::chrono::steady_clock::now();
|
||||
|
||||
static const auto save_delta_time = std::chrono::seconds(1);
|
||||
auto next_save_time = timeout_time + save_delta_time;
|
||||
|
||||
int count = 0;
|
||||
|
||||
while (m_active) {
|
||||
// handle loop taking too long
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
if (start > timeout_time) timeout_time = start;
|
||||
|
||||
// wait for periodic or when flushed
|
||||
timeout_time += std::chrono::milliseconds(m_update_rate);
|
||||
std::unique_lock<wpi::mutex> flush_lock(m_flush_mutex);
|
||||
m_flush_cv.wait_until(flush_lock, timeout_time,
|
||||
[&] { return !m_active || m_do_flush; });
|
||||
m_do_flush = false;
|
||||
flush_lock.unlock();
|
||||
if (!m_active) break; // in case we were woken up to terminate
|
||||
|
||||
// perform periodic persistent save
|
||||
if ((m_networkMode & NT_NET_MODE_SERVER) != 0 &&
|
||||
!m_persist_filename.empty() && start > next_save_time) {
|
||||
next_save_time += save_delta_time;
|
||||
// handle loop taking too long
|
||||
if (start > next_save_time) next_save_time = start + save_delta_time;
|
||||
const char* err = m_storage.SavePersistent(m_persist_filename, true);
|
||||
if (err) WARNING("periodic persistent save: " << err);
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<wpi::mutex> user_lock(m_user_mutex);
|
||||
bool reconnect = false;
|
||||
|
||||
if (++count > 10) {
|
||||
DEBUG("dispatch running " << m_connections.size() << " connections");
|
||||
count = 0;
|
||||
}
|
||||
|
||||
for (auto& conn : m_connections) {
|
||||
// post outgoing messages if connection is active
|
||||
// only send keep-alives on client
|
||||
if (conn->state() == NetworkConnection::kActive)
|
||||
conn->PostOutgoing((m_networkMode & NT_NET_MODE_CLIENT) != 0);
|
||||
|
||||
// if client, reconnect if connection died
|
||||
if ((m_networkMode & NT_NET_MODE_CLIENT) != 0 &&
|
||||
conn->state() == NetworkConnection::kDead)
|
||||
reconnect = true;
|
||||
}
|
||||
// reconnect if we disconnected (and a reconnect is not in progress)
|
||||
if (reconnect && !m_do_reconnect) {
|
||||
m_do_reconnect = true;
|
||||
m_reconnect_cv.notify_one();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DispatcherBase::QueueOutgoing(std::shared_ptr<Message> msg,
|
||||
INetworkConnection* only,
|
||||
INetworkConnection* except) {
|
||||
std::lock_guard<wpi::mutex> user_lock(m_user_mutex);
|
||||
for (auto& conn : m_connections) {
|
||||
if (conn.get() == except) continue;
|
||||
if (only && conn.get() != only) continue;
|
||||
auto state = conn->state();
|
||||
if (state != NetworkConnection::kSynchronized &&
|
||||
state != NetworkConnection::kActive)
|
||||
continue;
|
||||
conn->QueueOutgoing(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void DispatcherBase::ServerThreadMain() {
|
||||
if (m_server_acceptor->start() != 0) {
|
||||
m_active = false;
|
||||
m_networkMode = NT_NET_MODE_SERVER | NT_NET_MODE_FAILURE;
|
||||
return;
|
||||
}
|
||||
m_networkMode = NT_NET_MODE_SERVER;
|
||||
while (m_active) {
|
||||
auto stream = m_server_acceptor->accept();
|
||||
if (!stream) {
|
||||
m_active = false;
|
||||
return;
|
||||
}
|
||||
if (!m_active) {
|
||||
m_networkMode = NT_NET_MODE_NONE;
|
||||
return;
|
||||
}
|
||||
DEBUG("server: client connection from " << stream->getPeerIP() << " port "
|
||||
<< stream->getPeerPort());
|
||||
|
||||
// add to connections list
|
||||
using namespace std::placeholders;
|
||||
auto conn = std::make_shared<NetworkConnection>(
|
||||
++m_connections_uid, std::move(stream), m_notifier, m_logger,
|
||||
std::bind(&Dispatcher::ServerHandshake, this, _1, _2, _3),
|
||||
std::bind(&IStorage::GetMessageEntryType, &m_storage, _1));
|
||||
conn->set_process_incoming(
|
||||
std::bind(&IStorage::ProcessIncoming, &m_storage, _1, _2,
|
||||
std::weak_ptr<NetworkConnection>(conn)));
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(m_user_mutex);
|
||||
// reuse dead connection slots
|
||||
bool placed = false;
|
||||
for (auto& c : m_connections) {
|
||||
if (c->state() == NetworkConnection::kDead) {
|
||||
c = conn;
|
||||
placed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!placed) m_connections.emplace_back(conn);
|
||||
conn->Start();
|
||||
}
|
||||
}
|
||||
m_networkMode = NT_NET_MODE_NONE;
|
||||
}
|
||||
|
||||
void DispatcherBase::ClientThreadMain() {
|
||||
while (m_active) {
|
||||
// sleep between retries
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
Connector connect;
|
||||
|
||||
// get next server to connect to
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(m_user_mutex);
|
||||
if (m_client_connector_override) {
|
||||
connect = m_client_connector_override;
|
||||
} else {
|
||||
if (!m_client_connector) {
|
||||
m_networkMode = NT_NET_MODE_CLIENT | NT_NET_MODE_FAILURE;
|
||||
continue;
|
||||
}
|
||||
connect = m_client_connector;
|
||||
}
|
||||
}
|
||||
|
||||
// try to connect (with timeout)
|
||||
DEBUG("client trying to connect");
|
||||
auto stream = connect();
|
||||
if (!stream) {
|
||||
m_networkMode = NT_NET_MODE_CLIENT | NT_NET_MODE_FAILURE;
|
||||
continue; // keep retrying
|
||||
}
|
||||
DEBUG("client connected");
|
||||
m_networkMode = NT_NET_MODE_CLIENT;
|
||||
|
||||
std::unique_lock<wpi::mutex> lock(m_user_mutex);
|
||||
using namespace std::placeholders;
|
||||
auto conn = std::make_shared<NetworkConnection>(
|
||||
++m_connections_uid, std::move(stream), m_notifier, m_logger,
|
||||
std::bind(&Dispatcher::ClientHandshake, this, _1, _2, _3),
|
||||
std::bind(&IStorage::GetMessageEntryType, &m_storage, _1));
|
||||
conn->set_process_incoming(
|
||||
std::bind(&IStorage::ProcessIncoming, &m_storage, _1, _2,
|
||||
std::weak_ptr<NetworkConnection>(conn)));
|
||||
m_connections.resize(0); // disconnect any current
|
||||
m_connections.emplace_back(conn);
|
||||
conn->set_proto_rev(m_reconnect_proto_rev);
|
||||
conn->Start();
|
||||
|
||||
// reconnect the next time starting with latest protocol revision
|
||||
m_reconnect_proto_rev = 0x0300;
|
||||
|
||||
// block until told to reconnect
|
||||
m_do_reconnect = false;
|
||||
m_reconnect_cv.wait(lock, [&] { return !m_active || m_do_reconnect; });
|
||||
}
|
||||
m_networkMode = NT_NET_MODE_NONE;
|
||||
}
|
||||
|
||||
bool DispatcherBase::ClientHandshake(
|
||||
NetworkConnection& conn, std::function<std::shared_ptr<Message>()> get_msg,
|
||||
std::function<void(llvm::ArrayRef<std::shared_ptr<Message>>)> send_msgs) {
|
||||
// get identity
|
||||
std::string self_id;
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(m_user_mutex);
|
||||
self_id = m_identity;
|
||||
}
|
||||
|
||||
// send client hello
|
||||
DEBUG("client: sending hello");
|
||||
send_msgs(Message::ClientHello(self_id));
|
||||
|
||||
// wait for response
|
||||
auto msg = get_msg();
|
||||
if (!msg) {
|
||||
// disconnected, retry
|
||||
DEBUG("client: server disconnected before first response");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (msg->Is(Message::kProtoUnsup)) {
|
||||
if (msg->id() == 0x0200) ClientReconnect(0x0200);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool new_server = true;
|
||||
if (conn.proto_rev() >= 0x0300) {
|
||||
// should be server hello; if not, disconnect.
|
||||
if (!msg->Is(Message::kServerHello)) return false;
|
||||
conn.set_remote_id(msg->str());
|
||||
if ((msg->flags() & 1) != 0) new_server = false;
|
||||
// get the next message
|
||||
msg = get_msg();
|
||||
}
|
||||
|
||||
// receive initial assignments
|
||||
std::vector<std::shared_ptr<Message>> incoming;
|
||||
for (;;) {
|
||||
if (!msg) {
|
||||
// disconnected, retry
|
||||
DEBUG("client: server disconnected during initial entries");
|
||||
return false;
|
||||
}
|
||||
DEBUG4("received init str=" << msg->str() << " id=" << msg->id()
|
||||
<< " seq_num=" << msg->seq_num_uid());
|
||||
if (msg->Is(Message::kServerHelloDone)) break;
|
||||
// shouldn't receive a keep alive, but handle gracefully
|
||||
if (msg->Is(Message::kKeepAlive)) {
|
||||
msg = get_msg();
|
||||
continue;
|
||||
}
|
||||
if (!msg->Is(Message::kEntryAssign)) {
|
||||
// unexpected message
|
||||
DEBUG("client: received message ("
|
||||
<< msg->type()
|
||||
<< ") other than entry assignment during initial handshake");
|
||||
return false;
|
||||
}
|
||||
incoming.emplace_back(std::move(msg));
|
||||
// get the next message
|
||||
msg = get_msg();
|
||||
}
|
||||
|
||||
// generate outgoing assignments
|
||||
NetworkConnection::Outgoing outgoing;
|
||||
|
||||
m_storage.ApplyInitialAssignments(conn, incoming, new_server, &outgoing);
|
||||
|
||||
if (conn.proto_rev() >= 0x0300)
|
||||
outgoing.emplace_back(Message::ClientHelloDone());
|
||||
|
||||
if (!outgoing.empty()) send_msgs(outgoing);
|
||||
|
||||
INFO("client: CONNECTED to server " << conn.stream().getPeerIP() << " port "
|
||||
<< conn.stream().getPeerPort());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DispatcherBase::ServerHandshake(
|
||||
NetworkConnection& conn, std::function<std::shared_ptr<Message>()> get_msg,
|
||||
std::function<void(llvm::ArrayRef<std::shared_ptr<Message>>)> send_msgs) {
|
||||
// Wait for the client to send us a hello.
|
||||
auto msg = get_msg();
|
||||
if (!msg) {
|
||||
DEBUG("server: client disconnected before sending hello");
|
||||
return false;
|
||||
}
|
||||
if (!msg->Is(Message::kClientHello)) {
|
||||
DEBUG("server: client initial message was not client hello");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the client requested version is not too high.
|
||||
unsigned int proto_rev = msg->id();
|
||||
if (proto_rev > 0x0300) {
|
||||
DEBUG("server: client requested proto > 0x0300");
|
||||
send_msgs(Message::ProtoUnsup());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (proto_rev >= 0x0300) conn.set_remote_id(msg->str());
|
||||
|
||||
// Set the proto version to the client requested version
|
||||
DEBUG("server: client protocol " << proto_rev);
|
||||
conn.set_proto_rev(proto_rev);
|
||||
|
||||
// Send initial set of assignments
|
||||
NetworkConnection::Outgoing outgoing;
|
||||
|
||||
// Start with server hello. TODO: initial connection flag
|
||||
if (proto_rev >= 0x0300) {
|
||||
std::lock_guard<wpi::mutex> lock(m_user_mutex);
|
||||
outgoing.emplace_back(Message::ServerHello(0u, m_identity));
|
||||
}
|
||||
|
||||
// Get snapshot of initial assignments
|
||||
m_storage.GetInitialAssignments(conn, &outgoing);
|
||||
|
||||
// Finish with server hello done
|
||||
outgoing.emplace_back(Message::ServerHelloDone());
|
||||
|
||||
// Batch transmit
|
||||
DEBUG("server: sending initial assignments");
|
||||
send_msgs(outgoing);
|
||||
|
||||
// In proto rev 3.0 and later, the handshake concludes with a client hello
|
||||
// done message, so we can batch the assigns before marking the connection
|
||||
// active. In pre-3.0, we need to just immediately mark it active and hand
|
||||
// off control to the dispatcher to assign them as they arrive.
|
||||
if (proto_rev >= 0x0300) {
|
||||
// receive client initial assignments
|
||||
std::vector<std::shared_ptr<Message>> incoming;
|
||||
msg = get_msg();
|
||||
for (;;) {
|
||||
if (!msg) {
|
||||
// disconnected, retry
|
||||
DEBUG("server: disconnected waiting for initial entries");
|
||||
return false;
|
||||
}
|
||||
if (msg->Is(Message::kClientHelloDone)) break;
|
||||
// shouldn't receive a keep alive, but handle gracefully
|
||||
if (msg->Is(Message::kKeepAlive)) {
|
||||
msg = get_msg();
|
||||
continue;
|
||||
}
|
||||
if (!msg->Is(Message::kEntryAssign)) {
|
||||
// unexpected message
|
||||
DEBUG("server: received message ("
|
||||
<< msg->type()
|
||||
<< ") other than entry assignment during initial handshake");
|
||||
return false;
|
||||
}
|
||||
incoming.push_back(msg);
|
||||
// get the next message (blocks)
|
||||
msg = get_msg();
|
||||
}
|
||||
for (auto& msg : incoming)
|
||||
m_storage.ProcessIncoming(msg, &conn, std::weak_ptr<NetworkConnection>());
|
||||
}
|
||||
|
||||
INFO("server: client CONNECTED: " << conn.stream().getPeerIP() << " port "
|
||||
<< conn.stream().getPeerPort());
|
||||
return true;
|
||||
}
|
||||
|
||||
void DispatcherBase::ClientReconnect(unsigned int proto_rev) {
|
||||
if ((m_networkMode & NT_NET_MODE_SERVER) != 0) return;
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(m_user_mutex);
|
||||
m_reconnect_proto_rev = proto_rev;
|
||||
m_do_reconnect = true;
|
||||
}
|
||||
m_reconnect_cv.notify_one();
|
||||
}
|
||||
151
ntcore/src/main/native/cpp/Dispatcher.h
Normal file
151
ntcore/src/main/native/cpp/Dispatcher.h
Normal file
@@ -0,0 +1,151 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_DISPATCHER_H_
|
||||
#define NTCORE_DISPATCHER_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <llvm/StringRef.h>
|
||||
#include <llvm/Twine.h>
|
||||
#include <support/condition_variable.h>
|
||||
#include <support/mutex.h>
|
||||
|
||||
#include "IDispatcher.h"
|
||||
#include "INetworkConnection.h"
|
||||
|
||||
namespace wpi {
|
||||
class Logger;
|
||||
class NetworkAcceptor;
|
||||
class NetworkStream;
|
||||
} // namespace wpi
|
||||
|
||||
namespace nt {
|
||||
|
||||
class IConnectionNotifier;
|
||||
class IStorage;
|
||||
class NetworkConnection;
|
||||
|
||||
class DispatcherBase : public IDispatcher {
|
||||
friend class DispatcherTest;
|
||||
|
||||
public:
|
||||
typedef std::function<std::unique_ptr<wpi::NetworkStream>()> Connector;
|
||||
|
||||
DispatcherBase(IStorage& storage, IConnectionNotifier& notifier,
|
||||
wpi::Logger& logger);
|
||||
virtual ~DispatcherBase();
|
||||
|
||||
unsigned int GetNetworkMode() const;
|
||||
void StartServer(const Twine& persist_filename,
|
||||
std::unique_ptr<wpi::NetworkAcceptor> acceptor);
|
||||
void StartClient();
|
||||
void Stop();
|
||||
void SetUpdateRate(double interval);
|
||||
void SetIdentity(const Twine& name);
|
||||
void Flush();
|
||||
std::vector<ConnectionInfo> GetConnections() const;
|
||||
bool IsConnected() const;
|
||||
|
||||
unsigned int AddListener(
|
||||
std::function<void(const ConnectionNotification& event)> callback,
|
||||
bool immediate_notify) const;
|
||||
unsigned int AddPolledListener(unsigned int poller_uid,
|
||||
bool immediate_notify) const;
|
||||
|
||||
void SetConnector(Connector connector);
|
||||
void SetConnectorOverride(Connector connector);
|
||||
void ClearConnectorOverride();
|
||||
|
||||
bool active() const { return m_active; }
|
||||
|
||||
DispatcherBase(const DispatcherBase&) = delete;
|
||||
DispatcherBase& operator=(const DispatcherBase&) = delete;
|
||||
|
||||
private:
|
||||
void DispatchThreadMain();
|
||||
void ServerThreadMain();
|
||||
void ClientThreadMain();
|
||||
|
||||
bool ClientHandshake(
|
||||
NetworkConnection& conn,
|
||||
std::function<std::shared_ptr<Message>()> get_msg,
|
||||
std::function<void(llvm::ArrayRef<std::shared_ptr<Message>>)> send_msgs);
|
||||
bool ServerHandshake(
|
||||
NetworkConnection& conn,
|
||||
std::function<std::shared_ptr<Message>()> get_msg,
|
||||
std::function<void(llvm::ArrayRef<std::shared_ptr<Message>>)> send_msgs);
|
||||
|
||||
void ClientReconnect(unsigned int proto_rev = 0x0300);
|
||||
|
||||
void QueueOutgoing(std::shared_ptr<Message> msg, INetworkConnection* only,
|
||||
INetworkConnection* except) override;
|
||||
|
||||
IStorage& m_storage;
|
||||
IConnectionNotifier& m_notifier;
|
||||
unsigned int m_networkMode = NT_NET_MODE_NONE;
|
||||
std::string m_persist_filename;
|
||||
std::thread m_dispatch_thread;
|
||||
std::thread m_clientserver_thread;
|
||||
|
||||
std::unique_ptr<wpi::NetworkAcceptor> m_server_acceptor;
|
||||
Connector m_client_connector_override;
|
||||
Connector m_client_connector;
|
||||
uint8_t m_connections_uid = 0;
|
||||
|
||||
// Mutex for user-accessible items
|
||||
mutable wpi::mutex m_user_mutex;
|
||||
std::vector<std::shared_ptr<INetworkConnection>> m_connections;
|
||||
std::string m_identity;
|
||||
|
||||
std::atomic_bool m_active; // set to false to terminate threads
|
||||
std::atomic_uint m_update_rate; // periodic dispatch update rate, in ms
|
||||
|
||||
// Condition variable for forced dispatch wakeup (flush)
|
||||
wpi::mutex m_flush_mutex;
|
||||
wpi::condition_variable m_flush_cv;
|
||||
std::chrono::steady_clock::time_point m_last_flush;
|
||||
bool m_do_flush = false;
|
||||
|
||||
// Condition variable for client reconnect (uses user mutex)
|
||||
wpi::condition_variable m_reconnect_cv;
|
||||
unsigned int m_reconnect_proto_rev = 0x0300;
|
||||
bool m_do_reconnect = true;
|
||||
|
||||
protected:
|
||||
wpi::Logger& m_logger;
|
||||
};
|
||||
|
||||
class Dispatcher : public DispatcherBase {
|
||||
friend class DispatcherTest;
|
||||
|
||||
public:
|
||||
Dispatcher(IStorage& storage, IConnectionNotifier& notifier,
|
||||
wpi::Logger& logger)
|
||||
: DispatcherBase(storage, notifier, logger) {}
|
||||
|
||||
void StartServer(const Twine& persist_filename, const char* listen_address,
|
||||
unsigned int port);
|
||||
|
||||
void SetServer(const char* server_name, unsigned int port);
|
||||
void SetServer(ArrayRef<std::pair<StringRef, unsigned int>> servers);
|
||||
void SetServerTeam(unsigned int team, unsigned int port);
|
||||
|
||||
void SetServerOverride(const char* server_name, unsigned int port);
|
||||
void ClearServerOverride();
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_DISPATCHER_H_
|
||||
153
ntcore/src/main/native/cpp/DsClient.cpp
Normal file
153
ntcore/src/main/native/cpp/DsClient.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "DsClient.h"
|
||||
|
||||
#include <llvm/SmallString.h>
|
||||
#include <llvm/raw_ostream.h>
|
||||
#include <support/raw_socket_istream.h>
|
||||
|
||||
#include "Dispatcher.h"
|
||||
#include "Log.h"
|
||||
#include "tcpsockets/TCPConnector.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
class DsClient::Thread : public wpi::SafeThread {
|
||||
public:
|
||||
Thread(Dispatcher& dispatcher, wpi::Logger& logger, unsigned int port)
|
||||
: m_dispatcher(dispatcher), m_logger(logger), m_port(port) {}
|
||||
|
||||
void Main();
|
||||
|
||||
Dispatcher& m_dispatcher;
|
||||
wpi::Logger& m_logger;
|
||||
unsigned int m_port;
|
||||
std::unique_ptr<wpi::NetworkStream> m_stream;
|
||||
};
|
||||
|
||||
DsClient::DsClient(Dispatcher& dispatcher, wpi::Logger& logger)
|
||||
: m_dispatcher(dispatcher), m_logger(logger) {}
|
||||
|
||||
void DsClient::Start(unsigned int port) {
|
||||
auto thr = m_owner.GetThread();
|
||||
if (!thr)
|
||||
m_owner.Start(new Thread(m_dispatcher, m_logger, port));
|
||||
else
|
||||
thr->m_port = port;
|
||||
}
|
||||
|
||||
void DsClient::Stop() {
|
||||
{
|
||||
// Close the stream so the read (if any) terminates.
|
||||
auto thr = m_owner.GetThread();
|
||||
if (thr) {
|
||||
thr->m_active = false;
|
||||
if (thr->m_stream) thr->m_stream->close();
|
||||
}
|
||||
}
|
||||
m_owner.Stop();
|
||||
}
|
||||
|
||||
void DsClient::Thread::Main() {
|
||||
unsigned int oldip = 0;
|
||||
wpi::Logger nolog; // to silence log messages from TCPConnector
|
||||
|
||||
while (m_active) {
|
||||
// wait for periodic reconnect or termination
|
||||
auto timeout_time =
|
||||
std::chrono::steady_clock::now() + std::chrono::milliseconds(500);
|
||||
unsigned int port;
|
||||
{
|
||||
std::unique_lock<wpi::mutex> lock(m_mutex);
|
||||
m_cond.wait_until(lock, timeout_time, [&] { return !m_active; });
|
||||
port = m_port;
|
||||
}
|
||||
if (!m_active) goto done;
|
||||
|
||||
// Try to connect to DS on the local machine
|
||||
m_stream = wpi::TCPConnector::connect("127.0.0.1", 1742, nolog, 1);
|
||||
if (!m_active) goto done;
|
||||
if (!m_stream) continue;
|
||||
|
||||
DEBUG3("connected to DS");
|
||||
wpi::raw_socket_istream is(*m_stream);
|
||||
|
||||
while (m_active && !is.has_error()) {
|
||||
// Read JSON "{...}". This is very limited, does not handle quoted "}" or
|
||||
// nested {}, but is sufficient for this purpose.
|
||||
llvm::SmallString<128> json;
|
||||
char ch;
|
||||
|
||||
// Throw away characters until {
|
||||
do {
|
||||
is.read(ch);
|
||||
if (is.has_error()) break;
|
||||
if (!m_active) goto done;
|
||||
} while (ch != '{');
|
||||
json += '{';
|
||||
|
||||
if (is.has_error()) {
|
||||
m_stream = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
// Read characters until }
|
||||
do {
|
||||
is.read(ch);
|
||||
if (is.has_error()) break;
|
||||
if (!m_active) goto done;
|
||||
json += ch;
|
||||
} while (ch != '}');
|
||||
|
||||
if (is.has_error()) {
|
||||
m_stream = nullptr;
|
||||
break;
|
||||
}
|
||||
DEBUG3("json=" << json);
|
||||
|
||||
// Look for "robotIP":12345, and get 12345 portion
|
||||
size_t pos = json.find("\"robotIP\"");
|
||||
if (pos == llvm::StringRef::npos) continue; // could not find?
|
||||
pos += 9;
|
||||
pos = json.find(':', pos);
|
||||
if (pos == llvm::StringRef::npos) continue; // could not find?
|
||||
size_t endpos = json.find_first_not_of("0123456789", pos + 1);
|
||||
DEBUG3("found robotIP=" << json.slice(pos + 1, endpos));
|
||||
|
||||
// Parse into number
|
||||
unsigned int ip = 0;
|
||||
if (json.slice(pos + 1, endpos).getAsInteger(10, ip)) continue; // error
|
||||
|
||||
// If zero, clear the server override
|
||||
if (ip == 0) {
|
||||
m_dispatcher.ClearServerOverride();
|
||||
oldip = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If unchanged, don't reconnect
|
||||
if (ip == oldip) continue;
|
||||
oldip = ip;
|
||||
|
||||
// Convert number into dotted quad
|
||||
json.clear();
|
||||
llvm::raw_svector_ostream os{json};
|
||||
os << ((ip >> 24) & 0xff) << "." << ((ip >> 16) & 0xff) << "."
|
||||
<< ((ip >> 8) & 0xff) << "." << (ip & 0xff);
|
||||
INFO("client: DS overriding server IP to " << os.str());
|
||||
m_dispatcher.SetServerOverride(json.c_str(), port);
|
||||
}
|
||||
|
||||
// We disconnected from the DS, clear the server override
|
||||
m_dispatcher.ClearServerOverride();
|
||||
oldip = 0;
|
||||
}
|
||||
|
||||
done:
|
||||
m_dispatcher.ClearServerOverride();
|
||||
}
|
||||
36
ntcore/src/main/native/cpp/DsClient.h
Normal file
36
ntcore/src/main/native/cpp/DsClient.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_DSCLIENT_H_
|
||||
#define NTCORE_DSCLIENT_H_
|
||||
|
||||
#include <support/SafeThread.h>
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class Dispatcher;
|
||||
|
||||
class DsClient {
|
||||
public:
|
||||
DsClient(Dispatcher& dispatcher, wpi::Logger& logger);
|
||||
~DsClient() = default;
|
||||
|
||||
void Start(unsigned int port);
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
class Thread;
|
||||
wpi::SafeThreadOwner<Thread> m_owner;
|
||||
Dispatcher& m_dispatcher;
|
||||
wpi::Logger& m_logger;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_DSCLIENT_H_
|
||||
89
ntcore/src/main/native/cpp/EntryNotifier.cpp
Normal file
89
ntcore/src/main/native/cpp/EntryNotifier.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "EntryNotifier.h"
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
EntryNotifier::EntryNotifier(int inst, wpi::Logger& logger)
|
||||
: m_inst(inst), m_logger(logger) {
|
||||
m_local_notifiers = false;
|
||||
}
|
||||
|
||||
void EntryNotifier::Start() { DoStart(m_inst); }
|
||||
|
||||
bool EntryNotifier::local_notifiers() const { return m_local_notifiers; }
|
||||
|
||||
bool impl::EntryNotifierThread::Matches(const EntryListenerData& listener,
|
||||
const EntryNotification& data) {
|
||||
if (!data.value) return false;
|
||||
|
||||
// Flags must be within requested flag set for this listener.
|
||||
// Because assign messages can result in both a value and flags update,
|
||||
// we handle that case specially.
|
||||
unsigned int listen_flags =
|
||||
listener.flags & ~(NT_NOTIFY_IMMEDIATE | NT_NOTIFY_LOCAL);
|
||||
unsigned int flags = data.flags & ~(NT_NOTIFY_IMMEDIATE | NT_NOTIFY_LOCAL);
|
||||
unsigned int assign_both = NT_NOTIFY_UPDATE | NT_NOTIFY_FLAGS;
|
||||
if ((flags & assign_both) == assign_both) {
|
||||
if ((listen_flags & assign_both) == 0) return false;
|
||||
listen_flags &= ~assign_both;
|
||||
flags &= ~assign_both;
|
||||
}
|
||||
if ((flags & ~listen_flags) != 0) return false;
|
||||
|
||||
// must match local id or prefix
|
||||
if (listener.entry != 0 && data.entry != listener.entry) return false;
|
||||
if (listener.entry == 0 &&
|
||||
!llvm::StringRef(data.name).startswith(listener.prefix))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int EntryNotifier::Add(
|
||||
std::function<void(const EntryNotification& event)> callback,
|
||||
StringRef prefix, unsigned int flags) {
|
||||
if ((flags & NT_NOTIFY_LOCAL) != 0) m_local_notifiers = true;
|
||||
return DoAdd(callback, prefix, flags);
|
||||
}
|
||||
|
||||
unsigned int EntryNotifier::Add(
|
||||
std::function<void(const EntryNotification& event)> callback,
|
||||
unsigned int local_id, unsigned int flags) {
|
||||
if ((flags & NT_NOTIFY_LOCAL) != 0) m_local_notifiers = true;
|
||||
return DoAdd(callback, Handle(m_inst, local_id, Handle::kEntry), flags);
|
||||
}
|
||||
|
||||
unsigned int EntryNotifier::AddPolled(unsigned int poller_uid,
|
||||
llvm::StringRef prefix,
|
||||
unsigned int flags) {
|
||||
if ((flags & NT_NOTIFY_LOCAL) != 0) m_local_notifiers = true;
|
||||
return DoAdd(poller_uid, prefix, flags);
|
||||
}
|
||||
|
||||
unsigned int EntryNotifier::AddPolled(unsigned int poller_uid,
|
||||
unsigned int local_id,
|
||||
unsigned int flags) {
|
||||
if ((flags & NT_NOTIFY_LOCAL) != 0) m_local_notifiers = true;
|
||||
return DoAdd(poller_uid, Handle(m_inst, local_id, Handle::kEntry), flags);
|
||||
}
|
||||
|
||||
void EntryNotifier::NotifyEntry(unsigned int local_id, StringRef name,
|
||||
std::shared_ptr<Value> value,
|
||||
unsigned int flags,
|
||||
unsigned int only_listener) {
|
||||
// optimization: don't generate needless local queue entries if we have
|
||||
// no local listeners (as this is a common case on the server side)
|
||||
if ((flags & NT_NOTIFY_LOCAL) != 0 && !m_local_notifiers) return;
|
||||
DEBUG("notifying '" << name << "' (local=" << local_id
|
||||
<< "), flags=" << flags);
|
||||
Send(only_listener, 0, Handle(m_inst, local_id, Handle::kEntry).handle(),
|
||||
name, value, flags);
|
||||
}
|
||||
109
ntcore/src/main/native/cpp/EntryNotifier.h
Normal file
109
ntcore/src/main/native/cpp/EntryNotifier.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_ENTRYNOTIFIER_H_
|
||||
#define NTCORE_ENTRYNOTIFIER_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "CallbackManager.h"
|
||||
#include "Handle.h"
|
||||
#include "IEntryNotifier.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace wpi {
|
||||
class Logger;
|
||||
} // namespace wpi
|
||||
|
||||
namespace nt {
|
||||
|
||||
namespace impl {
|
||||
|
||||
struct EntryListenerData
|
||||
: public ListenerData<std::function<void(const EntryNotification& event)>> {
|
||||
EntryListenerData() = default;
|
||||
EntryListenerData(
|
||||
std::function<void(const EntryNotification& event)> callback_,
|
||||
StringRef prefix_, unsigned int flags_)
|
||||
: ListenerData(callback_), prefix(prefix_), flags(flags_) {}
|
||||
EntryListenerData(
|
||||
std::function<void(const EntryNotification& event)> callback_,
|
||||
NT_Entry entry_, unsigned int flags_)
|
||||
: ListenerData(callback_), entry(entry_), flags(flags_) {}
|
||||
EntryListenerData(unsigned int poller_uid_, StringRef prefix_,
|
||||
unsigned int flags_)
|
||||
: ListenerData(poller_uid_), prefix(prefix_), flags(flags_) {}
|
||||
EntryListenerData(unsigned int poller_uid_, NT_Entry entry_,
|
||||
unsigned int flags_)
|
||||
: ListenerData(poller_uid_), entry(entry_), flags(flags_) {}
|
||||
|
||||
std::string prefix;
|
||||
NT_Entry entry = 0;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
class EntryNotifierThread
|
||||
: public CallbackThread<EntryNotifierThread, EntryNotification,
|
||||
EntryListenerData> {
|
||||
public:
|
||||
explicit EntryNotifierThread(int inst) : m_inst(inst) {}
|
||||
|
||||
bool Matches(const EntryListenerData& listener,
|
||||
const EntryNotification& data);
|
||||
|
||||
void SetListener(EntryNotification* data, unsigned int listener_uid) {
|
||||
data->listener =
|
||||
Handle(m_inst, listener_uid, Handle::kEntryListener).handle();
|
||||
}
|
||||
|
||||
void DoCallback(std::function<void(const EntryNotification& event)> callback,
|
||||
const EntryNotification& data) {
|
||||
callback(data);
|
||||
}
|
||||
|
||||
int m_inst;
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
class EntryNotifier
|
||||
: public IEntryNotifier,
|
||||
public CallbackManager<EntryNotifier, impl::EntryNotifierThread> {
|
||||
friend class EntryNotifierTest;
|
||||
friend class CallbackManager<EntryNotifier, impl::EntryNotifierThread>;
|
||||
|
||||
public:
|
||||
explicit EntryNotifier(int inst, wpi::Logger& logger);
|
||||
|
||||
void Start();
|
||||
|
||||
bool local_notifiers() const override;
|
||||
|
||||
unsigned int Add(std::function<void(const EntryNotification& event)> callback,
|
||||
llvm::StringRef prefix, unsigned int flags) override;
|
||||
unsigned int Add(std::function<void(const EntryNotification& event)> callback,
|
||||
unsigned int local_id, unsigned int flags) override;
|
||||
unsigned int AddPolled(unsigned int poller_uid, llvm::StringRef prefix,
|
||||
unsigned int flags) override;
|
||||
unsigned int AddPolled(unsigned int poller_uid, unsigned int local_id,
|
||||
unsigned int flags) override;
|
||||
|
||||
void NotifyEntry(unsigned int local_id, StringRef name,
|
||||
std::shared_ptr<Value> value, unsigned int flags,
|
||||
unsigned int only_listener = UINT_MAX) override;
|
||||
|
||||
private:
|
||||
int m_inst;
|
||||
wpi::Logger& m_logger;
|
||||
std::atomic_bool m_local_notifiers;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_ENTRYNOTIFIER_H_
|
||||
65
ntcore/src/main/native/cpp/Handle.h
Normal file
65
ntcore/src/main/native/cpp/Handle.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_HANDLE_H_
|
||||
#define NTCORE_HANDLE_H_
|
||||
|
||||
#include "ntcore_c.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
// Handle data layout:
|
||||
// Bits 30-28: Type
|
||||
// Bits 27-20: Instance index
|
||||
// Bits 19-0: Handle index (0/unused for instance handles)
|
||||
|
||||
class Handle {
|
||||
public:
|
||||
enum Type {
|
||||
kConnectionListener = 1,
|
||||
kConnectionListenerPoller,
|
||||
kEntry,
|
||||
kEntryListener,
|
||||
kEntryListenerPoller,
|
||||
kInstance,
|
||||
kLogger,
|
||||
kLoggerPoller,
|
||||
kRpcCall,
|
||||
kRpcCallPoller
|
||||
};
|
||||
enum { kIndexMax = 0xfffff };
|
||||
|
||||
explicit Handle(NT_Handle handle) : m_handle(handle) {}
|
||||
operator NT_Handle() const { return m_handle; }
|
||||
|
||||
NT_Handle handle() const { return m_handle; }
|
||||
|
||||
Handle(int inst, int index, Type type) {
|
||||
if (inst < 0 || index < 0) {
|
||||
m_handle = 0;
|
||||
return;
|
||||
}
|
||||
m_handle = ((static_cast<int>(type) & 0xf) << 27) | ((inst & 0x7f) << 20) |
|
||||
(index & 0xfffff);
|
||||
}
|
||||
|
||||
int GetIndex() const { return static_cast<int>(m_handle) & 0xfffff; }
|
||||
Type GetType() const {
|
||||
return static_cast<Type>((static_cast<int>(m_handle) >> 27) & 0xf);
|
||||
}
|
||||
int GetInst() const { return (static_cast<int>(m_handle) >> 20) & 0x7f; }
|
||||
bool IsType(Type type) const { return type == GetType(); }
|
||||
int GetTypedIndex(Type type) const { return IsType(type) ? GetIndex() : -1; }
|
||||
int GetTypedInst(Type type) const { return IsType(type) ? GetInst() : -1; }
|
||||
|
||||
private:
|
||||
NT_Handle m_handle;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_HANDLE_H_
|
||||
32
ntcore/src/main/native/cpp/IConnectionNotifier.h
Normal file
32
ntcore/src/main/native/cpp/IConnectionNotifier.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_ICONNECTIONNOTIFIER_H_
|
||||
#define NTCORE_ICONNECTIONNOTIFIER_H_
|
||||
|
||||
#include <climits>
|
||||
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class IConnectionNotifier {
|
||||
public:
|
||||
IConnectionNotifier() = default;
|
||||
IConnectionNotifier(const IConnectionNotifier&) = delete;
|
||||
IConnectionNotifier& operator=(const IConnectionNotifier&) = delete;
|
||||
virtual ~IConnectionNotifier() = default;
|
||||
virtual unsigned int Add(
|
||||
std::function<void(const ConnectionNotification& event)> callback) = 0;
|
||||
virtual unsigned int AddPolled(unsigned int poller_uid) = 0;
|
||||
virtual void NotifyConnection(bool connected, const ConnectionInfo& conn_info,
|
||||
unsigned int only_listener = UINT_MAX) = 0;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_ICONNECTIONNOTIFIER_H_
|
||||
34
ntcore/src/main/native/cpp/IDispatcher.h
Normal file
34
ntcore/src/main/native/cpp/IDispatcher.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_IDISPATCHER_H_
|
||||
#define NTCORE_IDISPATCHER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "Message.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class INetworkConnection;
|
||||
|
||||
// Interface for generation of outgoing messages to break a dependency loop
|
||||
// between Storage and Dispatcher.
|
||||
class IDispatcher {
|
||||
public:
|
||||
IDispatcher() = default;
|
||||
IDispatcher(const IDispatcher&) = delete;
|
||||
IDispatcher& operator=(const IDispatcher&) = delete;
|
||||
virtual ~IDispatcher() = default;
|
||||
virtual void QueueOutgoing(std::shared_ptr<Message> msg,
|
||||
INetworkConnection* only,
|
||||
INetworkConnection* except) = 0;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_IDISPATCHER_H_
|
||||
45
ntcore/src/main/native/cpp/IEntryNotifier.h
Normal file
45
ntcore/src/main/native/cpp/IEntryNotifier.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_IENTRYNOTIFIER_H_
|
||||
#define NTCORE_IENTRYNOTIFIER_H_
|
||||
|
||||
#include <climits>
|
||||
#include <memory>
|
||||
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class IEntryNotifier {
|
||||
public:
|
||||
IEntryNotifier() = default;
|
||||
IEntryNotifier(const IEntryNotifier&) = delete;
|
||||
IEntryNotifier& operator=(const IEntryNotifier&) = delete;
|
||||
virtual ~IEntryNotifier() = default;
|
||||
virtual bool local_notifiers() const = 0;
|
||||
|
||||
virtual unsigned int Add(
|
||||
std::function<void(const EntryNotification& event)> callback,
|
||||
llvm::StringRef prefix, unsigned int flags) = 0;
|
||||
virtual unsigned int Add(
|
||||
std::function<void(const EntryNotification& event)> callback,
|
||||
unsigned int local_id, unsigned int flags) = 0;
|
||||
virtual unsigned int AddPolled(unsigned int poller_uid,
|
||||
llvm::StringRef prefix,
|
||||
unsigned int flags) = 0;
|
||||
virtual unsigned int AddPolled(unsigned int poller_uid, unsigned int local_id,
|
||||
unsigned int flags) = 0;
|
||||
|
||||
virtual void NotifyEntry(unsigned int local_id, StringRef name,
|
||||
std::shared_ptr<Value> value, unsigned int flags,
|
||||
unsigned int only_listener = UINT_MAX) = 0;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_IENTRYNOTIFIER_H_
|
||||
41
ntcore/src/main/native/cpp/INetworkConnection.h
Normal file
41
ntcore/src/main/native/cpp/INetworkConnection.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_INETWORKCONNECTION_H_
|
||||
#define NTCORE_INETWORKCONNECTION_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "Message.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class INetworkConnection {
|
||||
public:
|
||||
enum State { kCreated, kInit, kHandshake, kSynchronized, kActive, kDead };
|
||||
|
||||
INetworkConnection() = default;
|
||||
INetworkConnection(const INetworkConnection&) = delete;
|
||||
INetworkConnection& operator=(const INetworkConnection&) = delete;
|
||||
virtual ~INetworkConnection() = default;
|
||||
|
||||
virtual ConnectionInfo info() const = 0;
|
||||
|
||||
virtual void QueueOutgoing(std::shared_ptr<Message> msg) = 0;
|
||||
virtual void PostOutgoing(bool keep_alive) = 0;
|
||||
|
||||
virtual unsigned int proto_rev() const = 0;
|
||||
virtual void set_proto_rev(unsigned int proto_rev) = 0;
|
||||
|
||||
virtual State state() const = 0;
|
||||
virtual void set_state(State state) = 0;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_INETWORKCONNECTION_H_
|
||||
38
ntcore/src/main/native/cpp/IRpcServer.h
Normal file
38
ntcore/src/main/native/cpp/IRpcServer.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_IRPCSERVER_H_
|
||||
#define NTCORE_IRPCSERVER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "Message.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class IRpcServer {
|
||||
public:
|
||||
typedef std::function<void(StringRef result)> SendResponseFunc;
|
||||
|
||||
IRpcServer() = default;
|
||||
IRpcServer(const IRpcServer&) = delete;
|
||||
IRpcServer& operator=(const IRpcServer&) = delete;
|
||||
virtual ~IRpcServer() = default;
|
||||
|
||||
virtual void RemoveRpc(unsigned int rpc_uid) = 0;
|
||||
|
||||
virtual void ProcessRpc(unsigned int local_id, unsigned int call_uid,
|
||||
StringRef name, StringRef params,
|
||||
const ConnectionInfo& conn,
|
||||
SendResponseFunc send_response,
|
||||
unsigned int rpc_uid) = 0;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_IRPCSERVER_H_
|
||||
65
ntcore/src/main/native/cpp/IStorage.h
Normal file
65
ntcore/src/main/native/cpp/IStorage.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_ISTORAGE_H_
|
||||
#define NTCORE_ISTORAGE_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <llvm/ArrayRef.h>
|
||||
#include <llvm/Twine.h>
|
||||
|
||||
#include "Message.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class IDispatcher;
|
||||
class INetworkConnection;
|
||||
|
||||
class IStorage {
|
||||
public:
|
||||
IStorage() = default;
|
||||
IStorage(const IStorage&) = delete;
|
||||
IStorage& operator=(const IStorage&) = delete;
|
||||
virtual ~IStorage() = default;
|
||||
|
||||
// Accessors required by Dispatcher. An interface is used for
|
||||
// generation of outgoing messages to break a dependency loop between
|
||||
// Storage and Dispatcher.
|
||||
virtual void SetDispatcher(IDispatcher* dispatcher, bool server) = 0;
|
||||
virtual void ClearDispatcher() = 0;
|
||||
|
||||
// Required for wire protocol 2.0 to get the entry type of an entry when
|
||||
// receiving entry updates (because the length/type is not provided in the
|
||||
// message itself). Not used in wire protocol 3.0.
|
||||
virtual NT_Type GetMessageEntryType(unsigned int id) const = 0;
|
||||
|
||||
virtual void ProcessIncoming(std::shared_ptr<Message> msg,
|
||||
INetworkConnection* conn,
|
||||
std::weak_ptr<INetworkConnection> conn_weak) = 0;
|
||||
virtual void GetInitialAssignments(
|
||||
INetworkConnection& conn,
|
||||
std::vector<std::shared_ptr<Message>>* msgs) = 0;
|
||||
virtual void ApplyInitialAssignments(
|
||||
INetworkConnection& conn, llvm::ArrayRef<std::shared_ptr<Message>> msgs,
|
||||
bool new_server, std::vector<std::shared_ptr<Message>>* out_msgs) = 0;
|
||||
|
||||
// Filename-based save/load functions. Used both by periodic saves and
|
||||
// accessible directly via the user API.
|
||||
virtual const char* SavePersistent(const Twine& filename,
|
||||
bool periodic) const = 0;
|
||||
virtual const char* LoadPersistent(
|
||||
const Twine& filename,
|
||||
std::function<void(size_t line, const char* msg)> warn) = 0;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_ISTORAGE_H_
|
||||
108
ntcore/src/main/native/cpp/InstanceImpl.cpp
Normal file
108
ntcore/src/main/native/cpp/InstanceImpl.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "InstanceImpl.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
std::atomic<int> InstanceImpl::s_default{-1};
|
||||
std::atomic<InstanceImpl*> InstanceImpl::s_fast_instances[10];
|
||||
wpi::UidVector<std::unique_ptr<InstanceImpl>, 10> InstanceImpl::s_instances;
|
||||
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)),
|
||||
connection_notifier(inst),
|
||||
entry_notifier(inst, logger),
|
||||
rpc_server(inst, logger),
|
||||
storage(entry_notifier, rpc_server, logger),
|
||||
dispatcher(storage, connection_notifier, logger),
|
||||
ds_client(dispatcher, logger) {
|
||||
logger.set_min_level(logger_impl.GetMinLevel());
|
||||
}
|
||||
|
||||
InstanceImpl::~InstanceImpl() { logger.SetLogger(nullptr); }
|
||||
|
||||
InstanceImpl* InstanceImpl::GetDefault() { return Get(GetDefaultIndex()); }
|
||||
|
||||
InstanceImpl* InstanceImpl::Get(int inst) {
|
||||
if (inst < 0) return nullptr;
|
||||
|
||||
// fast path, just an atomic read
|
||||
if (static_cast<unsigned int>(inst) <
|
||||
(sizeof(s_fast_instances) / sizeof(s_fast_instances[0]))) {
|
||||
InstanceImpl* ptr = s_fast_instances[inst];
|
||||
if (ptr) return ptr;
|
||||
}
|
||||
|
||||
// slow path
|
||||
std::lock_guard<wpi::mutex> lock(s_mutex);
|
||||
|
||||
// static fast-path block
|
||||
if (static_cast<unsigned int>(inst) <
|
||||
(sizeof(s_fast_instances) / sizeof(s_fast_instances[0]))) {
|
||||
// double-check
|
||||
return s_fast_instances[inst];
|
||||
}
|
||||
|
||||
// vector
|
||||
if (static_cast<unsigned int>(inst) < s_instances.size()) {
|
||||
return s_instances[inst].get();
|
||||
}
|
||||
|
||||
// doesn't exist
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int InstanceImpl::GetDefaultIndex() {
|
||||
int inst = s_default;
|
||||
if (inst >= 0) return inst;
|
||||
|
||||
// slow path
|
||||
std::lock_guard<wpi::mutex> lock(s_mutex);
|
||||
|
||||
// double-check
|
||||
inst = s_default;
|
||||
if (inst >= 0) return inst;
|
||||
|
||||
// alloc and save
|
||||
inst = AllocImpl();
|
||||
s_default = inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
int InstanceImpl::Alloc() {
|
||||
std::lock_guard<wpi::mutex> lock(s_mutex);
|
||||
return AllocImpl();
|
||||
}
|
||||
|
||||
int InstanceImpl::AllocImpl() {
|
||||
unsigned int inst = s_instances.emplace_back();
|
||||
InstanceImpl* ptr = new InstanceImpl(inst);
|
||||
s_instances[inst].reset(ptr);
|
||||
|
||||
if (inst < (sizeof(s_fast_instances) / sizeof(s_fast_instances[0]))) {
|
||||
s_fast_instances[inst] = ptr;
|
||||
}
|
||||
|
||||
return static_cast<int>(inst);
|
||||
}
|
||||
|
||||
void InstanceImpl::Destroy(int inst) {
|
||||
std::lock_guard<wpi::mutex> lock(s_mutex);
|
||||
if (inst < 0 || static_cast<unsigned int>(inst) >= s_instances.size()) return;
|
||||
|
||||
if (static_cast<unsigned int>(inst) <
|
||||
(sizeof(s_fast_instances) / sizeof(s_fast_instances[0]))) {
|
||||
s_fast_instances[inst] = nullptr;
|
||||
}
|
||||
|
||||
s_instances.erase(inst);
|
||||
}
|
||||
60
ntcore/src/main/native/cpp/InstanceImpl.h
Normal file
60
ntcore/src/main/native/cpp/InstanceImpl.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_INSTANCEIMPL_H_
|
||||
#define NTCORE_INSTANCEIMPL_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
#include <support/UidVector.h>
|
||||
#include <support/mutex.h>
|
||||
|
||||
#include "ConnectionNotifier.h"
|
||||
#include "Dispatcher.h"
|
||||
#include "DsClient.h"
|
||||
#include "EntryNotifier.h"
|
||||
#include "Log.h"
|
||||
#include "LoggerImpl.h"
|
||||
#include "RpcServer.h"
|
||||
#include "Storage.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class InstanceImpl {
|
||||
public:
|
||||
explicit InstanceImpl(int inst);
|
||||
~InstanceImpl();
|
||||
|
||||
// Instance repository
|
||||
static InstanceImpl* GetDefault();
|
||||
static InstanceImpl* Get(int inst);
|
||||
static int GetDefaultIndex();
|
||||
static int Alloc();
|
||||
static void Destroy(int inst);
|
||||
|
||||
LoggerImpl logger_impl;
|
||||
wpi::Logger logger;
|
||||
ConnectionNotifier connection_notifier;
|
||||
EntryNotifier entry_notifier;
|
||||
RpcServer rpc_server;
|
||||
Storage storage;
|
||||
Dispatcher dispatcher;
|
||||
DsClient ds_client;
|
||||
|
||||
private:
|
||||
static int AllocImpl();
|
||||
|
||||
static std::atomic<int> s_default;
|
||||
static std::atomic<InstanceImpl*> s_fast_instances[10];
|
||||
static wpi::UidVector<std::unique_ptr<InstanceImpl>, 10> s_instances;
|
||||
static wpi::mutex s_mutex;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_INSTANCEIMPL_H_
|
||||
26
ntcore/src/main/native/cpp/Log.h
Normal file
26
ntcore/src/main/native/cpp/Log.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_LOG_H_
|
||||
#define NTCORE_LOG_H_
|
||||
|
||||
#include <support/Logger.h>
|
||||
|
||||
#define LOG(level, x) WPI_LOG(m_logger, level, x)
|
||||
|
||||
#undef ERROR
|
||||
#define ERROR(x) WPI_ERROR(m_logger, x)
|
||||
#define WARNING(x) WPI_WARNING(m_logger, x)
|
||||
#define INFO(x) WPI_INFO(m_logger, x)
|
||||
|
||||
#define DEBUG(x) WPI_DEBUG(m_logger, x)
|
||||
#define DEBUG1(x) WPI_DEBUG1(m_logger, x)
|
||||
#define DEBUG2(x) WPI_DEBUG2(m_logger, x)
|
||||
#define DEBUG3(x) WPI_DEBUG3(m_logger, x)
|
||||
#define DEBUG4(x) WPI_DEBUG4(m_logger, x)
|
||||
|
||||
#endif // NTCORE_LOG_H_
|
||||
77
ntcore/src/main/native/cpp/LoggerImpl.cpp
Normal file
77
ntcore/src/main/native/cpp/LoggerImpl.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "LoggerImpl.h"
|
||||
|
||||
#include <llvm/Path.h>
|
||||
#include <llvm/SmallString.h>
|
||||
#include <llvm/StringRef.h>
|
||||
#include <llvm/raw_ostream.h>
|
||||
|
||||
using namespace nt;
|
||||
|
||||
static void DefaultLogger(unsigned int level, const char* file,
|
||||
unsigned int line, const char* msg) {
|
||||
llvm::SmallString<128> buf;
|
||||
llvm::raw_svector_ostream oss(buf);
|
||||
if (level == 20) {
|
||||
oss << "NT: " << msg << '\n';
|
||||
llvm::errs() << oss.str();
|
||||
return;
|
||||
}
|
||||
|
||||
llvm::StringRef levelmsg;
|
||||
if (level >= 50)
|
||||
levelmsg = "CRITICAL: ";
|
||||
else if (level >= 40)
|
||||
levelmsg = "ERROR: ";
|
||||
else if (level >= 30)
|
||||
levelmsg = "WARNING: ";
|
||||
else
|
||||
return;
|
||||
oss << "NT: " << levelmsg << msg << " (" << file << ':' << line << ")\n";
|
||||
llvm::errs() << oss.str();
|
||||
}
|
||||
|
||||
LoggerImpl::LoggerImpl(int inst) : m_inst(inst) {}
|
||||
|
||||
void LoggerImpl::Start() { DoStart(m_inst); }
|
||||
|
||||
unsigned int LoggerImpl::Add(
|
||||
std::function<void(const LogMessage& msg)> callback, unsigned int min_level,
|
||||
unsigned int max_level) {
|
||||
return DoAdd(callback, min_level, max_level);
|
||||
}
|
||||
|
||||
unsigned int LoggerImpl::AddPolled(unsigned int poller_uid,
|
||||
unsigned int min_level,
|
||||
unsigned int max_level) {
|
||||
return DoAdd(poller_uid, min_level, max_level);
|
||||
}
|
||||
|
||||
unsigned int LoggerImpl::GetMinLevel() {
|
||||
auto thr = GetThread();
|
||||
if (!thr) return NT_LOG_INFO;
|
||||
unsigned int level = NT_LOG_INFO;
|
||||
for (size_t i = 0; i < thr->m_listeners.size(); ++i) {
|
||||
const auto& listener = thr->m_listeners[i];
|
||||
if (listener && listener.min_level < level) level = listener.min_level;
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
void LoggerImpl::Log(unsigned int level, const char* file, unsigned int line,
|
||||
const char* msg) {
|
||||
// this is safe because it's null terminated and always the end
|
||||
const char* filename = llvm::sys::path::filename(file).data();
|
||||
{
|
||||
auto thr = GetThread();
|
||||
if (!thr || thr->m_listeners.empty())
|
||||
DefaultLogger(level, filename, line, msg);
|
||||
}
|
||||
Send(UINT_MAX, 0, level, filename, line, msg);
|
||||
}
|
||||
83
ntcore/src/main/native/cpp/LoggerImpl.h
Normal file
83
ntcore/src/main/native/cpp/LoggerImpl.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_LOGGERIMPL_H_
|
||||
#define NTCORE_LOGGERIMPL_H_
|
||||
|
||||
#include "CallbackManager.h"
|
||||
#include "Handle.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
namespace impl {
|
||||
|
||||
struct LoggerListenerData
|
||||
: public ListenerData<std::function<void(const LogMessage& msg)>> {
|
||||
LoggerListenerData() = default;
|
||||
LoggerListenerData(std::function<void(const LogMessage& msg)> callback_,
|
||||
unsigned int min_level_, unsigned int max_level_)
|
||||
: ListenerData(callback_), min_level(min_level_), max_level(max_level_) {}
|
||||
LoggerListenerData(unsigned int poller_uid_, unsigned int min_level_,
|
||||
unsigned int max_level_)
|
||||
: ListenerData(poller_uid_),
|
||||
min_level(min_level_),
|
||||
max_level(max_level_) {}
|
||||
|
||||
unsigned int min_level;
|
||||
unsigned int max_level;
|
||||
};
|
||||
|
||||
class LoggerThread
|
||||
: public CallbackThread<LoggerThread, LogMessage, LoggerListenerData> {
|
||||
public:
|
||||
explicit LoggerThread(int inst) : m_inst(inst) {}
|
||||
|
||||
bool Matches(const LoggerListenerData& listener, const LogMessage& data) {
|
||||
return data.level >= listener.min_level && data.level <= listener.max_level;
|
||||
}
|
||||
|
||||
void SetListener(LogMessage* data, unsigned int listener_uid) {
|
||||
data->logger = Handle(m_inst, listener_uid, Handle::kLogger).handle();
|
||||
}
|
||||
|
||||
void DoCallback(std::function<void(const LogMessage& msg)> callback,
|
||||
const LogMessage& data) {
|
||||
callback(data);
|
||||
}
|
||||
|
||||
int m_inst;
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
class LoggerImpl : public CallbackManager<LoggerImpl, impl::LoggerThread> {
|
||||
friend class LoggerTest;
|
||||
friend class CallbackManager<LoggerImpl, impl::LoggerThread>;
|
||||
|
||||
public:
|
||||
explicit LoggerImpl(int inst);
|
||||
|
||||
void Start();
|
||||
|
||||
unsigned int Add(std::function<void(const LogMessage& msg)> callback,
|
||||
unsigned int min_level, unsigned int max_level);
|
||||
unsigned int AddPolled(unsigned int poller_uid, unsigned int min_level,
|
||||
unsigned int max_level);
|
||||
|
||||
unsigned int GetMinLevel();
|
||||
|
||||
void Log(unsigned int level, const char* file, unsigned int line,
|
||||
const char* msg);
|
||||
|
||||
private:
|
||||
int m_inst;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_LOGGERIMPL_H_
|
||||
301
ntcore/src/main/native/cpp/Message.cpp
Normal file
301
ntcore/src/main/native/cpp/Message.cpp
Normal file
@@ -0,0 +1,301 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "Message.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Log.h"
|
||||
#include "WireDecoder.h"
|
||||
#include "WireEncoder.h"
|
||||
|
||||
#define kClearAllMagic 0xD06CB27Aul
|
||||
|
||||
using namespace nt;
|
||||
|
||||
std::shared_ptr<Message> Message::Read(WireDecoder& decoder,
|
||||
GetEntryTypeFunc get_entry_type) {
|
||||
unsigned int msg_type = 0;
|
||||
if (!decoder.Read8(&msg_type)) return nullptr;
|
||||
auto msg =
|
||||
std::make_shared<Message>(static_cast<MsgType>(msg_type), private_init());
|
||||
switch (msg_type) {
|
||||
case kKeepAlive:
|
||||
break;
|
||||
case kClientHello: {
|
||||
unsigned int proto_rev;
|
||||
if (!decoder.Read16(&proto_rev)) return nullptr;
|
||||
msg->m_id = proto_rev;
|
||||
// This intentionally uses the provided proto_rev instead of
|
||||
// decoder.proto_rev().
|
||||
if (proto_rev >= 0x0300u) {
|
||||
if (!decoder.ReadString(&msg->m_str)) return nullptr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kProtoUnsup: {
|
||||
if (!decoder.Read16(&msg->m_id)) return nullptr; // proto rev
|
||||
break;
|
||||
}
|
||||
case kServerHelloDone:
|
||||
break;
|
||||
case kServerHello:
|
||||
if (decoder.proto_rev() < 0x0300u) {
|
||||
decoder.set_error("received SERVER_HELLO in protocol < 3.0");
|
||||
return nullptr;
|
||||
}
|
||||
if (!decoder.Read8(&msg->m_flags)) return nullptr;
|
||||
if (!decoder.ReadString(&msg->m_str)) return nullptr;
|
||||
break;
|
||||
case kClientHelloDone:
|
||||
if (decoder.proto_rev() < 0x0300u) {
|
||||
decoder.set_error("received CLIENT_HELLO_DONE in protocol < 3.0");
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
case kEntryAssign: {
|
||||
if (!decoder.ReadString(&msg->m_str)) return nullptr; // name
|
||||
NT_Type type;
|
||||
if (!decoder.ReadType(&type)) return nullptr; // entry type
|
||||
if (!decoder.Read16(&msg->m_id)) return nullptr; // id
|
||||
if (!decoder.Read16(&msg->m_seq_num_uid)) return nullptr; // seq num
|
||||
if (decoder.proto_rev() >= 0x0300u) {
|
||||
if (!decoder.Read8(&msg->m_flags)) return nullptr; // flags
|
||||
}
|
||||
msg->m_value = decoder.ReadValue(type);
|
||||
if (!msg->m_value) return nullptr;
|
||||
break;
|
||||
}
|
||||
case kEntryUpdate: {
|
||||
if (!decoder.Read16(&msg->m_id)) return nullptr; // id
|
||||
if (!decoder.Read16(&msg->m_seq_num_uid)) return nullptr; // seq num
|
||||
NT_Type type;
|
||||
if (decoder.proto_rev() >= 0x0300u) {
|
||||
if (!decoder.ReadType(&type)) return nullptr;
|
||||
} else {
|
||||
type = get_entry_type(msg->m_id);
|
||||
}
|
||||
WPI_DEBUG4(decoder.logger(), "update message data type: " << type);
|
||||
msg->m_value = decoder.ReadValue(type);
|
||||
if (!msg->m_value) return nullptr;
|
||||
break;
|
||||
}
|
||||
case kFlagsUpdate: {
|
||||
if (decoder.proto_rev() < 0x0300u) {
|
||||
decoder.set_error("received FLAGS_UPDATE in protocol < 3.0");
|
||||
return nullptr;
|
||||
}
|
||||
if (!decoder.Read16(&msg->m_id)) return nullptr;
|
||||
if (!decoder.Read8(&msg->m_flags)) return nullptr;
|
||||
break;
|
||||
}
|
||||
case kEntryDelete: {
|
||||
if (decoder.proto_rev() < 0x0300u) {
|
||||
decoder.set_error("received ENTRY_DELETE in protocol < 3.0");
|
||||
return nullptr;
|
||||
}
|
||||
if (!decoder.Read16(&msg->m_id)) return nullptr;
|
||||
break;
|
||||
}
|
||||
case kClearEntries: {
|
||||
if (decoder.proto_rev() < 0x0300u) {
|
||||
decoder.set_error("received CLEAR_ENTRIES in protocol < 3.0");
|
||||
return nullptr;
|
||||
}
|
||||
uint32_t magic;
|
||||
if (!decoder.Read32(&magic)) return nullptr;
|
||||
if (magic != kClearAllMagic) {
|
||||
decoder.set_error(
|
||||
"received incorrect CLEAR_ENTRIES magic value, ignoring");
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kExecuteRpc: {
|
||||
if (decoder.proto_rev() < 0x0300u) {
|
||||
decoder.set_error("received EXECUTE_RPC in protocol < 3.0");
|
||||
return nullptr;
|
||||
}
|
||||
if (!decoder.Read16(&msg->m_id)) return nullptr;
|
||||
if (!decoder.Read16(&msg->m_seq_num_uid)) return nullptr; // uid
|
||||
uint64_t size;
|
||||
if (!decoder.ReadUleb128(&size)) return nullptr;
|
||||
const char* params;
|
||||
if (!decoder.Read(¶ms, size)) return nullptr;
|
||||
msg->m_str = llvm::StringRef(params, size);
|
||||
break;
|
||||
}
|
||||
case kRpcResponse: {
|
||||
if (decoder.proto_rev() < 0x0300u) {
|
||||
decoder.set_error("received RPC_RESPONSE in protocol < 3.0");
|
||||
return nullptr;
|
||||
}
|
||||
if (!decoder.Read16(&msg->m_id)) return nullptr;
|
||||
if (!decoder.Read16(&msg->m_seq_num_uid)) return nullptr; // uid
|
||||
uint64_t size;
|
||||
if (!decoder.ReadUleb128(&size)) return nullptr;
|
||||
const char* results;
|
||||
if (!decoder.Read(&results, size)) return nullptr;
|
||||
msg->m_str = llvm::StringRef(results, size);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
decoder.set_error("unrecognized message type");
|
||||
WPI_INFO(decoder.logger(), "unrecognized message type: " << msg_type);
|
||||
return nullptr;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::shared_ptr<Message> Message::ClientHello(llvm::StringRef self_id) {
|
||||
auto msg = std::make_shared<Message>(kClientHello, private_init());
|
||||
msg->m_str = self_id;
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::shared_ptr<Message> Message::ServerHello(unsigned int flags,
|
||||
llvm::StringRef self_id) {
|
||||
auto msg = std::make_shared<Message>(kServerHello, private_init());
|
||||
msg->m_str = self_id;
|
||||
msg->m_flags = flags;
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::shared_ptr<Message> Message::EntryAssign(llvm::StringRef name,
|
||||
unsigned int id,
|
||||
unsigned int seq_num,
|
||||
std::shared_ptr<Value> value,
|
||||
unsigned int flags) {
|
||||
auto msg = std::make_shared<Message>(kEntryAssign, private_init());
|
||||
msg->m_str = name;
|
||||
msg->m_value = value;
|
||||
msg->m_id = id;
|
||||
msg->m_flags = flags;
|
||||
msg->m_seq_num_uid = seq_num;
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::shared_ptr<Message> Message::EntryUpdate(unsigned int id,
|
||||
unsigned int seq_num,
|
||||
std::shared_ptr<Value> value) {
|
||||
auto msg = std::make_shared<Message>(kEntryUpdate, private_init());
|
||||
msg->m_value = value;
|
||||
msg->m_id = id;
|
||||
msg->m_seq_num_uid = seq_num;
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::shared_ptr<Message> Message::FlagsUpdate(unsigned int id,
|
||||
unsigned int flags) {
|
||||
auto msg = std::make_shared<Message>(kFlagsUpdate, private_init());
|
||||
msg->m_id = id;
|
||||
msg->m_flags = flags;
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::shared_ptr<Message> Message::EntryDelete(unsigned int id) {
|
||||
auto msg = std::make_shared<Message>(kEntryDelete, private_init());
|
||||
msg->m_id = id;
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::shared_ptr<Message> Message::ExecuteRpc(unsigned int id, unsigned int uid,
|
||||
llvm::StringRef params) {
|
||||
auto msg = std::make_shared<Message>(kExecuteRpc, private_init());
|
||||
msg->m_str = params;
|
||||
msg->m_id = id;
|
||||
msg->m_seq_num_uid = uid;
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::shared_ptr<Message> Message::RpcResponse(unsigned int id, unsigned int uid,
|
||||
llvm::StringRef result) {
|
||||
auto msg = std::make_shared<Message>(kRpcResponse, private_init());
|
||||
msg->m_str = result;
|
||||
msg->m_id = id;
|
||||
msg->m_seq_num_uid = uid;
|
||||
return msg;
|
||||
}
|
||||
|
||||
void Message::Write(WireEncoder& encoder) const {
|
||||
switch (m_type) {
|
||||
case kKeepAlive:
|
||||
encoder.Write8(kKeepAlive);
|
||||
break;
|
||||
case kClientHello:
|
||||
encoder.Write8(kClientHello);
|
||||
encoder.Write16(encoder.proto_rev());
|
||||
if (encoder.proto_rev() < 0x0300u) return;
|
||||
encoder.WriteString(m_str);
|
||||
break;
|
||||
case kProtoUnsup:
|
||||
encoder.Write8(kProtoUnsup);
|
||||
encoder.Write16(encoder.proto_rev());
|
||||
break;
|
||||
case kServerHelloDone:
|
||||
encoder.Write8(kServerHelloDone);
|
||||
break;
|
||||
case kServerHello:
|
||||
if (encoder.proto_rev() < 0x0300u) return; // new message in version 3.0
|
||||
encoder.Write8(kServerHello);
|
||||
encoder.Write8(m_flags);
|
||||
encoder.WriteString(m_str);
|
||||
break;
|
||||
case kClientHelloDone:
|
||||
if (encoder.proto_rev() < 0x0300u) return; // new message in version 3.0
|
||||
encoder.Write8(kClientHelloDone);
|
||||
break;
|
||||
case kEntryAssign:
|
||||
encoder.Write8(kEntryAssign);
|
||||
encoder.WriteString(m_str);
|
||||
encoder.WriteType(m_value->type());
|
||||
encoder.Write16(m_id);
|
||||
encoder.Write16(m_seq_num_uid);
|
||||
if (encoder.proto_rev() >= 0x0300u) encoder.Write8(m_flags);
|
||||
encoder.WriteValue(*m_value);
|
||||
break;
|
||||
case kEntryUpdate:
|
||||
encoder.Write8(kEntryUpdate);
|
||||
encoder.Write16(m_id);
|
||||
encoder.Write16(m_seq_num_uid);
|
||||
if (encoder.proto_rev() >= 0x0300u) encoder.WriteType(m_value->type());
|
||||
encoder.WriteValue(*m_value);
|
||||
break;
|
||||
case kFlagsUpdate:
|
||||
if (encoder.proto_rev() < 0x0300u) return; // new message in version 3.0
|
||||
encoder.Write8(kFlagsUpdate);
|
||||
encoder.Write16(m_id);
|
||||
encoder.Write8(m_flags);
|
||||
break;
|
||||
case kEntryDelete:
|
||||
if (encoder.proto_rev() < 0x0300u) return; // new message in version 3.0
|
||||
encoder.Write8(kEntryDelete);
|
||||
encoder.Write16(m_id);
|
||||
break;
|
||||
case kClearEntries:
|
||||
if (encoder.proto_rev() < 0x0300u) return; // new message in version 3.0
|
||||
encoder.Write8(kClearEntries);
|
||||
encoder.Write32(kClearAllMagic);
|
||||
break;
|
||||
case kExecuteRpc:
|
||||
if (encoder.proto_rev() < 0x0300u) return; // new message in version 3.0
|
||||
encoder.Write8(kExecuteRpc);
|
||||
encoder.Write16(m_id);
|
||||
encoder.Write16(m_seq_num_uid);
|
||||
encoder.WriteString(m_str);
|
||||
break;
|
||||
case kRpcResponse:
|
||||
if (encoder.proto_rev() < 0x0300u) return; // new message in version 3.0
|
||||
encoder.Write8(kRpcResponse);
|
||||
encoder.Write16(m_id);
|
||||
encoder.Write16(m_seq_num_uid);
|
||||
encoder.WriteString(m_str);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
117
ntcore/src/main/native/cpp/Message.h
Normal file
117
ntcore/src/main/native/cpp/Message.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_MESSAGE_H_
|
||||
#define NTCORE_MESSAGE_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class WireDecoder;
|
||||
class WireEncoder;
|
||||
|
||||
class Message {
|
||||
struct private_init {};
|
||||
|
||||
public:
|
||||
enum MsgType {
|
||||
kUnknown = -1,
|
||||
kKeepAlive = 0x00,
|
||||
kClientHello = 0x01,
|
||||
kProtoUnsup = 0x02,
|
||||
kServerHelloDone = 0x03,
|
||||
kServerHello = 0x04,
|
||||
kClientHelloDone = 0x05,
|
||||
kEntryAssign = 0x10,
|
||||
kEntryUpdate = 0x11,
|
||||
kFlagsUpdate = 0x12,
|
||||
kEntryDelete = 0x13,
|
||||
kClearEntries = 0x14,
|
||||
kExecuteRpc = 0x20,
|
||||
kRpcResponse = 0x21
|
||||
};
|
||||
typedef std::function<NT_Type(unsigned int id)> GetEntryTypeFunc;
|
||||
|
||||
Message() : m_type(kUnknown), m_id(0), m_flags(0), m_seq_num_uid(0) {}
|
||||
Message(MsgType type, const private_init&)
|
||||
: m_type(type), m_id(0), m_flags(0), m_seq_num_uid(0) {}
|
||||
|
||||
MsgType type() const { return m_type; }
|
||||
bool Is(MsgType type) const { return type == m_type; }
|
||||
|
||||
// Message data accessors. Callers are responsible for knowing what data is
|
||||
// actually provided for a particular message.
|
||||
llvm::StringRef str() const { return m_str; }
|
||||
std::shared_ptr<Value> value() const { return m_value; }
|
||||
unsigned int id() const { return m_id; }
|
||||
unsigned int flags() const { return m_flags; }
|
||||
unsigned int seq_num_uid() const { return m_seq_num_uid; }
|
||||
|
||||
// Read and write from wire representation
|
||||
void Write(WireEncoder& encoder) const;
|
||||
static std::shared_ptr<Message> Read(WireDecoder& decoder,
|
||||
GetEntryTypeFunc get_entry_type);
|
||||
|
||||
// Create messages without data
|
||||
static std::shared_ptr<Message> KeepAlive() {
|
||||
return std::make_shared<Message>(kKeepAlive, private_init());
|
||||
}
|
||||
static std::shared_ptr<Message> ProtoUnsup() {
|
||||
return std::make_shared<Message>(kProtoUnsup, private_init());
|
||||
}
|
||||
static std::shared_ptr<Message> ServerHelloDone() {
|
||||
return std::make_shared<Message>(kServerHelloDone, private_init());
|
||||
}
|
||||
static std::shared_ptr<Message> ClientHelloDone() {
|
||||
return std::make_shared<Message>(kClientHelloDone, private_init());
|
||||
}
|
||||
static std::shared_ptr<Message> ClearEntries() {
|
||||
return std::make_shared<Message>(kClearEntries, private_init());
|
||||
}
|
||||
|
||||
// Create messages with data
|
||||
static std::shared_ptr<Message> ClientHello(llvm::StringRef self_id);
|
||||
static std::shared_ptr<Message> ServerHello(unsigned int flags,
|
||||
llvm::StringRef self_id);
|
||||
static std::shared_ptr<Message> EntryAssign(llvm::StringRef name,
|
||||
unsigned int id,
|
||||
unsigned int seq_num,
|
||||
std::shared_ptr<Value> value,
|
||||
unsigned int flags);
|
||||
static std::shared_ptr<Message> EntryUpdate(unsigned int id,
|
||||
unsigned int seq_num,
|
||||
std::shared_ptr<Value> value);
|
||||
static std::shared_ptr<Message> FlagsUpdate(unsigned int id,
|
||||
unsigned int flags);
|
||||
static std::shared_ptr<Message> EntryDelete(unsigned int id);
|
||||
static std::shared_ptr<Message> ExecuteRpc(unsigned int id, unsigned int uid,
|
||||
llvm::StringRef params);
|
||||
static std::shared_ptr<Message> RpcResponse(unsigned int id, unsigned int uid,
|
||||
llvm::StringRef result);
|
||||
|
||||
Message(const Message&) = delete;
|
||||
Message& operator=(const Message&) = delete;
|
||||
|
||||
private:
|
||||
MsgType m_type;
|
||||
|
||||
// Message data. Use varies by message type.
|
||||
std::string m_str;
|
||||
std::shared_ptr<Value> m_value;
|
||||
unsigned int m_id; // also used for proto_rev
|
||||
unsigned int m_flags;
|
||||
unsigned int m_seq_num_uid;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_MESSAGE_H_
|
||||
333
ntcore/src/main/native/cpp/NetworkConnection.cpp
Normal file
333
ntcore/src/main/native/cpp/NetworkConnection.cpp
Normal file
@@ -0,0 +1,333 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "NetworkConnection.h"
|
||||
|
||||
#include <support/raw_socket_istream.h>
|
||||
#include <support/timestamp.h>
|
||||
|
||||
#include "IConnectionNotifier.h"
|
||||
#include "Log.h"
|
||||
#include "WireDecoder.h"
|
||||
#include "WireEncoder.h"
|
||||
#include "tcpsockets/NetworkStream.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
NetworkConnection::NetworkConnection(unsigned int uid,
|
||||
std::unique_ptr<wpi::NetworkStream> stream,
|
||||
IConnectionNotifier& notifier,
|
||||
wpi::Logger& logger,
|
||||
HandshakeFunc handshake,
|
||||
Message::GetEntryTypeFunc get_entry_type)
|
||||
: m_uid(uid),
|
||||
m_stream(std::move(stream)),
|
||||
m_notifier(notifier),
|
||||
m_logger(logger),
|
||||
m_handshake(handshake),
|
||||
m_get_entry_type(get_entry_type),
|
||||
m_state(kCreated) {
|
||||
m_active = false;
|
||||
m_proto_rev = 0x0300;
|
||||
m_last_update = 0;
|
||||
|
||||
// turn off Nagle algorithm; we bundle packets for transmission
|
||||
m_stream->setNoDelay();
|
||||
}
|
||||
|
||||
NetworkConnection::~NetworkConnection() { Stop(); }
|
||||
|
||||
void NetworkConnection::Start() {
|
||||
if (m_active) return;
|
||||
m_active = true;
|
||||
set_state(kInit);
|
||||
// clear queue
|
||||
while (!m_outgoing.empty()) m_outgoing.pop();
|
||||
// reset shutdown flags
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(m_shutdown_mutex);
|
||||
m_read_shutdown = false;
|
||||
m_write_shutdown = false;
|
||||
}
|
||||
// start threads
|
||||
m_write_thread = std::thread(&NetworkConnection::WriteThreadMain, this);
|
||||
m_read_thread = std::thread(&NetworkConnection::ReadThreadMain, this);
|
||||
}
|
||||
|
||||
void NetworkConnection::Stop() {
|
||||
DEBUG2("NetworkConnection stopping (" << this << ")");
|
||||
set_state(kDead);
|
||||
m_active = false;
|
||||
// closing the stream so the read thread terminates
|
||||
if (m_stream) m_stream->close();
|
||||
// send an empty outgoing message set so the write thread terminates
|
||||
m_outgoing.push(Outgoing());
|
||||
// wait for threads to terminate, with timeout
|
||||
if (m_write_thread.joinable()) {
|
||||
std::unique_lock<wpi::mutex> lock(m_shutdown_mutex);
|
||||
auto timeout_time =
|
||||
std::chrono::steady_clock::now() + std::chrono::milliseconds(200);
|
||||
if (m_write_shutdown_cv.wait_until(lock, timeout_time,
|
||||
[&] { return m_write_shutdown; }))
|
||||
m_write_thread.join();
|
||||
else
|
||||
m_write_thread.detach(); // timed out, detach it
|
||||
}
|
||||
if (m_read_thread.joinable()) {
|
||||
std::unique_lock<wpi::mutex> lock(m_shutdown_mutex);
|
||||
auto timeout_time =
|
||||
std::chrono::steady_clock::now() + std::chrono::milliseconds(200);
|
||||
if (m_read_shutdown_cv.wait_until(lock, timeout_time,
|
||||
[&] { return m_read_shutdown; }))
|
||||
m_read_thread.join();
|
||||
else
|
||||
m_read_thread.detach(); // timed out, detach it
|
||||
}
|
||||
// clear queue
|
||||
while (!m_outgoing.empty()) m_outgoing.pop();
|
||||
}
|
||||
|
||||
ConnectionInfo NetworkConnection::info() const {
|
||||
return ConnectionInfo{remote_id(), m_stream->getPeerIP(),
|
||||
static_cast<unsigned int>(m_stream->getPeerPort()),
|
||||
m_last_update, m_proto_rev};
|
||||
}
|
||||
|
||||
unsigned int NetworkConnection::proto_rev() const { return m_proto_rev; }
|
||||
|
||||
void NetworkConnection::set_proto_rev(unsigned int proto_rev) {
|
||||
m_proto_rev = proto_rev;
|
||||
}
|
||||
|
||||
NetworkConnection::State NetworkConnection::state() const {
|
||||
std::lock_guard<wpi::mutex> lock(m_state_mutex);
|
||||
return m_state;
|
||||
}
|
||||
|
||||
void NetworkConnection::set_state(State state) {
|
||||
std::lock_guard<wpi::mutex> lock(m_state_mutex);
|
||||
// Don't update state any more once we've died
|
||||
if (m_state == kDead) return;
|
||||
// One-shot notify state changes
|
||||
if (m_state != kActive && state == kActive)
|
||||
m_notifier.NotifyConnection(true, info());
|
||||
if (m_state != kDead && state == kDead)
|
||||
m_notifier.NotifyConnection(false, info());
|
||||
m_state = state;
|
||||
}
|
||||
|
||||
std::string NetworkConnection::remote_id() const {
|
||||
std::lock_guard<wpi::mutex> lock(m_remote_id_mutex);
|
||||
return m_remote_id;
|
||||
}
|
||||
|
||||
void NetworkConnection::set_remote_id(StringRef remote_id) {
|
||||
std::lock_guard<wpi::mutex> lock(m_remote_id_mutex);
|
||||
m_remote_id = remote_id;
|
||||
}
|
||||
|
||||
void NetworkConnection::ReadThreadMain() {
|
||||
wpi::raw_socket_istream is(*m_stream);
|
||||
WireDecoder decoder(is, m_proto_rev, m_logger);
|
||||
|
||||
set_state(kHandshake);
|
||||
if (!m_handshake(*this,
|
||||
[&] {
|
||||
decoder.set_proto_rev(m_proto_rev);
|
||||
auto msg = Message::Read(decoder, m_get_entry_type);
|
||||
if (!msg && decoder.error())
|
||||
DEBUG("error reading in handshake: " << decoder.error());
|
||||
return msg;
|
||||
},
|
||||
[&](llvm::ArrayRef<std::shared_ptr<Message>> msgs) {
|
||||
m_outgoing.emplace(msgs);
|
||||
})) {
|
||||
set_state(kDead);
|
||||
m_active = false;
|
||||
goto done;
|
||||
}
|
||||
|
||||
set_state(kActive);
|
||||
while (m_active) {
|
||||
if (!m_stream) break;
|
||||
decoder.set_proto_rev(m_proto_rev);
|
||||
decoder.Reset();
|
||||
auto msg = Message::Read(decoder, m_get_entry_type);
|
||||
if (!msg) {
|
||||
if (decoder.error()) INFO("read error: " << decoder.error());
|
||||
// terminate connection on bad message
|
||||
if (m_stream) m_stream->close();
|
||||
break;
|
||||
}
|
||||
DEBUG3("received type=" << msg->type() << " with str=" << msg->str()
|
||||
<< " id=" << msg->id()
|
||||
<< " seq_num=" << msg->seq_num_uid());
|
||||
m_last_update = Now();
|
||||
m_process_incoming(std::move(msg), this);
|
||||
}
|
||||
DEBUG2("read thread died (" << this << ")");
|
||||
set_state(kDead);
|
||||
m_active = false;
|
||||
m_outgoing.push(Outgoing()); // also kill write thread
|
||||
|
||||
done:
|
||||
// use condition variable to signal thread shutdown
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(m_shutdown_mutex);
|
||||
m_read_shutdown = true;
|
||||
m_read_shutdown_cv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkConnection::WriteThreadMain() {
|
||||
WireEncoder encoder(m_proto_rev);
|
||||
|
||||
while (m_active) {
|
||||
auto msgs = m_outgoing.pop();
|
||||
DEBUG4("write thread woke up");
|
||||
if (msgs.empty()) continue;
|
||||
encoder.set_proto_rev(m_proto_rev);
|
||||
encoder.Reset();
|
||||
DEBUG3("sending " << msgs.size() << " messages");
|
||||
for (auto& msg : msgs) {
|
||||
if (msg) {
|
||||
DEBUG3("sending type=" << msg->type() << " with str=" << msg->str()
|
||||
<< " id=" << msg->id()
|
||||
<< " seq_num=" << msg->seq_num_uid());
|
||||
msg->Write(encoder);
|
||||
}
|
||||
}
|
||||
wpi::NetworkStream::Error err;
|
||||
if (!m_stream) break;
|
||||
if (encoder.size() == 0) continue;
|
||||
if (m_stream->send(encoder.data(), encoder.size(), &err) == 0) break;
|
||||
DEBUG4("sent " << encoder.size() << " bytes");
|
||||
}
|
||||
DEBUG2("write thread died (" << this << ")");
|
||||
set_state(kDead);
|
||||
m_active = false;
|
||||
if (m_stream) m_stream->close(); // also kill read thread
|
||||
|
||||
// use condition variable to signal thread shutdown
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(m_shutdown_mutex);
|
||||
m_write_shutdown = true;
|
||||
m_write_shutdown_cv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkConnection::QueueOutgoing(std::shared_ptr<Message> msg) {
|
||||
std::lock_guard<wpi::mutex> lock(m_pending_mutex);
|
||||
|
||||
// Merge with previous. One case we don't combine: delete/assign loop.
|
||||
switch (msg->type()) {
|
||||
case Message::kEntryAssign:
|
||||
case Message::kEntryUpdate: {
|
||||
// don't do this for unassigned id's
|
||||
unsigned int id = msg->id();
|
||||
if (id == 0xffff) {
|
||||
m_pending_outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
if (id < m_pending_update.size() && m_pending_update[id].first != 0) {
|
||||
// overwrite the previous one for this id
|
||||
auto& oldmsg = m_pending_outgoing[m_pending_update[id].first - 1];
|
||||
if (oldmsg && oldmsg->Is(Message::kEntryAssign) &&
|
||||
msg->Is(Message::kEntryUpdate)) {
|
||||
// need to update assignment with new seq_num and value
|
||||
oldmsg = Message::EntryAssign(oldmsg->str(), id, msg->seq_num_uid(),
|
||||
msg->value(), oldmsg->flags());
|
||||
} else {
|
||||
oldmsg = msg; // easy update
|
||||
}
|
||||
} else {
|
||||
// new, but remember it
|
||||
size_t pos = m_pending_outgoing.size();
|
||||
m_pending_outgoing.push_back(msg);
|
||||
if (id >= m_pending_update.size()) m_pending_update.resize(id + 1);
|
||||
m_pending_update[id].first = pos + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Message::kEntryDelete: {
|
||||
// don't do this for unassigned id's
|
||||
unsigned int id = msg->id();
|
||||
if (id == 0xffff) {
|
||||
m_pending_outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
// clear previous updates
|
||||
if (id < m_pending_update.size()) {
|
||||
if (m_pending_update[id].first != 0) {
|
||||
m_pending_outgoing[m_pending_update[id].first - 1].reset();
|
||||
m_pending_update[id].first = 0;
|
||||
}
|
||||
if (m_pending_update[id].second != 0) {
|
||||
m_pending_outgoing[m_pending_update[id].second - 1].reset();
|
||||
m_pending_update[id].second = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// add deletion
|
||||
m_pending_outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
case Message::kFlagsUpdate: {
|
||||
// don't do this for unassigned id's
|
||||
unsigned int id = msg->id();
|
||||
if (id == 0xffff) {
|
||||
m_pending_outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
if (id < m_pending_update.size() && m_pending_update[id].second != 0) {
|
||||
// overwrite the previous one for this id
|
||||
m_pending_outgoing[m_pending_update[id].second - 1] = msg;
|
||||
} else {
|
||||
// new, but remember it
|
||||
size_t pos = m_pending_outgoing.size();
|
||||
m_pending_outgoing.push_back(msg);
|
||||
if (id >= m_pending_update.size()) m_pending_update.resize(id + 1);
|
||||
m_pending_update[id].second = pos + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Message::kClearEntries: {
|
||||
// knock out all previous assigns/updates!
|
||||
for (auto& i : m_pending_outgoing) {
|
||||
if (!i) continue;
|
||||
auto t = i->type();
|
||||
if (t == Message::kEntryAssign || t == Message::kEntryUpdate ||
|
||||
t == Message::kFlagsUpdate || t == Message::kEntryDelete ||
|
||||
t == Message::kClearEntries)
|
||||
i.reset();
|
||||
}
|
||||
m_pending_update.resize(0);
|
||||
m_pending_outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
m_pending_outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkConnection::PostOutgoing(bool keep_alive) {
|
||||
std::lock_guard<wpi::mutex> lock(m_pending_mutex);
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (m_pending_outgoing.empty()) {
|
||||
if (!keep_alive) return;
|
||||
// send keep-alives once a second (if no other messages have been sent)
|
||||
if ((now - m_last_post) < std::chrono::seconds(1)) return;
|
||||
m_outgoing.emplace(Outgoing{Message::KeepAlive()});
|
||||
} else {
|
||||
m_outgoing.emplace(std::move(m_pending_outgoing));
|
||||
m_pending_outgoing.resize(0);
|
||||
m_pending_update.resize(0);
|
||||
}
|
||||
m_last_post = now;
|
||||
}
|
||||
127
ntcore/src/main/native/cpp/NetworkConnection.h
Normal file
127
ntcore/src/main/native/cpp/NetworkConnection.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_NETWORKCONNECTION_H_
|
||||
#define NTCORE_NETWORKCONNECTION_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <support/ConcurrentQueue.h>
|
||||
#include <support/condition_variable.h>
|
||||
#include <support/mutex.h>
|
||||
|
||||
#include "INetworkConnection.h"
|
||||
#include "Message.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace wpi {
|
||||
class Logger;
|
||||
class NetworkStream;
|
||||
} // namespace wpi
|
||||
|
||||
namespace nt {
|
||||
|
||||
class IConnectionNotifier;
|
||||
|
||||
class NetworkConnection : public INetworkConnection {
|
||||
public:
|
||||
typedef std::function<bool(
|
||||
NetworkConnection& conn,
|
||||
std::function<std::shared_ptr<Message>()> get_msg,
|
||||
std::function<void(llvm::ArrayRef<std::shared_ptr<Message>>)> send_msgs)>
|
||||
HandshakeFunc;
|
||||
typedef std::function<void(std::shared_ptr<Message> msg,
|
||||
NetworkConnection* conn)>
|
||||
ProcessIncomingFunc;
|
||||
typedef std::vector<std::shared_ptr<Message>> Outgoing;
|
||||
typedef wpi::ConcurrentQueue<Outgoing> OutgoingQueue;
|
||||
|
||||
NetworkConnection(unsigned int uid,
|
||||
std::unique_ptr<wpi::NetworkStream> stream,
|
||||
IConnectionNotifier& notifier, wpi::Logger& logger,
|
||||
HandshakeFunc handshake,
|
||||
Message::GetEntryTypeFunc get_entry_type);
|
||||
~NetworkConnection();
|
||||
|
||||
// Set the input processor function. This must be called before Start().
|
||||
void set_process_incoming(ProcessIncomingFunc func) {
|
||||
m_process_incoming = func;
|
||||
}
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
ConnectionInfo info() const override;
|
||||
|
||||
bool active() const { return m_active; }
|
||||
wpi::NetworkStream& stream() { return *m_stream; }
|
||||
|
||||
void QueueOutgoing(std::shared_ptr<Message> msg) override;
|
||||
void PostOutgoing(bool keep_alive) override;
|
||||
|
||||
unsigned int uid() const { return m_uid; }
|
||||
|
||||
unsigned int proto_rev() const override;
|
||||
void set_proto_rev(unsigned int proto_rev) override;
|
||||
|
||||
State state() const override;
|
||||
void set_state(State state) override;
|
||||
|
||||
std::string remote_id() const;
|
||||
void set_remote_id(StringRef remote_id);
|
||||
|
||||
uint64_t last_update() const { return m_last_update; }
|
||||
|
||||
NetworkConnection(const NetworkConnection&) = delete;
|
||||
NetworkConnection& operator=(const NetworkConnection&) = delete;
|
||||
|
||||
private:
|
||||
void ReadThreadMain();
|
||||
void WriteThreadMain();
|
||||
|
||||
unsigned int m_uid;
|
||||
std::unique_ptr<wpi::NetworkStream> m_stream;
|
||||
IConnectionNotifier& m_notifier;
|
||||
wpi::Logger& m_logger;
|
||||
OutgoingQueue m_outgoing;
|
||||
HandshakeFunc m_handshake;
|
||||
Message::GetEntryTypeFunc m_get_entry_type;
|
||||
ProcessIncomingFunc m_process_incoming;
|
||||
std::thread m_read_thread;
|
||||
std::thread m_write_thread;
|
||||
std::atomic_bool m_active;
|
||||
std::atomic_uint m_proto_rev;
|
||||
mutable wpi::mutex m_state_mutex;
|
||||
State m_state;
|
||||
mutable wpi::mutex m_remote_id_mutex;
|
||||
std::string m_remote_id;
|
||||
std::atomic_ullong m_last_update;
|
||||
std::chrono::steady_clock::time_point m_last_post;
|
||||
|
||||
wpi::mutex m_pending_mutex;
|
||||
Outgoing m_pending_outgoing;
|
||||
std::vector<std::pair<size_t, size_t>> m_pending_update;
|
||||
|
||||
// Condition variables for shutdown
|
||||
wpi::mutex m_shutdown_mutex;
|
||||
wpi::condition_variable m_read_shutdown_cv;
|
||||
wpi::condition_variable m_write_shutdown_cv;
|
||||
bool m_read_shutdown = false;
|
||||
bool m_write_shutdown = false;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_NETWORKCONNECTION_H_
|
||||
48
ntcore/src/main/native/cpp/RpcServer.cpp
Normal file
48
ntcore/src/main/native/cpp/RpcServer.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "RpcServer.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
RpcServer::RpcServer(int inst, wpi::Logger& logger)
|
||||
: m_inst(inst), m_logger(logger) {}
|
||||
|
||||
void RpcServer::Start() { DoStart(m_inst, m_logger); }
|
||||
|
||||
unsigned int RpcServer::Add(
|
||||
std::function<void(const RpcAnswer& answer)> callback) {
|
||||
return DoAdd(callback);
|
||||
}
|
||||
|
||||
unsigned int RpcServer::AddPolled(unsigned int poller_uid) {
|
||||
return DoAdd(poller_uid);
|
||||
}
|
||||
|
||||
void RpcServer::RemoveRpc(unsigned int rpc_uid) { Remove(rpc_uid); }
|
||||
|
||||
void RpcServer::ProcessRpc(unsigned int local_id, unsigned int call_uid,
|
||||
StringRef name, StringRef params,
|
||||
const ConnectionInfo& conn,
|
||||
SendResponseFunc send_response,
|
||||
unsigned int rpc_uid) {
|
||||
Send(rpc_uid, Handle(m_inst, local_id, Handle::kEntry).handle(),
|
||||
Handle(m_inst, call_uid, Handle::kRpcCall).handle(), name, params, conn,
|
||||
send_response);
|
||||
}
|
||||
|
||||
void RpcServer::PostRpcResponse(unsigned int local_id, unsigned int call_uid,
|
||||
llvm::StringRef result) {
|
||||
auto thr = GetThread();
|
||||
auto i = thr->m_response_map.find(impl::RpcIdPair{local_id, call_uid});
|
||||
if (i == thr->m_response_map.end()) {
|
||||
WARNING("posting RPC response to nonexistent call (or duplicate response)");
|
||||
return;
|
||||
}
|
||||
(i->getSecond())(result);
|
||||
thr->m_response_map.erase(i);
|
||||
}
|
||||
112
ntcore/src/main/native/cpp/RpcServer.h
Normal file
112
ntcore/src/main/native/cpp/RpcServer.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_RPCSERVER_H_
|
||||
#define NTCORE_RPCSERVER_H_
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <llvm/DenseMap.h>
|
||||
#include <support/mutex.h>
|
||||
|
||||
#include "CallbackManager.h"
|
||||
#include "Handle.h"
|
||||
#include "IRpcServer.h"
|
||||
#include "Log.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
namespace impl {
|
||||
|
||||
typedef std::pair<unsigned int, unsigned int> RpcIdPair;
|
||||
|
||||
struct RpcNotifierData : public RpcAnswer {
|
||||
RpcNotifierData(NT_Entry entry_, NT_RpcCall call_, StringRef name_,
|
||||
StringRef params_, const ConnectionInfo& conn_,
|
||||
IRpcServer::SendResponseFunc send_response_)
|
||||
: RpcAnswer{entry_, call_, name_, params_, conn_},
|
||||
send_response{send_response_} {}
|
||||
|
||||
IRpcServer::SendResponseFunc send_response;
|
||||
};
|
||||
|
||||
using RpcListenerData =
|
||||
ListenerData<std::function<void(const RpcAnswer& answer)>>;
|
||||
|
||||
class RpcServerThread
|
||||
: public CallbackThread<RpcServerThread, RpcAnswer, RpcListenerData,
|
||||
RpcNotifierData> {
|
||||
public:
|
||||
RpcServerThread(int inst, wpi::Logger& logger)
|
||||
: m_inst(inst), m_logger(logger) {}
|
||||
|
||||
bool Matches(const RpcListenerData& listener, const RpcNotifierData& data) {
|
||||
return !data.name.empty() && data.send_response;
|
||||
}
|
||||
|
||||
void SetListener(RpcNotifierData* data, unsigned int listener_uid) {
|
||||
unsigned int local_id = Handle{data->entry}.GetIndex();
|
||||
unsigned int call_uid = Handle{data->call}.GetIndex();
|
||||
RpcIdPair lookup_uid{local_id, call_uid};
|
||||
m_response_map.insert(std::make_pair(lookup_uid, data->send_response));
|
||||
}
|
||||
|
||||
void DoCallback(std::function<void(const RpcAnswer& call)> callback,
|
||||
const RpcNotifierData& data) {
|
||||
DEBUG4("rpc calling " << data.name);
|
||||
unsigned int local_id = Handle{data.entry}.GetIndex();
|
||||
unsigned int call_uid = Handle{data.call}.GetIndex();
|
||||
RpcIdPair lookup_uid{local_id, call_uid};
|
||||
callback(data);
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto i = m_response_map.find(lookup_uid);
|
||||
if (i != m_response_map.end()) {
|
||||
// post an empty response and erase it
|
||||
(i->getSecond())("");
|
||||
m_response_map.erase(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int m_inst;
|
||||
wpi::Logger& m_logger;
|
||||
llvm::DenseMap<RpcIdPair, IRpcServer::SendResponseFunc> m_response_map;
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
class RpcServer : public IRpcServer,
|
||||
public CallbackManager<RpcServer, impl::RpcServerThread> {
|
||||
friend class RpcServerTest;
|
||||
friend class CallbackManager<RpcServer, impl::RpcServerThread>;
|
||||
|
||||
public:
|
||||
RpcServer(int inst, wpi::Logger& logger);
|
||||
|
||||
void Start();
|
||||
|
||||
unsigned int Add(std::function<void(const RpcAnswer& answer)> callback);
|
||||
unsigned int AddPolled(unsigned int poller_uid);
|
||||
void RemoveRpc(unsigned int rpc_uid) override;
|
||||
|
||||
void ProcessRpc(unsigned int local_id, unsigned int call_uid, StringRef name,
|
||||
StringRef params, const ConnectionInfo& conn,
|
||||
SendResponseFunc send_response,
|
||||
unsigned int rpc_uid) override;
|
||||
|
||||
void PostRpcResponse(unsigned int local_id, unsigned int call_uid,
|
||||
llvm::StringRef result);
|
||||
|
||||
private:
|
||||
int m_inst;
|
||||
wpi::Logger& m_logger;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_RPCSERVER_H_
|
||||
30
ntcore/src/main/native/cpp/SequenceNumber.cpp
Normal file
30
ntcore/src/main/native/cpp/SequenceNumber.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "SequenceNumber.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
bool operator<(const SequenceNumber& lhs, const SequenceNumber& rhs) {
|
||||
if (lhs.m_value < rhs.m_value)
|
||||
return (rhs.m_value - lhs.m_value) < (1u << 15);
|
||||
else if (lhs.m_value > rhs.m_value)
|
||||
return (lhs.m_value - rhs.m_value) > (1u << 15);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator>(const SequenceNumber& lhs, const SequenceNumber& rhs) {
|
||||
if (lhs.m_value < rhs.m_value)
|
||||
return (rhs.m_value - lhs.m_value) > (1u << 15);
|
||||
else if (lhs.m_value > rhs.m_value)
|
||||
return (lhs.m_value - rhs.m_value) < (1u << 15);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
63
ntcore/src/main/native/cpp/SequenceNumber.h
Normal file
63
ntcore/src/main/native/cpp/SequenceNumber.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_SEQUENCENUMBER_H_
|
||||
#define NTCORE_SEQUENCENUMBER_H_
|
||||
|
||||
namespace nt {
|
||||
|
||||
/* A sequence number per RFC 1982 */
|
||||
class SequenceNumber {
|
||||
public:
|
||||
SequenceNumber() : m_value(0) {}
|
||||
explicit SequenceNumber(unsigned int value) : m_value(value) {}
|
||||
unsigned int value() const { return m_value; }
|
||||
|
||||
SequenceNumber& operator++() {
|
||||
++m_value;
|
||||
if (m_value > 0xffff) m_value = 0;
|
||||
return *this;
|
||||
}
|
||||
SequenceNumber operator++(int) {
|
||||
SequenceNumber tmp(*this);
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
friend bool operator<(const SequenceNumber& lhs, const SequenceNumber& rhs);
|
||||
friend bool operator>(const SequenceNumber& lhs, const SequenceNumber& rhs);
|
||||
friend bool operator<=(const SequenceNumber& lhs, const SequenceNumber& rhs);
|
||||
friend bool operator>=(const SequenceNumber& lhs, const SequenceNumber& rhs);
|
||||
friend bool operator==(const SequenceNumber& lhs, const SequenceNumber& rhs);
|
||||
friend bool operator!=(const SequenceNumber& lhs, const SequenceNumber& rhs);
|
||||
|
||||
private:
|
||||
unsigned int m_value;
|
||||
};
|
||||
|
||||
bool operator<(const SequenceNumber& lhs, const SequenceNumber& rhs);
|
||||
bool operator>(const SequenceNumber& lhs, const SequenceNumber& rhs);
|
||||
|
||||
inline bool operator<=(const SequenceNumber& lhs, const SequenceNumber& rhs) {
|
||||
return lhs == rhs || lhs < rhs;
|
||||
}
|
||||
|
||||
inline bool operator>=(const SequenceNumber& lhs, const SequenceNumber& rhs) {
|
||||
return lhs == rhs || lhs > rhs;
|
||||
}
|
||||
|
||||
inline bool operator==(const SequenceNumber& lhs, const SequenceNumber& rhs) {
|
||||
return lhs.m_value == rhs.m_value;
|
||||
}
|
||||
|
||||
inline bool operator!=(const SequenceNumber& lhs, const SequenceNumber& rhs) {
|
||||
return lhs.m_value != rhs.m_value;
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_SEQUENCENUMBER_H_
|
||||
1137
ntcore/src/main/native/cpp/Storage.cpp
Normal file
1137
ntcore/src/main/native/cpp/Storage.cpp
Normal file
File diff suppressed because it is too large
Load Diff
265
ntcore/src/main/native/cpp/Storage.h
Normal file
265
ntcore/src/main/native/cpp/Storage.h
Normal file
@@ -0,0 +1,265 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_STORAGE_H_
|
||||
#define NTCORE_STORAGE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <llvm/DenseMap.h>
|
||||
#include <llvm/SmallSet.h>
|
||||
#include <llvm/StringMap.h>
|
||||
#include <support/condition_variable.h>
|
||||
#include <support/mutex.h>
|
||||
|
||||
#include "IStorage.h"
|
||||
#include "Message.h"
|
||||
#include "SequenceNumber.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace llvm {
|
||||
class raw_ostream;
|
||||
} // namespace llvm
|
||||
|
||||
namespace wpi {
|
||||
class Logger;
|
||||
class raw_istream;
|
||||
} // namespace wpi
|
||||
|
||||
namespace nt {
|
||||
|
||||
class IEntryNotifier;
|
||||
class INetworkConnection;
|
||||
class IRpcServer;
|
||||
class IStorageTest;
|
||||
|
||||
class Storage : public IStorage {
|
||||
friend class StorageTest;
|
||||
|
||||
public:
|
||||
Storage(IEntryNotifier& notifier, IRpcServer& rpcserver, wpi::Logger& logger);
|
||||
Storage(const Storage&) = delete;
|
||||
Storage& operator=(const Storage&) = delete;
|
||||
|
||||
~Storage();
|
||||
|
||||
// Accessors required by Dispatcher. An interface is used for
|
||||
// generation of outgoing messages to break a dependency loop between
|
||||
// Storage and Dispatcher.
|
||||
void SetDispatcher(IDispatcher* dispatcher, bool server) override;
|
||||
void ClearDispatcher() override;
|
||||
|
||||
// Required for wire protocol 2.0 to get the entry type of an entry when
|
||||
// receiving entry updates (because the length/type is not provided in the
|
||||
// message itself). Not used in wire protocol 3.0.
|
||||
NT_Type GetMessageEntryType(unsigned int id) const override;
|
||||
|
||||
void ProcessIncoming(std::shared_ptr<Message> msg, INetworkConnection* conn,
|
||||
std::weak_ptr<INetworkConnection> conn_weak) override;
|
||||
void GetInitialAssignments(
|
||||
INetworkConnection& conn,
|
||||
std::vector<std::shared_ptr<Message>>* msgs) override;
|
||||
void ApplyInitialAssignments(
|
||||
INetworkConnection& conn, llvm::ArrayRef<std::shared_ptr<Message>> msgs,
|
||||
bool new_server,
|
||||
std::vector<std::shared_ptr<Message>>* out_msgs) override;
|
||||
|
||||
// User functions. These are the actual implementations of the corresponding
|
||||
// user API functions in ntcore_cpp.
|
||||
std::shared_ptr<Value> GetEntryValue(StringRef name) const;
|
||||
std::shared_ptr<Value> GetEntryValue(unsigned int local_id) const;
|
||||
|
||||
bool SetDefaultEntryValue(StringRef name, std::shared_ptr<Value> value);
|
||||
bool SetDefaultEntryValue(unsigned int local_id,
|
||||
std::shared_ptr<Value> value);
|
||||
|
||||
bool SetEntryValue(StringRef name, std::shared_ptr<Value> value);
|
||||
bool SetEntryValue(unsigned int local_id, std::shared_ptr<Value> value);
|
||||
|
||||
void SetEntryTypeValue(StringRef name, std::shared_ptr<Value> value);
|
||||
void SetEntryTypeValue(unsigned int local_id, std::shared_ptr<Value> value);
|
||||
|
||||
void SetEntryFlags(StringRef name, unsigned int flags);
|
||||
void SetEntryFlags(unsigned int local_id, unsigned int flags);
|
||||
|
||||
unsigned int GetEntryFlags(StringRef name) const;
|
||||
unsigned int GetEntryFlags(unsigned int local_id) const;
|
||||
|
||||
void DeleteEntry(StringRef name);
|
||||
void DeleteEntry(unsigned int local_id);
|
||||
|
||||
void DeleteAllEntries();
|
||||
|
||||
std::vector<EntryInfo> GetEntryInfo(int inst, const Twine& prefix,
|
||||
unsigned int types);
|
||||
|
||||
unsigned int AddListener(
|
||||
const Twine& prefix,
|
||||
std::function<void(const EntryNotification& event)> callback,
|
||||
unsigned int flags) const;
|
||||
unsigned int AddListener(
|
||||
unsigned int local_id,
|
||||
std::function<void(const EntryNotification& event)> callback,
|
||||
unsigned int flags) const;
|
||||
|
||||
unsigned int AddPolledListener(unsigned int poller_uid, const Twine& prefix,
|
||||
unsigned int flags) const;
|
||||
unsigned int AddPolledListener(unsigned int poller_uid, unsigned int local_id,
|
||||
unsigned int flags) const;
|
||||
|
||||
// Index-only
|
||||
unsigned int GetEntry(const Twine& name);
|
||||
std::vector<unsigned int> GetEntries(const Twine& prefix, unsigned int types);
|
||||
EntryInfo GetEntryInfo(int inst, unsigned int local_id) const;
|
||||
std::string GetEntryName(unsigned int local_id) const;
|
||||
NT_Type GetEntryType(unsigned int local_id) const;
|
||||
uint64_t GetEntryLastChange(unsigned int local_id) const;
|
||||
|
||||
// Filename-based save/load functions. Used both by periodic saves and
|
||||
// accessible directly via the user API.
|
||||
const char* SavePersistent(const Twine& filename,
|
||||
bool periodic) const override;
|
||||
const char* LoadPersistent(
|
||||
const Twine& filename,
|
||||
std::function<void(size_t line, const char* msg)> warn) override;
|
||||
|
||||
const char* SaveEntries(const Twine& filename, const Twine& prefix) const;
|
||||
const char* LoadEntries(
|
||||
const Twine& filename, const Twine& prefix,
|
||||
std::function<void(size_t line, const char* msg)> warn);
|
||||
|
||||
// Stream-based save/load functions (exposed for testing purposes). These
|
||||
// implement the guts of the filename-based functions.
|
||||
void SavePersistent(llvm::raw_ostream& os, bool periodic) const;
|
||||
bool LoadEntries(wpi::raw_istream& is, const Twine& prefix, bool persistent,
|
||||
std::function<void(size_t line, const char* msg)> warn);
|
||||
|
||||
void SaveEntries(llvm::raw_ostream& os, const Twine& prefix) const;
|
||||
|
||||
// RPC configuration needs to come through here as RPC definitions are
|
||||
// actually special Storage value types.
|
||||
void CreateRpc(unsigned int local_id, StringRef def, unsigned int rpc_uid);
|
||||
unsigned int CallRpc(unsigned int local_id, StringRef params);
|
||||
bool GetRpcResult(unsigned int local_id, unsigned int call_uid,
|
||||
std::string* result);
|
||||
bool GetRpcResult(unsigned int local_id, unsigned int call_uid,
|
||||
std::string* result, double timeout, bool* timed_out);
|
||||
void CancelRpcResult(unsigned int local_id, unsigned int call_uid);
|
||||
|
||||
private:
|
||||
// Data for each table entry.
|
||||
struct Entry {
|
||||
explicit Entry(llvm::StringRef name_) : name(name_) {}
|
||||
bool IsPersistent() const { return (flags & NT_PERSISTENT) != 0; }
|
||||
|
||||
// We redundantly store the name so that it's available when accessing the
|
||||
// raw Entry* via the ID map.
|
||||
std::string name;
|
||||
|
||||
// The current value and flags.
|
||||
std::shared_ptr<Value> value;
|
||||
unsigned int flags{0};
|
||||
|
||||
// Unique ID for this entry as used in network messages. The value is
|
||||
// assigned by the server, so on the client this is 0xffff until an
|
||||
// entry assignment is received back from the server.
|
||||
unsigned int id{0xffff};
|
||||
|
||||
// Local ID.
|
||||
unsigned int local_id{UINT_MAX};
|
||||
|
||||
// Sequence number for update resolution.
|
||||
SequenceNumber seq_num;
|
||||
|
||||
// If value has been written locally. Used during initial handshake
|
||||
// on client to determine whether or not to accept remote changes.
|
||||
bool local_write{false};
|
||||
|
||||
// RPC handle.
|
||||
unsigned int rpc_uid{UINT_MAX};
|
||||
|
||||
// Last UID used when calling this RPC (primarily for client use). This
|
||||
// is incremented for each call.
|
||||
unsigned int rpc_call_uid{0};
|
||||
};
|
||||
|
||||
typedef llvm::StringMap<Entry*> EntriesMap;
|
||||
typedef std::vector<Entry*> IdMap;
|
||||
typedef std::vector<std::unique_ptr<Entry>> LocalMap;
|
||||
typedef std::pair<unsigned int, unsigned int> RpcIdPair;
|
||||
typedef llvm::DenseMap<RpcIdPair, std::string> RpcResultMap;
|
||||
typedef llvm::SmallSet<RpcIdPair, 12> RpcBlockingCallSet;
|
||||
|
||||
mutable wpi::mutex m_mutex;
|
||||
EntriesMap m_entries;
|
||||
IdMap m_idmap;
|
||||
LocalMap m_localmap;
|
||||
RpcResultMap m_rpc_results;
|
||||
RpcBlockingCallSet m_rpc_blocking_calls;
|
||||
// If any persistent values have changed
|
||||
mutable bool m_persistent_dirty = false;
|
||||
|
||||
// condition variable and termination flag for blocking on a RPC result
|
||||
std::atomic_bool m_terminating;
|
||||
wpi::condition_variable m_rpc_results_cond;
|
||||
|
||||
// configured by dispatcher at startup
|
||||
IDispatcher* m_dispatcher = nullptr;
|
||||
bool m_server = true;
|
||||
|
||||
IEntryNotifier& m_notifier;
|
||||
IRpcServer& m_rpc_server;
|
||||
wpi::Logger& m_logger;
|
||||
|
||||
void ProcessIncomingEntryAssign(std::shared_ptr<Message> msg,
|
||||
INetworkConnection* conn);
|
||||
void ProcessIncomingEntryUpdate(std::shared_ptr<Message> msg,
|
||||
INetworkConnection* conn);
|
||||
void ProcessIncomingFlagsUpdate(std::shared_ptr<Message> msg,
|
||||
INetworkConnection* conn);
|
||||
void ProcessIncomingEntryDelete(std::shared_ptr<Message> msg,
|
||||
INetworkConnection* conn);
|
||||
void ProcessIncomingClearEntries(std::shared_ptr<Message> msg,
|
||||
INetworkConnection* conn);
|
||||
void ProcessIncomingExecuteRpc(std::shared_ptr<Message> msg,
|
||||
INetworkConnection* conn,
|
||||
std::weak_ptr<INetworkConnection> conn_weak);
|
||||
void ProcessIncomingRpcResponse(std::shared_ptr<Message> msg,
|
||||
INetworkConnection* conn);
|
||||
|
||||
bool GetPersistentEntries(
|
||||
bool periodic,
|
||||
std::vector<std::pair<std::string, std::shared_ptr<Value>>>* entries)
|
||||
const;
|
||||
bool GetEntries(const Twine& prefix,
|
||||
std::vector<std::pair<std::string, std::shared_ptr<Value>>>*
|
||||
entries) const;
|
||||
void SetEntryValueImpl(Entry* entry, std::shared_ptr<Value> value,
|
||||
std::unique_lock<wpi::mutex>& lock, bool local);
|
||||
void SetEntryFlagsImpl(Entry* entry, unsigned int flags,
|
||||
std::unique_lock<wpi::mutex>& lock, bool local);
|
||||
void DeleteEntryImpl(Entry* entry, std::unique_lock<wpi::mutex>& lock,
|
||||
bool local);
|
||||
|
||||
// Must be called with m_mutex held
|
||||
template <typename F>
|
||||
void DeleteAllEntriesImpl(bool local, F should_delete);
|
||||
void DeleteAllEntriesImpl(bool local);
|
||||
Entry* GetOrNew(const Twine& name);
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_STORAGE_H_
|
||||
453
ntcore/src/main/native/cpp/Storage_load.cpp
Normal file
453
ntcore/src/main/native/cpp/Storage_load.cpp
Normal file
@@ -0,0 +1,453 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
|
||||
#include <llvm/SmallString.h>
|
||||
#include <llvm/StringExtras.h>
|
||||
#include <support/Base64.h>
|
||||
#include <support/raw_istream.h>
|
||||
|
||||
#include "IDispatcher.h"
|
||||
#include "IEntryNotifier.h"
|
||||
#include "Storage.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
namespace {
|
||||
|
||||
class LoadPersistentImpl {
|
||||
public:
|
||||
typedef std::pair<std::string, std::shared_ptr<Value>> Entry;
|
||||
typedef std::function<void(size_t line, const char* msg)> WarnFunc;
|
||||
|
||||
LoadPersistentImpl(wpi::raw_istream& is, WarnFunc warn)
|
||||
: m_is(is), m_warn(warn) {}
|
||||
|
||||
bool Load(StringRef prefix, std::vector<Entry>* entries);
|
||||
|
||||
private:
|
||||
bool ReadLine();
|
||||
bool ReadHeader();
|
||||
NT_Type ReadType();
|
||||
llvm::StringRef ReadName(llvm::SmallVectorImpl<char>& buf);
|
||||
std::shared_ptr<Value> ReadValue(NT_Type type);
|
||||
std::shared_ptr<Value> ReadBooleanValue();
|
||||
std::shared_ptr<Value> ReadDoubleValue();
|
||||
std::shared_ptr<Value> ReadStringValue();
|
||||
std::shared_ptr<Value> ReadRawValue();
|
||||
std::shared_ptr<Value> ReadBooleanArrayValue();
|
||||
std::shared_ptr<Value> ReadDoubleArrayValue();
|
||||
std::shared_ptr<Value> ReadStringArrayValue();
|
||||
|
||||
void Warn(const char* msg) {
|
||||
if (m_warn) m_warn(m_line_num, msg);
|
||||
}
|
||||
|
||||
wpi::raw_istream& m_is;
|
||||
WarnFunc m_warn;
|
||||
|
||||
llvm::StringRef m_line;
|
||||
llvm::SmallString<128> m_line_buf;
|
||||
size_t m_line_num = 0;
|
||||
|
||||
std::vector<int> m_buf_boolean_array;
|
||||
std::vector<double> m_buf_double_array;
|
||||
std::vector<std::string> m_buf_string_array;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/* Extracts an escaped string token. Does not unescape the string.
|
||||
* If a string cannot be matched, an empty string is returned.
|
||||
* If the string is unterminated, an empty tail string is returned.
|
||||
* The returned token includes the starting and trailing quotes (unless the
|
||||
* string is unterminated).
|
||||
* Returns a pair containing the extracted token (if any) and the remaining
|
||||
* tail string.
|
||||
*/
|
||||
static std::pair<llvm::StringRef, llvm::StringRef> ReadStringToken(
|
||||
llvm::StringRef source) {
|
||||
// Match opening quote
|
||||
if (source.empty() || source.front() != '"')
|
||||
return std::make_pair(llvm::StringRef(), source);
|
||||
|
||||
// Scan for ending double quote, checking for escaped as we go.
|
||||
size_t size = source.size();
|
||||
size_t pos;
|
||||
for (pos = 1; pos < size; ++pos) {
|
||||
if (source[pos] == '"' && source[pos - 1] != '\\') {
|
||||
++pos; // we want to include the trailing quote in the result
|
||||
break;
|
||||
}
|
||||
}
|
||||
return std::make_pair(source.slice(0, pos), source.substr(pos));
|
||||
}
|
||||
|
||||
static int fromxdigit(char ch) {
|
||||
if (ch >= 'a' && ch <= 'f')
|
||||
return (ch - 'a' + 10);
|
||||
else if (ch >= 'A' && ch <= 'F')
|
||||
return (ch - 'A' + 10);
|
||||
else
|
||||
return ch - '0';
|
||||
}
|
||||
|
||||
static llvm::StringRef UnescapeString(llvm::StringRef source,
|
||||
llvm::SmallVectorImpl<char>& buf) {
|
||||
assert(source.size() >= 2 && source.front() == '"' && source.back() == '"');
|
||||
buf.clear();
|
||||
buf.reserve(source.size() - 2);
|
||||
for (auto s = source.begin() + 1, end = source.end() - 1; s != end; ++s) {
|
||||
if (*s != '\\') {
|
||||
buf.push_back(*s);
|
||||
continue;
|
||||
}
|
||||
switch (*++s) {
|
||||
case '\\':
|
||||
case '"':
|
||||
buf.push_back(s[-1]);
|
||||
break;
|
||||
case 't':
|
||||
buf.push_back('\t');
|
||||
break;
|
||||
case 'n':
|
||||
buf.push_back('\n');
|
||||
break;
|
||||
case 'x': {
|
||||
if (!isxdigit(*(s + 1))) {
|
||||
buf.push_back('x'); // treat it like a unknown escape
|
||||
break;
|
||||
}
|
||||
int ch = fromxdigit(*++s);
|
||||
if (std::isxdigit(*(s + 1))) {
|
||||
ch <<= 4;
|
||||
ch |= fromxdigit(*++s);
|
||||
}
|
||||
buf.push_back(static_cast<char>(ch));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
buf.push_back(s[-1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return llvm::StringRef{buf.data(), buf.size()};
|
||||
}
|
||||
|
||||
bool LoadPersistentImpl::Load(StringRef prefix, std::vector<Entry>* entries) {
|
||||
if (!ReadHeader()) return false; // header
|
||||
|
||||
while (ReadLine()) {
|
||||
// type
|
||||
NT_Type type = ReadType();
|
||||
if (type == NT_UNASSIGNED) {
|
||||
Warn("unrecognized type");
|
||||
continue;
|
||||
}
|
||||
|
||||
// name
|
||||
llvm::SmallString<128> buf;
|
||||
llvm::StringRef name = ReadName(buf);
|
||||
if (name.empty() || !name.startswith(prefix)) continue;
|
||||
|
||||
// =
|
||||
m_line = m_line.ltrim(" \t");
|
||||
if (m_line.empty() || m_line.front() != '=') {
|
||||
Warn("expected = after name");
|
||||
continue;
|
||||
}
|
||||
m_line = m_line.drop_front().ltrim(" \t");
|
||||
|
||||
// value
|
||||
auto value = ReadValue(type);
|
||||
|
||||
// move to entries
|
||||
if (value) entries->emplace_back(name, std::move(value));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadPersistentImpl::ReadLine() {
|
||||
// ignore blank lines and lines that start with ; or # (comments)
|
||||
while (!m_is.has_error()) {
|
||||
++m_line_num;
|
||||
m_line = m_is.getline(m_line_buf, INT_MAX).trim();
|
||||
if (!m_line.empty() && m_line.front() != ';' && m_line.front() != '#')
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LoadPersistentImpl::ReadHeader() {
|
||||
// header
|
||||
if (!ReadLine() || m_line != "[NetworkTables Storage 3.0]") {
|
||||
Warn("header line mismatch, ignoring rest of file");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
NT_Type LoadPersistentImpl::ReadType() {
|
||||
llvm::StringRef tok;
|
||||
std::tie(tok, m_line) = m_line.split(' ');
|
||||
if (tok == "boolean") {
|
||||
return NT_BOOLEAN;
|
||||
} else if (tok == "double") {
|
||||
return NT_DOUBLE;
|
||||
} else if (tok == "string") {
|
||||
return NT_STRING;
|
||||
} else if (tok == "raw") {
|
||||
return NT_RAW;
|
||||
} else if (tok == "array") {
|
||||
llvm::StringRef array_tok;
|
||||
std::tie(array_tok, m_line) = m_line.split(' ');
|
||||
if (array_tok == "boolean")
|
||||
return NT_BOOLEAN_ARRAY;
|
||||
else if (array_tok == "double")
|
||||
return NT_DOUBLE_ARRAY;
|
||||
else if (array_tok == "string")
|
||||
return NT_STRING_ARRAY;
|
||||
}
|
||||
return NT_UNASSIGNED;
|
||||
}
|
||||
|
||||
llvm::StringRef LoadPersistentImpl::ReadName(llvm::SmallVectorImpl<char>& buf) {
|
||||
llvm::StringRef tok;
|
||||
std::tie(tok, m_line) = ReadStringToken(m_line);
|
||||
if (tok.empty()) {
|
||||
Warn("missing name");
|
||||
return llvm::StringRef{};
|
||||
}
|
||||
if (tok.back() != '"') {
|
||||
Warn("unterminated name string");
|
||||
return llvm::StringRef{};
|
||||
}
|
||||
return UnescapeString(tok, buf);
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> LoadPersistentImpl::ReadValue(NT_Type type) {
|
||||
switch (type) {
|
||||
case NT_BOOLEAN:
|
||||
return ReadBooleanValue();
|
||||
case NT_DOUBLE:
|
||||
return ReadDoubleValue();
|
||||
case NT_STRING:
|
||||
return ReadStringValue();
|
||||
case NT_RAW:
|
||||
return ReadRawValue();
|
||||
case NT_BOOLEAN_ARRAY:
|
||||
return ReadBooleanArrayValue();
|
||||
case NT_DOUBLE_ARRAY:
|
||||
return ReadDoubleArrayValue();
|
||||
case NT_STRING_ARRAY:
|
||||
return ReadStringArrayValue();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> LoadPersistentImpl::ReadBooleanValue() {
|
||||
// only true or false is accepted
|
||||
if (m_line == "true") return Value::MakeBoolean(true);
|
||||
if (m_line == "false") return Value::MakeBoolean(false);
|
||||
Warn("unrecognized boolean value, not 'true' or 'false'");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> LoadPersistentImpl::ReadDoubleValue() {
|
||||
// need to convert to null-terminated string for std::strtod()
|
||||
llvm::SmallString<64> buf;
|
||||
char* end;
|
||||
double v = std::strtod(m_line.c_str(buf), &end);
|
||||
if (*end != '\0') {
|
||||
Warn("invalid double value");
|
||||
return nullptr;
|
||||
}
|
||||
return Value::MakeDouble(v);
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> LoadPersistentImpl::ReadStringValue() {
|
||||
llvm::StringRef tok;
|
||||
std::tie(tok, m_line) = ReadStringToken(m_line);
|
||||
if (tok.empty()) {
|
||||
Warn("missing string value");
|
||||
return nullptr;
|
||||
}
|
||||
if (tok.back() != '"') {
|
||||
Warn("unterminated string value");
|
||||
return nullptr;
|
||||
}
|
||||
llvm::SmallString<128> buf;
|
||||
return Value::MakeString(UnescapeString(tok, buf));
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> LoadPersistentImpl::ReadRawValue() {
|
||||
llvm::SmallString<128> buf;
|
||||
size_t nr;
|
||||
return Value::MakeRaw(wpi::Base64Decode(m_line, &nr, buf));
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> LoadPersistentImpl::ReadBooleanArrayValue() {
|
||||
m_buf_boolean_array.clear();
|
||||
while (!m_line.empty()) {
|
||||
llvm::StringRef tok;
|
||||
std::tie(tok, m_line) = m_line.split(',');
|
||||
tok = tok.trim(" \t");
|
||||
if (tok == "true") {
|
||||
m_buf_boolean_array.push_back(1);
|
||||
} else if (tok == "false") {
|
||||
m_buf_boolean_array.push_back(0);
|
||||
} else {
|
||||
Warn("unrecognized boolean value, not 'true' or 'false'");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return Value::MakeBooleanArray(std::move(m_buf_boolean_array));
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> LoadPersistentImpl::ReadDoubleArrayValue() {
|
||||
m_buf_double_array.clear();
|
||||
while (!m_line.empty()) {
|
||||
llvm::StringRef tok;
|
||||
std::tie(tok, m_line) = m_line.split(',');
|
||||
tok = tok.trim(" \t");
|
||||
// need to convert to null-terminated string for std::strtod()
|
||||
llvm::SmallString<64> buf;
|
||||
char* end;
|
||||
double v = std::strtod(tok.c_str(buf), &end);
|
||||
if (*end != '\0') {
|
||||
Warn("invalid double value");
|
||||
return nullptr;
|
||||
}
|
||||
m_buf_double_array.push_back(v);
|
||||
}
|
||||
|
||||
return Value::MakeDoubleArray(std::move(m_buf_double_array));
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> LoadPersistentImpl::ReadStringArrayValue() {
|
||||
m_buf_string_array.clear();
|
||||
while (!m_line.empty()) {
|
||||
llvm::StringRef tok;
|
||||
std::tie(tok, m_line) = ReadStringToken(m_line);
|
||||
if (tok.empty()) {
|
||||
Warn("missing string value");
|
||||
return nullptr;
|
||||
}
|
||||
if (tok.back() != '"') {
|
||||
Warn("unterminated string value");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
llvm::SmallString<128> buf;
|
||||
m_buf_string_array.push_back(UnescapeString(tok, buf));
|
||||
|
||||
m_line = m_line.ltrim(" \t");
|
||||
if (m_line.empty()) break;
|
||||
if (m_line.front() != ',') {
|
||||
Warn("expected comma between strings");
|
||||
return nullptr;
|
||||
}
|
||||
m_line = m_line.drop_front().ltrim(" \t");
|
||||
}
|
||||
|
||||
return Value::MakeStringArray(std::move(m_buf_string_array));
|
||||
}
|
||||
|
||||
bool Storage::LoadEntries(
|
||||
wpi::raw_istream& is, const Twine& prefix, bool persistent,
|
||||
std::function<void(size_t line, const char* msg)> warn) {
|
||||
llvm::SmallString<128> prefixBuf;
|
||||
StringRef prefixStr = prefix.toStringRef(prefixBuf);
|
||||
|
||||
// entries to add
|
||||
std::vector<LoadPersistentImpl::Entry> entries;
|
||||
|
||||
// load file
|
||||
if (!LoadPersistentImpl(is, warn).Load(prefixStr, &entries)) return false;
|
||||
|
||||
// copy values into storage as quickly as possible so lock isn't held
|
||||
std::vector<std::shared_ptr<Message>> msgs;
|
||||
std::unique_lock<wpi::mutex> lock(m_mutex);
|
||||
for (auto& i : entries) {
|
||||
Entry* entry = GetOrNew(i.first);
|
||||
auto old_value = entry->value;
|
||||
entry->value = i.second;
|
||||
bool was_persist = entry->IsPersistent();
|
||||
if (!was_persist && persistent) entry->flags |= NT_PERSISTENT;
|
||||
|
||||
// if we're the server, assign an id if it doesn't have one
|
||||
if (m_server && entry->id == 0xffff) {
|
||||
unsigned int id = m_idmap.size();
|
||||
entry->id = id;
|
||||
m_idmap.push_back(entry);
|
||||
}
|
||||
|
||||
// notify (for local listeners)
|
||||
if (m_notifier.local_notifiers()) {
|
||||
if (!old_value) {
|
||||
m_notifier.NotifyEntry(entry->local_id, i.first, i.second,
|
||||
NT_NOTIFY_NEW | NT_NOTIFY_LOCAL);
|
||||
} else if (*old_value != *i.second) {
|
||||
unsigned int notify_flags = NT_NOTIFY_UPDATE | NT_NOTIFY_LOCAL;
|
||||
if (!was_persist && persistent) notify_flags |= NT_NOTIFY_FLAGS;
|
||||
m_notifier.NotifyEntry(entry->local_id, i.first, i.second,
|
||||
notify_flags);
|
||||
} else if (!was_persist && persistent) {
|
||||
m_notifier.NotifyEntry(entry->local_id, i.first, i.second,
|
||||
NT_NOTIFY_FLAGS | NT_NOTIFY_LOCAL);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_dispatcher) continue; // shortcut
|
||||
++entry->seq_num;
|
||||
|
||||
// put on update queue
|
||||
if (!old_value || old_value->type() != i.second->type()) {
|
||||
msgs.emplace_back(Message::EntryAssign(
|
||||
i.first, entry->id, entry->seq_num.value(), i.second, entry->flags));
|
||||
} else if (entry->id != 0xffff) {
|
||||
// don't send an update if we don't have an assigned id yet
|
||||
if (*old_value != *i.second)
|
||||
msgs.emplace_back(
|
||||
Message::EntryUpdate(entry->id, entry->seq_num.value(), i.second));
|
||||
if (!was_persist)
|
||||
msgs.emplace_back(Message::FlagsUpdate(entry->id, entry->flags));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_dispatcher) {
|
||||
auto dispatcher = m_dispatcher;
|
||||
lock.unlock();
|
||||
for (auto& msg : msgs)
|
||||
dispatcher->QueueOutgoing(std::move(msg), nullptr, nullptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* Storage::LoadPersistent(
|
||||
const Twine& filename,
|
||||
std::function<void(size_t line, const char* msg)> warn) {
|
||||
std::error_code ec;
|
||||
wpi::raw_fd_istream is(filename, ec);
|
||||
if (ec.value() != 0) return "could not open file";
|
||||
if (!LoadEntries(is, "", true, warn)) return "error reading file";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* Storage::LoadEntries(
|
||||
const Twine& filename, const Twine& prefix,
|
||||
std::function<void(size_t line, const char* msg)> warn) {
|
||||
std::error_code ec;
|
||||
wpi::raw_fd_istream is(filename, ec);
|
||||
if (ec.value() != 0) return "could not open file";
|
||||
if (!LoadEntries(is, prefix, false, warn)) return "error reading file";
|
||||
return nullptr;
|
||||
}
|
||||
271
ntcore/src/main/native/cpp/Storage_save.cpp
Normal file
271
ntcore/src/main/native/cpp/Storage_save.cpp
Normal file
@@ -0,0 +1,271 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
|
||||
#include <llvm/Format.h>
|
||||
#include <llvm/SmallString.h>
|
||||
#include <llvm/StringExtras.h>
|
||||
#include <llvm/raw_ostream.h>
|
||||
#include <support/Base64.h>
|
||||
|
||||
#include "Log.h"
|
||||
#include "Storage.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
namespace {
|
||||
|
||||
class SavePersistentImpl {
|
||||
public:
|
||||
typedef std::pair<std::string, std::shared_ptr<Value>> Entry;
|
||||
|
||||
explicit SavePersistentImpl(llvm::raw_ostream& os) : m_os(os) {}
|
||||
|
||||
void Save(llvm::ArrayRef<Entry> entries);
|
||||
|
||||
private:
|
||||
void WriteString(llvm::StringRef str);
|
||||
void WriteHeader();
|
||||
void WriteEntries(llvm::ArrayRef<Entry> entries);
|
||||
void WriteEntry(llvm::StringRef name, const Value& value);
|
||||
bool WriteType(NT_Type type);
|
||||
void WriteValue(const Value& value);
|
||||
|
||||
llvm::raw_ostream& m_os;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/* Escapes and writes a string, including start and end double quotes */
|
||||
void SavePersistentImpl::WriteString(llvm::StringRef str) {
|
||||
m_os << '"';
|
||||
for (auto c : str) {
|
||||
switch (c) {
|
||||
case '\\':
|
||||
m_os << "\\\\";
|
||||
break;
|
||||
case '\t':
|
||||
m_os << "\\t";
|
||||
break;
|
||||
case '\n':
|
||||
m_os << "\\n";
|
||||
break;
|
||||
case '"':
|
||||
m_os << "\\\"";
|
||||
break;
|
||||
default:
|
||||
if (std::isprint(c) && c != '=') {
|
||||
m_os << c;
|
||||
break;
|
||||
}
|
||||
|
||||
// Write out the escaped representation.
|
||||
m_os << "\\x";
|
||||
m_os << llvm::hexdigit((c >> 4) & 0xF);
|
||||
m_os << llvm::hexdigit((c >> 0) & 0xF);
|
||||
}
|
||||
}
|
||||
m_os << '"';
|
||||
}
|
||||
|
||||
void SavePersistentImpl::Save(llvm::ArrayRef<Entry> entries) {
|
||||
WriteHeader();
|
||||
WriteEntries(entries);
|
||||
}
|
||||
|
||||
void SavePersistentImpl::WriteHeader() {
|
||||
m_os << "[NetworkTables Storage 3.0]\n";
|
||||
}
|
||||
|
||||
void SavePersistentImpl::WriteEntries(llvm::ArrayRef<Entry> entries) {
|
||||
for (auto& i : entries) {
|
||||
if (!i.second) continue;
|
||||
WriteEntry(i.first, *i.second);
|
||||
}
|
||||
}
|
||||
|
||||
void SavePersistentImpl::WriteEntry(llvm::StringRef name, const Value& value) {
|
||||
if (!WriteType(value.type())) return; // type
|
||||
WriteString(name); // name
|
||||
m_os << '='; // '='
|
||||
WriteValue(value); // value
|
||||
m_os << '\n'; // eol
|
||||
}
|
||||
|
||||
bool SavePersistentImpl::WriteType(NT_Type type) {
|
||||
switch (type) {
|
||||
case NT_BOOLEAN:
|
||||
m_os << "boolean ";
|
||||
break;
|
||||
case NT_DOUBLE:
|
||||
m_os << "double ";
|
||||
break;
|
||||
case NT_STRING:
|
||||
m_os << "string ";
|
||||
break;
|
||||
case NT_RAW:
|
||||
m_os << "raw ";
|
||||
break;
|
||||
case NT_BOOLEAN_ARRAY:
|
||||
m_os << "array boolean ";
|
||||
break;
|
||||
case NT_DOUBLE_ARRAY:
|
||||
m_os << "array double ";
|
||||
break;
|
||||
case NT_STRING_ARRAY:
|
||||
m_os << "array string ";
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SavePersistentImpl::WriteValue(const Value& value) {
|
||||
switch (value.type()) {
|
||||
case NT_BOOLEAN:
|
||||
m_os << (value.GetBoolean() ? "true" : "false");
|
||||
break;
|
||||
case NT_DOUBLE:
|
||||
m_os << llvm::format("%g", value.GetDouble());
|
||||
break;
|
||||
case NT_STRING:
|
||||
WriteString(value.GetString());
|
||||
break;
|
||||
case NT_RAW: {
|
||||
wpi::Base64Encode(m_os, value.GetRaw());
|
||||
break;
|
||||
}
|
||||
case NT_BOOLEAN_ARRAY: {
|
||||
bool first = true;
|
||||
for (auto elem : value.GetBooleanArray()) {
|
||||
if (!first) m_os << ',';
|
||||
first = false;
|
||||
m_os << (elem ? "true" : "false");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NT_DOUBLE_ARRAY: {
|
||||
bool first = true;
|
||||
for (auto elem : value.GetDoubleArray()) {
|
||||
if (!first) m_os << ',';
|
||||
first = false;
|
||||
m_os << llvm::format("%g", elem);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NT_STRING_ARRAY: {
|
||||
bool first = true;
|
||||
for (auto& elem : value.GetStringArray()) {
|
||||
if (!first) m_os << ',';
|
||||
first = false;
|
||||
WriteString(elem);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Storage::SavePersistent(llvm::raw_ostream& os, bool periodic) const {
|
||||
std::vector<SavePersistentImpl::Entry> entries;
|
||||
if (!GetPersistentEntries(periodic, &entries)) return;
|
||||
SavePersistentImpl(os).Save(entries);
|
||||
}
|
||||
|
||||
const char* Storage::SavePersistent(const Twine& filename,
|
||||
bool periodic) const {
|
||||
llvm::SmallString<128> fn;
|
||||
filename.toVector(fn);
|
||||
llvm::SmallString<128> tmp = fn;
|
||||
tmp += ".tmp";
|
||||
llvm::SmallString<128> bak = fn;
|
||||
bak += ".bak";
|
||||
|
||||
// Get entries before creating file
|
||||
std::vector<SavePersistentImpl::Entry> entries;
|
||||
if (!GetPersistentEntries(periodic, &entries)) return nullptr;
|
||||
|
||||
const char* err = nullptr;
|
||||
|
||||
// start by writing to temporary file
|
||||
std::error_code ec;
|
||||
llvm::raw_fd_ostream os(tmp, ec, llvm::sys::fs::F_Text);
|
||||
if (ec.value() != 0) {
|
||||
err = "could not open file";
|
||||
goto done;
|
||||
}
|
||||
DEBUG("saving persistent file '" << filename << "'");
|
||||
SavePersistentImpl(os).Save(entries);
|
||||
os.close();
|
||||
if (os.has_error()) {
|
||||
std::remove(tmp.c_str());
|
||||
err = "error saving file";
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Safely move to real file. We ignore any failures related to the backup.
|
||||
std::remove(bak.c_str());
|
||||
std::rename(fn.c_str(), bak.c_str());
|
||||
if (std::rename(tmp.c_str(), fn.c_str()) != 0) {
|
||||
std::rename(bak.c_str(), fn.c_str()); // attempt to restore backup
|
||||
err = "could not rename temp file to real file";
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
// try again if there was an error
|
||||
if (err && periodic) m_persistent_dirty = true;
|
||||
return err;
|
||||
}
|
||||
|
||||
void Storage::SaveEntries(llvm::raw_ostream& os, const Twine& prefix) const {
|
||||
std::vector<SavePersistentImpl::Entry> entries;
|
||||
if (!GetEntries(prefix, &entries)) return;
|
||||
SavePersistentImpl(os).Save(entries);
|
||||
}
|
||||
|
||||
const char* Storage::SaveEntries(const Twine& filename,
|
||||
const Twine& prefix) const {
|
||||
llvm::SmallString<128> fn;
|
||||
filename.toVector(fn);
|
||||
llvm::SmallString<128> tmp = fn;
|
||||
tmp += ".tmp";
|
||||
llvm::SmallString<128> bak = fn;
|
||||
bak += ".bak";
|
||||
|
||||
// Get entries before creating file
|
||||
std::vector<SavePersistentImpl::Entry> entries;
|
||||
if (!GetEntries(prefix, &entries)) return nullptr;
|
||||
|
||||
// start by writing to temporary file
|
||||
std::error_code ec;
|
||||
llvm::raw_fd_ostream os(tmp, ec, llvm::sys::fs::F_Text);
|
||||
if (ec.value() != 0) {
|
||||
return "could not open file";
|
||||
}
|
||||
DEBUG("saving file '" << filename << "'");
|
||||
SavePersistentImpl(os).Save(entries);
|
||||
os.close();
|
||||
if (os.has_error()) {
|
||||
std::remove(tmp.c_str());
|
||||
return "error saving file";
|
||||
}
|
||||
|
||||
// Safely move to real file. We ignore any failures related to the backup.
|
||||
std::remove(bak.c_str());
|
||||
std::rename(fn.c_str(), bak.c_str());
|
||||
if (std::rename(tmp.c_str(), fn.c_str()) != 0) {
|
||||
std::rename(bak.c_str(), fn.c_str()); // attempt to restore backup
|
||||
return "could not rename temp file to real file";
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
218
ntcore/src/main/native/cpp/Value.cpp
Normal file
218
ntcore/src/main/native/cpp/Value.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <support/timestamp.h>
|
||||
|
||||
#include "Value_internal.h"
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
Value::Value() {
|
||||
m_val.type = NT_UNASSIGNED;
|
||||
m_val.last_change = wpi::Now();
|
||||
}
|
||||
|
||||
Value::Value(NT_Type type, uint64_t time, const private_init&) {
|
||||
m_val.type = type;
|
||||
if (time == 0)
|
||||
m_val.last_change = wpi::Now();
|
||||
else
|
||||
m_val.last_change = time;
|
||||
if (m_val.type == NT_BOOLEAN_ARRAY)
|
||||
m_val.data.arr_boolean.arr = nullptr;
|
||||
else if (m_val.type == NT_DOUBLE_ARRAY)
|
||||
m_val.data.arr_double.arr = nullptr;
|
||||
else if (m_val.type == NT_STRING_ARRAY)
|
||||
m_val.data.arr_string.arr = nullptr;
|
||||
}
|
||||
|
||||
Value::~Value() {
|
||||
if (m_val.type == NT_BOOLEAN_ARRAY)
|
||||
delete[] m_val.data.arr_boolean.arr;
|
||||
else if (m_val.type == NT_DOUBLE_ARRAY)
|
||||
delete[] m_val.data.arr_double.arr;
|
||||
else if (m_val.type == NT_STRING_ARRAY)
|
||||
delete[] m_val.data.arr_string.arr;
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> Value::MakeBooleanArray(llvm::ArrayRef<int> value,
|
||||
uint64_t time) {
|
||||
auto val = std::make_shared<Value>(NT_BOOLEAN_ARRAY, time, private_init());
|
||||
val->m_val.data.arr_boolean.arr = new int[value.size()];
|
||||
val->m_val.data.arr_boolean.size = value.size();
|
||||
std::copy(value.begin(), value.end(), val->m_val.data.arr_boolean.arr);
|
||||
return val;
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> Value::MakeDoubleArray(llvm::ArrayRef<double> value,
|
||||
uint64_t time) {
|
||||
auto val = std::make_shared<Value>(NT_DOUBLE_ARRAY, time, private_init());
|
||||
val->m_val.data.arr_double.arr = new double[value.size()];
|
||||
val->m_val.data.arr_double.size = value.size();
|
||||
std::copy(value.begin(), value.end(), val->m_val.data.arr_double.arr);
|
||||
return val;
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> Value::MakeStringArray(llvm::ArrayRef<std::string> value,
|
||||
uint64_t time) {
|
||||
auto val = std::make_shared<Value>(NT_STRING_ARRAY, time, private_init());
|
||||
val->m_string_array = value;
|
||||
// point NT_Value to the contents in the vector.
|
||||
val->m_val.data.arr_string.arr = new NT_String[value.size()];
|
||||
val->m_val.data.arr_string.size = val->m_string_array.size();
|
||||
for (size_t i = 0; i < value.size(); ++i) {
|
||||
val->m_val.data.arr_string.arr[i].str = const_cast<char*>(value[i].c_str());
|
||||
val->m_val.data.arr_string.arr[i].len = value[i].size();
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> Value::MakeStringArray(std::vector<std::string>&& value,
|
||||
uint64_t time) {
|
||||
auto val = std::make_shared<Value>(NT_STRING_ARRAY, time, private_init());
|
||||
val->m_string_array = std::move(value);
|
||||
value.clear();
|
||||
// point NT_Value to the contents in the vector.
|
||||
val->m_val.data.arr_string.arr = new NT_String[val->m_string_array.size()];
|
||||
val->m_val.data.arr_string.size = val->m_string_array.size();
|
||||
for (size_t i = 0; i < val->m_string_array.size(); ++i) {
|
||||
val->m_val.data.arr_string.arr[i].str =
|
||||
const_cast<char*>(val->m_string_array[i].c_str());
|
||||
val->m_val.data.arr_string.arr[i].len = val->m_string_array[i].size();
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
void nt::ConvertToC(const Value& in, NT_Value* out) {
|
||||
out->type = NT_UNASSIGNED;
|
||||
switch (in.type()) {
|
||||
case NT_UNASSIGNED:
|
||||
return;
|
||||
case NT_BOOLEAN:
|
||||
out->data.v_boolean = in.GetBoolean() ? 1 : 0;
|
||||
break;
|
||||
case NT_DOUBLE:
|
||||
out->data.v_double = in.GetDouble();
|
||||
break;
|
||||
case NT_STRING:
|
||||
ConvertToC(in.GetString(), &out->data.v_string);
|
||||
break;
|
||||
case NT_RAW:
|
||||
ConvertToC(in.GetRaw(), &out->data.v_raw);
|
||||
break;
|
||||
case NT_RPC:
|
||||
ConvertToC(in.GetRpc(), &out->data.v_raw);
|
||||
break;
|
||||
case NT_BOOLEAN_ARRAY: {
|
||||
auto v = in.GetBooleanArray();
|
||||
out->data.arr_boolean.arr =
|
||||
static_cast<int*>(std::malloc(v.size() * sizeof(int)));
|
||||
out->data.arr_boolean.size = v.size();
|
||||
std::copy(v.begin(), v.end(), out->data.arr_boolean.arr);
|
||||
break;
|
||||
}
|
||||
case NT_DOUBLE_ARRAY: {
|
||||
auto v = in.GetDoubleArray();
|
||||
out->data.arr_double.arr =
|
||||
static_cast<double*>(std::malloc(v.size() * sizeof(double)));
|
||||
out->data.arr_double.size = v.size();
|
||||
std::copy(v.begin(), v.end(), out->data.arr_double.arr);
|
||||
break;
|
||||
}
|
||||
case NT_STRING_ARRAY: {
|
||||
auto v = in.GetStringArray();
|
||||
out->data.arr_string.arr =
|
||||
static_cast<NT_String*>(std::malloc(v.size() * sizeof(NT_String)));
|
||||
for (size_t i = 0; i < v.size(); ++i)
|
||||
ConvertToC(v[i], &out->data.arr_string.arr[i]);
|
||||
out->data.arr_string.size = v.size();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// assert(false && "unknown value type");
|
||||
return;
|
||||
}
|
||||
out->type = in.type();
|
||||
}
|
||||
|
||||
void nt::ConvertToC(llvm::StringRef in, NT_String* out) {
|
||||
out->len = in.size();
|
||||
out->str = static_cast<char*>(std::malloc(in.size() + 1));
|
||||
std::memcpy(out->str, in.data(), in.size());
|
||||
out->str[in.size()] = '\0';
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> nt::ConvertFromC(const NT_Value& value) {
|
||||
switch (value.type) {
|
||||
case NT_UNASSIGNED:
|
||||
return nullptr;
|
||||
case NT_BOOLEAN:
|
||||
return Value::MakeBoolean(value.data.v_boolean != 0);
|
||||
case NT_DOUBLE:
|
||||
return Value::MakeDouble(value.data.v_double);
|
||||
case NT_STRING:
|
||||
return Value::MakeString(ConvertFromC(value.data.v_string));
|
||||
case NT_RAW:
|
||||
return Value::MakeRaw(ConvertFromC(value.data.v_raw));
|
||||
case NT_RPC:
|
||||
return Value::MakeRpc(ConvertFromC(value.data.v_raw));
|
||||
case NT_BOOLEAN_ARRAY:
|
||||
return Value::MakeBooleanArray(llvm::ArrayRef<int>(
|
||||
value.data.arr_boolean.arr, value.data.arr_boolean.size));
|
||||
case NT_DOUBLE_ARRAY:
|
||||
return Value::MakeDoubleArray(llvm::ArrayRef<double>(
|
||||
value.data.arr_double.arr, value.data.arr_double.size));
|
||||
case NT_STRING_ARRAY: {
|
||||
std::vector<std::string> v;
|
||||
v.reserve(value.data.arr_string.size);
|
||||
for (size_t i = 0; i < value.data.arr_string.size; ++i)
|
||||
v.push_back(ConvertFromC(value.data.arr_string.arr[i]));
|
||||
return Value::MakeStringArray(std::move(v));
|
||||
}
|
||||
default:
|
||||
// assert(false && "unknown value type");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool nt::operator==(const Value& lhs, const Value& rhs) {
|
||||
if (lhs.type() != rhs.type()) return false;
|
||||
switch (lhs.type()) {
|
||||
case NT_UNASSIGNED:
|
||||
return true; // XXX: is this better being false instead?
|
||||
case NT_BOOLEAN:
|
||||
return lhs.m_val.data.v_boolean == rhs.m_val.data.v_boolean;
|
||||
case NT_DOUBLE:
|
||||
return lhs.m_val.data.v_double == rhs.m_val.data.v_double;
|
||||
case NT_STRING:
|
||||
case NT_RAW:
|
||||
case NT_RPC:
|
||||
return lhs.m_string == rhs.m_string;
|
||||
case NT_BOOLEAN_ARRAY:
|
||||
if (lhs.m_val.data.arr_boolean.size != rhs.m_val.data.arr_boolean.size)
|
||||
return false;
|
||||
return std::memcmp(lhs.m_val.data.arr_boolean.arr,
|
||||
rhs.m_val.data.arr_boolean.arr,
|
||||
lhs.m_val.data.arr_boolean.size *
|
||||
sizeof(lhs.m_val.data.arr_boolean.arr[0])) == 0;
|
||||
case NT_DOUBLE_ARRAY:
|
||||
if (lhs.m_val.data.arr_double.size != rhs.m_val.data.arr_double.size)
|
||||
return false;
|
||||
return std::memcmp(lhs.m_val.data.arr_double.arr,
|
||||
rhs.m_val.data.arr_double.arr,
|
||||
lhs.m_val.data.arr_double.size *
|
||||
sizeof(lhs.m_val.data.arr_double.arr[0])) == 0;
|
||||
case NT_STRING_ARRAY:
|
||||
return lhs.m_string_array == rhs.m_string_array;
|
||||
default:
|
||||
// assert(false && "unknown value type");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
31
ntcore/src/main/native/cpp/Value_internal.h
Normal file
31
ntcore/src/main/native/cpp/Value_internal.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_VALUE_INTERNAL_H_
|
||||
#define NTCORE_VALUE_INTERNAL_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <llvm/StringRef.h>
|
||||
|
||||
#include "ntcore_c.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class Value;
|
||||
|
||||
void ConvertToC(const Value& in, NT_Value* out);
|
||||
std::shared_ptr<Value> ConvertFromC(const NT_Value& value);
|
||||
void ConvertToC(llvm::StringRef in, NT_String* out);
|
||||
inline llvm::StringRef ConvertFromC(const NT_String& str) {
|
||||
return llvm::StringRef(str.str, str.len);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_VALUE_INTERNAL_H_
|
||||
207
ntcore/src/main/native/cpp/WireDecoder.cpp
Normal file
207
ntcore/src/main/native/cpp/WireDecoder.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "WireDecoder.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <llvm/MathExtras.h>
|
||||
#include <support/leb128.h>
|
||||
|
||||
using namespace nt;
|
||||
|
||||
static double ReadDouble(const char*& buf) {
|
||||
// Fast but non-portable!
|
||||
uint64_t val = (*reinterpret_cast<const unsigned char*>(buf)) & 0xff;
|
||||
++buf;
|
||||
val <<= 8;
|
||||
val |= (*reinterpret_cast<const unsigned char*>(buf)) & 0xff;
|
||||
++buf;
|
||||
val <<= 8;
|
||||
val |= (*reinterpret_cast<const unsigned char*>(buf)) & 0xff;
|
||||
++buf;
|
||||
val <<= 8;
|
||||
val |= (*reinterpret_cast<const unsigned char*>(buf)) & 0xff;
|
||||
++buf;
|
||||
val <<= 8;
|
||||
val |= (*reinterpret_cast<const unsigned char*>(buf)) & 0xff;
|
||||
++buf;
|
||||
val <<= 8;
|
||||
val |= (*reinterpret_cast<const unsigned char*>(buf)) & 0xff;
|
||||
++buf;
|
||||
val <<= 8;
|
||||
val |= (*reinterpret_cast<const unsigned char*>(buf)) & 0xff;
|
||||
++buf;
|
||||
val <<= 8;
|
||||
val |= (*reinterpret_cast<const unsigned char*>(buf)) & 0xff;
|
||||
++buf;
|
||||
return llvm::BitsToDouble(val);
|
||||
}
|
||||
|
||||
WireDecoder::WireDecoder(wpi::raw_istream& is, unsigned int proto_rev,
|
||||
wpi::Logger& logger)
|
||||
: m_is(is), m_logger(logger) {
|
||||
// Start with a 1K temporary buffer. Use malloc instead of new so we can
|
||||
// realloc.
|
||||
m_allocated = 1024;
|
||||
m_buf = static_cast<char*>(std::malloc(m_allocated));
|
||||
m_proto_rev = proto_rev;
|
||||
m_error = nullptr;
|
||||
}
|
||||
|
||||
WireDecoder::~WireDecoder() { std::free(m_buf); }
|
||||
|
||||
bool WireDecoder::ReadDouble(double* val) {
|
||||
const char* buf;
|
||||
if (!Read(&buf, 8)) return false;
|
||||
*val = ::ReadDouble(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WireDecoder::Realloc(size_t len) {
|
||||
// Double current buffer size until we have enough space.
|
||||
if (m_allocated >= len) return;
|
||||
size_t newlen = m_allocated * 2;
|
||||
while (newlen < len) newlen *= 2;
|
||||
m_buf = static_cast<char*>(std::realloc(m_buf, newlen));
|
||||
m_allocated = newlen;
|
||||
}
|
||||
|
||||
bool WireDecoder::ReadType(NT_Type* type) {
|
||||
unsigned int itype;
|
||||
if (!Read8(&itype)) return false;
|
||||
// Convert from byte value to enum
|
||||
switch (itype) {
|
||||
case 0x00:
|
||||
*type = NT_BOOLEAN;
|
||||
break;
|
||||
case 0x01:
|
||||
*type = NT_DOUBLE;
|
||||
break;
|
||||
case 0x02:
|
||||
*type = NT_STRING;
|
||||
break;
|
||||
case 0x03:
|
||||
*type = NT_RAW;
|
||||
break;
|
||||
case 0x10:
|
||||
*type = NT_BOOLEAN_ARRAY;
|
||||
break;
|
||||
case 0x11:
|
||||
*type = NT_DOUBLE_ARRAY;
|
||||
break;
|
||||
case 0x12:
|
||||
*type = NT_STRING_ARRAY;
|
||||
break;
|
||||
case 0x20:
|
||||
*type = NT_RPC;
|
||||
break;
|
||||
default:
|
||||
*type = NT_UNASSIGNED;
|
||||
m_error = "unrecognized value type";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> WireDecoder::ReadValue(NT_Type type) {
|
||||
switch (type) {
|
||||
case NT_BOOLEAN: {
|
||||
unsigned int v;
|
||||
if (!Read8(&v)) return nullptr;
|
||||
return Value::MakeBoolean(v != 0);
|
||||
}
|
||||
case NT_DOUBLE: {
|
||||
double v;
|
||||
if (!ReadDouble(&v)) return nullptr;
|
||||
return Value::MakeDouble(v);
|
||||
}
|
||||
case NT_STRING: {
|
||||
std::string v;
|
||||
if (!ReadString(&v)) return nullptr;
|
||||
return Value::MakeString(std::move(v));
|
||||
}
|
||||
case NT_RAW: {
|
||||
if (m_proto_rev < 0x0300u) {
|
||||
m_error = "received raw value in protocol < 3.0";
|
||||
return nullptr;
|
||||
}
|
||||
std::string v;
|
||||
if (!ReadString(&v)) return nullptr;
|
||||
return Value::MakeRaw(std::move(v));
|
||||
}
|
||||
case NT_RPC: {
|
||||
if (m_proto_rev < 0x0300u) {
|
||||
m_error = "received RPC value in protocol < 3.0";
|
||||
return nullptr;
|
||||
}
|
||||
std::string v;
|
||||
if (!ReadString(&v)) return nullptr;
|
||||
return Value::MakeRpc(std::move(v));
|
||||
}
|
||||
case NT_BOOLEAN_ARRAY: {
|
||||
// size
|
||||
unsigned int size;
|
||||
if (!Read8(&size)) return nullptr;
|
||||
|
||||
// array values
|
||||
const char* buf;
|
||||
if (!Read(&buf, size)) return nullptr;
|
||||
std::vector<int> v(size);
|
||||
for (unsigned int i = 0; i < size; ++i) v[i] = buf[i] ? 1 : 0;
|
||||
return Value::MakeBooleanArray(std::move(v));
|
||||
}
|
||||
case NT_DOUBLE_ARRAY: {
|
||||
// size
|
||||
unsigned int size;
|
||||
if (!Read8(&size)) return nullptr;
|
||||
|
||||
// array values
|
||||
const char* buf;
|
||||
if (!Read(&buf, size * 8)) return nullptr;
|
||||
std::vector<double> v(size);
|
||||
for (unsigned int i = 0; i < size; ++i) v[i] = ::ReadDouble(buf);
|
||||
return Value::MakeDoubleArray(std::move(v));
|
||||
}
|
||||
case NT_STRING_ARRAY: {
|
||||
// size
|
||||
unsigned int size;
|
||||
if (!Read8(&size)) return nullptr;
|
||||
|
||||
// array values
|
||||
std::vector<std::string> v(size);
|
||||
for (unsigned int i = 0; i < size; ++i) {
|
||||
if (!ReadString(&v[i])) return nullptr;
|
||||
}
|
||||
return Value::MakeStringArray(std::move(v));
|
||||
}
|
||||
default:
|
||||
m_error = "invalid type when trying to read value";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool WireDecoder::ReadString(std::string* str) {
|
||||
size_t len;
|
||||
if (m_proto_rev < 0x0300u) {
|
||||
unsigned int v;
|
||||
if (!Read16(&v)) return false;
|
||||
len = v;
|
||||
} else {
|
||||
uint64_t v;
|
||||
if (!ReadUleb128(&v)) return false;
|
||||
len = v;
|
||||
}
|
||||
const char* buf;
|
||||
if (!Read(&buf, len)) return false;
|
||||
*str = llvm::StringRef(buf, len);
|
||||
return true;
|
||||
}
|
||||
158
ntcore/src/main/native/cpp/WireDecoder.h
Normal file
158
ntcore/src/main/native/cpp/WireDecoder.h
Normal file
@@ -0,0 +1,158 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_WIREDECODER_H_
|
||||
#define NTCORE_WIREDECODER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <support/leb128.h>
|
||||
#include <support/raw_istream.h>
|
||||
|
||||
#include "Log.h"
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
/* Decodes network data into native representation.
|
||||
* This class is designed to read from a raw_istream, which provides a blocking
|
||||
* read interface. There are no provisions in this class for resuming a read
|
||||
* that was interrupted partway. Read functions return false if
|
||||
* raw_istream.read() returned false (indicating the end of the input data
|
||||
* stream).
|
||||
*/
|
||||
class WireDecoder {
|
||||
public:
|
||||
WireDecoder(wpi::raw_istream& is, unsigned int proto_rev,
|
||||
wpi::Logger& logger);
|
||||
~WireDecoder();
|
||||
|
||||
void set_proto_rev(unsigned int proto_rev) { m_proto_rev = proto_rev; }
|
||||
|
||||
/* Get the active protocol revision. */
|
||||
unsigned int proto_rev() const { return m_proto_rev; }
|
||||
|
||||
/* Get the logger. */
|
||||
wpi::Logger& logger() const { return m_logger; }
|
||||
|
||||
/* Clears error indicator. */
|
||||
void Reset() { m_error = nullptr; }
|
||||
|
||||
/* Returns error indicator (a string describing the error). Returns nullptr
|
||||
* if no error has occurred.
|
||||
*/
|
||||
const char* error() const { return m_error; }
|
||||
|
||||
void set_error(const char* error) { m_error = error; }
|
||||
|
||||
/* Reads the specified number of bytes.
|
||||
* @param buf pointer to read data (output parameter)
|
||||
* @param len number of bytes to read
|
||||
* Caution: the buffer is only temporarily valid.
|
||||
*/
|
||||
bool Read(const char** buf, size_t len) {
|
||||
if (len > m_allocated) Realloc(len);
|
||||
*buf = m_buf;
|
||||
m_is.read(m_buf, len);
|
||||
#if 0
|
||||
if (m_logger.min_level() <= NT_LOG_DEBUG4 && m_logger.HasLogger()) {
|
||||
std::ostringstream oss;
|
||||
oss << "read " << len << " bytes:" << std::hex;
|
||||
if (!rv) {
|
||||
oss << "error";
|
||||
} else {
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
oss << ' ' << static_cast<unsigned int>((*buf)[i]);
|
||||
}
|
||||
m_logger.Log(NT_LOG_DEBUG4, __FILE__, __LINE__, oss.str().c_str());
|
||||
}
|
||||
#endif
|
||||
return !m_is.has_error();
|
||||
}
|
||||
|
||||
/* Reads a single byte. */
|
||||
bool Read8(unsigned int* val) {
|
||||
const char* buf;
|
||||
if (!Read(&buf, 1)) return false;
|
||||
*val = (*reinterpret_cast<const unsigned char*>(buf)) & 0xff;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Reads a 16-bit word. */
|
||||
bool Read16(unsigned int* val) {
|
||||
const char* buf;
|
||||
if (!Read(&buf, 2)) return false;
|
||||
unsigned int v = (*reinterpret_cast<const unsigned char*>(buf)) & 0xff;
|
||||
++buf;
|
||||
v <<= 8;
|
||||
v |= (*reinterpret_cast<const unsigned char*>(buf)) & 0xff;
|
||||
*val = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Reads a 32-bit word. */
|
||||
bool Read32(uint32_t* val) {
|
||||
const char* buf;
|
||||
if (!Read(&buf, 4)) return false;
|
||||
unsigned int v = (*reinterpret_cast<const unsigned char*>(buf)) & 0xff;
|
||||
++buf;
|
||||
v <<= 8;
|
||||
v |= (*reinterpret_cast<const unsigned char*>(buf)) & 0xff;
|
||||
++buf;
|
||||
v <<= 8;
|
||||
v |= (*reinterpret_cast<const unsigned char*>(buf)) & 0xff;
|
||||
++buf;
|
||||
v <<= 8;
|
||||
v |= (*reinterpret_cast<const unsigned char*>(buf)) & 0xff;
|
||||
*val = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Reads a double. */
|
||||
bool ReadDouble(double* val);
|
||||
|
||||
/* Reads an ULEB128-encoded unsigned integer. */
|
||||
bool ReadUleb128(uint64_t* val) { return wpi::ReadUleb128(m_is, val); }
|
||||
|
||||
bool ReadType(NT_Type* type);
|
||||
bool ReadString(std::string* str);
|
||||
std::shared_ptr<Value> ReadValue(NT_Type type);
|
||||
|
||||
WireDecoder(const WireDecoder&) = delete;
|
||||
WireDecoder& operator=(const WireDecoder&) = delete;
|
||||
|
||||
protected:
|
||||
/* The protocol revision. E.g. 0x0200 for version 2.0. */
|
||||
unsigned int m_proto_rev;
|
||||
|
||||
/* Error indicator. */
|
||||
const char* m_error;
|
||||
|
||||
private:
|
||||
/* Reallocate temporary buffer to specified length. */
|
||||
void Realloc(size_t len);
|
||||
|
||||
/* input stream */
|
||||
wpi::raw_istream& m_is;
|
||||
|
||||
/* logger */
|
||||
wpi::Logger& m_logger;
|
||||
|
||||
/* temporary buffer */
|
||||
char* m_buf;
|
||||
|
||||
/* allocated size of temporary buffer */
|
||||
size_t m_allocated;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_WIREDECODER_H_
|
||||
199
ntcore/src/main/native/cpp/WireEncoder.cpp
Normal file
199
ntcore/src/main/native/cpp/WireEncoder.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "WireEncoder.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <llvm/MathExtras.h>
|
||||
#include <support/leb128.h>
|
||||
|
||||
using namespace nt;
|
||||
|
||||
WireEncoder::WireEncoder(unsigned int proto_rev) {
|
||||
m_proto_rev = proto_rev;
|
||||
m_error = nullptr;
|
||||
}
|
||||
|
||||
void WireEncoder::WriteDouble(double val) {
|
||||
// The highest performance way to do this, albeit non-portable.
|
||||
uint64_t v = llvm::DoubleToBits(val);
|
||||
m_data.append(
|
||||
{static_cast<char>((v >> 56) & 0xff), static_cast<char>((v >> 48) & 0xff),
|
||||
static_cast<char>((v >> 40) & 0xff), static_cast<char>((v >> 32) & 0xff),
|
||||
static_cast<char>((v >> 24) & 0xff), static_cast<char>((v >> 16) & 0xff),
|
||||
static_cast<char>((v >> 8) & 0xff), static_cast<char>(v & 0xff)});
|
||||
}
|
||||
|
||||
void WireEncoder::WriteUleb128(uint32_t val) { wpi::WriteUleb128(m_data, val); }
|
||||
|
||||
void WireEncoder::WriteType(NT_Type type) {
|
||||
char ch;
|
||||
// Convert from enum to actual byte value.
|
||||
switch (type) {
|
||||
case NT_BOOLEAN:
|
||||
ch = 0x00;
|
||||
break;
|
||||
case NT_DOUBLE:
|
||||
ch = 0x01;
|
||||
break;
|
||||
case NT_STRING:
|
||||
ch = 0x02;
|
||||
break;
|
||||
case NT_RAW:
|
||||
if (m_proto_rev < 0x0300u) {
|
||||
m_error = "raw type not supported in protocol < 3.0";
|
||||
return;
|
||||
}
|
||||
ch = 0x03;
|
||||
break;
|
||||
case NT_BOOLEAN_ARRAY:
|
||||
ch = 0x10;
|
||||
break;
|
||||
case NT_DOUBLE_ARRAY:
|
||||
ch = 0x11;
|
||||
break;
|
||||
case NT_STRING_ARRAY:
|
||||
ch = 0x12;
|
||||
break;
|
||||
case NT_RPC:
|
||||
if (m_proto_rev < 0x0300u) {
|
||||
m_error = "RPC type not supported in protocol < 3.0";
|
||||
return;
|
||||
}
|
||||
ch = 0x20;
|
||||
break;
|
||||
default:
|
||||
m_error = "unrecognized type";
|
||||
return;
|
||||
}
|
||||
m_data.push_back(ch);
|
||||
}
|
||||
|
||||
size_t WireEncoder::GetValueSize(const Value& value) const {
|
||||
switch (value.type()) {
|
||||
case NT_BOOLEAN:
|
||||
return 1;
|
||||
case NT_DOUBLE:
|
||||
return 8;
|
||||
case NT_STRING:
|
||||
return GetStringSize(value.GetString());
|
||||
case NT_RAW:
|
||||
if (m_proto_rev < 0x0300u) return 0;
|
||||
return GetStringSize(value.GetRaw());
|
||||
case NT_RPC:
|
||||
if (m_proto_rev < 0x0300u) return 0;
|
||||
return GetStringSize(value.GetRpc());
|
||||
case NT_BOOLEAN_ARRAY: {
|
||||
// 1-byte size, 1 byte per element
|
||||
size_t size = value.GetBooleanArray().size();
|
||||
if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
|
||||
return 1 + size;
|
||||
}
|
||||
case NT_DOUBLE_ARRAY: {
|
||||
// 1-byte size, 8 bytes per element
|
||||
size_t size = value.GetDoubleArray().size();
|
||||
if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
|
||||
return 1 + size * 8;
|
||||
}
|
||||
case NT_STRING_ARRAY: {
|
||||
auto v = value.GetStringArray();
|
||||
size_t size = v.size();
|
||||
if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
|
||||
size_t len = 1; // 1-byte size
|
||||
for (size_t i = 0; i < size; ++i) len += GetStringSize(v[i]);
|
||||
return len;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void WireEncoder::WriteValue(const Value& value) {
|
||||
switch (value.type()) {
|
||||
case NT_BOOLEAN:
|
||||
Write8(value.GetBoolean() ? 1 : 0);
|
||||
break;
|
||||
case NT_DOUBLE:
|
||||
WriteDouble(value.GetDouble());
|
||||
break;
|
||||
case NT_STRING:
|
||||
WriteString(value.GetString());
|
||||
break;
|
||||
case NT_RAW:
|
||||
if (m_proto_rev < 0x0300u) {
|
||||
m_error = "raw values not supported in protocol < 3.0";
|
||||
return;
|
||||
}
|
||||
WriteString(value.GetRaw());
|
||||
break;
|
||||
case NT_RPC:
|
||||
if (m_proto_rev < 0x0300u) {
|
||||
m_error = "RPC values not supported in protocol < 3.0";
|
||||
return;
|
||||
}
|
||||
WriteString(value.GetRpc());
|
||||
break;
|
||||
case NT_BOOLEAN_ARRAY: {
|
||||
auto v = value.GetBooleanArray();
|
||||
size_t size = v.size();
|
||||
if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
|
||||
Write8(size);
|
||||
|
||||
for (size_t i = 0; i < size; ++i) Write8(v[i] ? 1 : 0);
|
||||
break;
|
||||
}
|
||||
case NT_DOUBLE_ARRAY: {
|
||||
auto v = value.GetDoubleArray();
|
||||
size_t size = v.size();
|
||||
if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
|
||||
Write8(size);
|
||||
|
||||
for (size_t i = 0; i < size; ++i) WriteDouble(v[i]);
|
||||
break;
|
||||
}
|
||||
case NT_STRING_ARRAY: {
|
||||
auto v = value.GetStringArray();
|
||||
size_t size = v.size();
|
||||
if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
|
||||
Write8(size);
|
||||
|
||||
for (size_t i = 0; i < size; ++i) WriteString(v[i]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
m_error = "unrecognized type when writing value";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
size_t WireEncoder::GetStringSize(llvm::StringRef str) const {
|
||||
if (m_proto_rev < 0x0300u) {
|
||||
size_t len = str.size();
|
||||
if (len > 0xffff) len = 0xffff; // Limited to 64K length; truncate
|
||||
return 2 + len;
|
||||
}
|
||||
return wpi::SizeUleb128(str.size()) + str.size();
|
||||
}
|
||||
|
||||
void WireEncoder::WriteString(llvm::StringRef str) {
|
||||
// length
|
||||
size_t len = str.size();
|
||||
if (m_proto_rev < 0x0300u) {
|
||||
if (len > 0xffff) len = 0xffff; // Limited to 64K length; truncate
|
||||
Write16(len);
|
||||
} else {
|
||||
WriteUleb128(len);
|
||||
}
|
||||
|
||||
// contents
|
||||
m_data.append(str.data(), str.data() + len);
|
||||
}
|
||||
111
ntcore/src/main/native/cpp/WireEncoder.h
Normal file
111
ntcore/src/main/native/cpp/WireEncoder.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_WIREENCODER_H_
|
||||
#define NTCORE_WIREENCODER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
|
||||
#include <llvm/SmallVector.h>
|
||||
#include <llvm/StringRef.h>
|
||||
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
/* Encodes native data for network transmission.
|
||||
* This class maintains an internal memory buffer for written data so that
|
||||
* it can be efficiently bursted to the network after a number of writes
|
||||
* have been performed. For this reason, all operations are non-blocking.
|
||||
*/
|
||||
class WireEncoder {
|
||||
public:
|
||||
explicit WireEncoder(unsigned int proto_rev);
|
||||
|
||||
/* Change the protocol revision (mostly affects value encoding). */
|
||||
void set_proto_rev(unsigned int proto_rev) { m_proto_rev = proto_rev; }
|
||||
|
||||
/* Get the active protocol revision. */
|
||||
unsigned int proto_rev() const { return m_proto_rev; }
|
||||
|
||||
/* Clears buffer and error indicator. */
|
||||
void Reset() {
|
||||
m_data.clear();
|
||||
m_error = nullptr;
|
||||
}
|
||||
|
||||
/* Returns error indicator (a string describing the error). Returns nullptr
|
||||
* if no error has occurred.
|
||||
*/
|
||||
const char* error() const { return m_error; }
|
||||
|
||||
/* Returns pointer to start of memory buffer with written data. */
|
||||
const char* data() const { return m_data.data(); }
|
||||
|
||||
/* Returns number of bytes written to memory buffer. */
|
||||
size_t size() const { return m_data.size(); }
|
||||
|
||||
llvm::StringRef ToStringRef() const {
|
||||
return llvm::StringRef(m_data.data(), m_data.size());
|
||||
}
|
||||
|
||||
/* Writes a single byte. */
|
||||
void Write8(unsigned int val) {
|
||||
m_data.push_back(static_cast<char>(val & 0xff));
|
||||
}
|
||||
|
||||
/* Writes a 16-bit word. */
|
||||
void Write16(unsigned int val) {
|
||||
m_data.append(
|
||||
{static_cast<char>((val >> 8) & 0xff), static_cast<char>(val & 0xff)});
|
||||
}
|
||||
|
||||
/* Writes a 32-bit word. */
|
||||
void Write32(uint32_t val) {
|
||||
m_data.append({static_cast<char>((val >> 24) & 0xff),
|
||||
static_cast<char>((val >> 16) & 0xff),
|
||||
static_cast<char>((val >> 8) & 0xff),
|
||||
static_cast<char>(val & 0xff)});
|
||||
}
|
||||
|
||||
/* Writes a double. */
|
||||
void WriteDouble(double val);
|
||||
|
||||
/* Writes an ULEB128-encoded unsigned integer. */
|
||||
void WriteUleb128(uint32_t val);
|
||||
|
||||
void WriteType(NT_Type type);
|
||||
void WriteValue(const Value& value);
|
||||
void WriteString(llvm::StringRef str);
|
||||
|
||||
/* Utility function to get the written size of a value (without actually
|
||||
* writing it).
|
||||
*/
|
||||
size_t GetValueSize(const Value& value) const;
|
||||
|
||||
/* Utility function to get the written size of a string (without actually
|
||||
* writing it).
|
||||
*/
|
||||
size_t GetStringSize(llvm::StringRef str) const;
|
||||
|
||||
protected:
|
||||
/* The protocol revision. E.g. 0x0200 for version 2.0. */
|
||||
unsigned int m_proto_rev;
|
||||
|
||||
/* Error indicator. */
|
||||
const char* m_error;
|
||||
|
||||
private:
|
||||
llvm::SmallVector<char, 256> m_data;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_WIREENCODER_H_
|
||||
1771
ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp
Normal file
1771
ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp
Normal file
File diff suppressed because it is too large
Load Diff
527
ntcore/src/main/native/cpp/networktables/NetworkTable.cpp
Normal file
527
ntcore/src/main/native/cpp/networktables/NetworkTable.cpp
Normal file
@@ -0,0 +1,527 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "networktables/NetworkTable.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <llvm/SmallString.h>
|
||||
#include <llvm/StringMap.h>
|
||||
#include <llvm/raw_ostream.h>
|
||||
|
||||
#include "networktables/NetworkTableInstance.h"
|
||||
#include "ntcore.h"
|
||||
#include "tables/ITableListener.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
const char NetworkTable::PATH_SEPARATOR_CHAR = '/';
|
||||
std::string NetworkTable::s_persistent_filename = "networktables.ini";
|
||||
bool NetworkTable::s_client = false;
|
||||
bool NetworkTable::s_enable_ds = true;
|
||||
bool NetworkTable::s_running = false;
|
||||
unsigned int NetworkTable::s_port = NT_DEFAULT_PORT;
|
||||
|
||||
StringRef NetworkTable::BasenameKey(StringRef key) {
|
||||
size_t slash = key.rfind(PATH_SEPARATOR_CHAR);
|
||||
if (slash == StringRef::npos) return key;
|
||||
return key.substr(slash + 1);
|
||||
}
|
||||
|
||||
std::string NetworkTable::NormalizeKey(const Twine& key,
|
||||
bool withLeadingSlash) {
|
||||
llvm::SmallString<128> buf;
|
||||
return NormalizeKey(key, buf, withLeadingSlash);
|
||||
}
|
||||
|
||||
StringRef NetworkTable::NormalizeKey(const Twine& key,
|
||||
llvm::SmallVectorImpl<char>& buf,
|
||||
bool withLeadingSlash) {
|
||||
buf.clear();
|
||||
if (withLeadingSlash) buf.push_back(PATH_SEPARATOR_CHAR);
|
||||
// for each path element, add it with a slash following
|
||||
llvm::SmallString<128> keyBuf;
|
||||
StringRef keyStr = key.toStringRef(keyBuf);
|
||||
llvm::SmallVector<StringRef, 16> parts;
|
||||
keyStr.split(parts, PATH_SEPARATOR_CHAR, -1, false);
|
||||
for (auto i = parts.begin(); i != parts.end(); ++i) {
|
||||
buf.append(i->begin(), i->end());
|
||||
buf.push_back(PATH_SEPARATOR_CHAR);
|
||||
}
|
||||
// remove trailing slash if the input key didn't have one
|
||||
if (!keyStr.empty() && keyStr.back() != PATH_SEPARATOR_CHAR) buf.pop_back();
|
||||
return StringRef(buf.data(), buf.size());
|
||||
}
|
||||
|
||||
std::vector<std::string> NetworkTable::GetHierarchy(const Twine& key) {
|
||||
std::vector<std::string> hierarchy;
|
||||
hierarchy.emplace_back(1, PATH_SEPARATOR_CHAR);
|
||||
// for each path element, add it to the end of what we built previously
|
||||
llvm::SmallString<128> keyBuf;
|
||||
StringRef keyStr = key.toStringRef(keyBuf);
|
||||
llvm::SmallString<128> path;
|
||||
llvm::SmallVector<StringRef, 16> parts;
|
||||
keyStr.split(parts, PATH_SEPARATOR_CHAR, -1, false);
|
||||
if (!parts.empty()) {
|
||||
for (auto i = parts.begin(); i != parts.end(); ++i) {
|
||||
path += PATH_SEPARATOR_CHAR;
|
||||
path += *i;
|
||||
hierarchy.emplace_back(path.str());
|
||||
}
|
||||
// handle trailing slash
|
||||
if (keyStr.back() == PATH_SEPARATOR_CHAR) {
|
||||
path += PATH_SEPARATOR_CHAR;
|
||||
hierarchy.emplace_back(path.str());
|
||||
}
|
||||
}
|
||||
return hierarchy;
|
||||
}
|
||||
|
||||
void NetworkTable::Initialize() {
|
||||
if (s_running) Shutdown();
|
||||
auto inst = NetworkTableInstance::GetDefault();
|
||||
if (s_client) {
|
||||
inst.StartClient();
|
||||
if (s_enable_ds) inst.StartDSClient(s_port);
|
||||
} else {
|
||||
inst.StartServer(s_persistent_filename, "", s_port);
|
||||
}
|
||||
s_running = true;
|
||||
}
|
||||
|
||||
void NetworkTable::Shutdown() {
|
||||
if (!s_running) return;
|
||||
auto inst = NetworkTableInstance::GetDefault();
|
||||
if (s_client) {
|
||||
inst.StopDSClient();
|
||||
inst.StopClient();
|
||||
} else {
|
||||
inst.StopServer();
|
||||
}
|
||||
s_running = false;
|
||||
}
|
||||
|
||||
void NetworkTable::SetClientMode() { s_client = true; }
|
||||
|
||||
void NetworkTable::SetServerMode() { s_client = false; }
|
||||
|
||||
void NetworkTable::SetTeam(int team) {
|
||||
auto inst = NetworkTableInstance::GetDefault();
|
||||
inst.SetServerTeam(team, s_port);
|
||||
if (s_enable_ds) inst.StartDSClient(s_port);
|
||||
}
|
||||
|
||||
void NetworkTable::SetIPAddress(StringRef address) {
|
||||
auto inst = NetworkTableInstance::GetDefault();
|
||||
llvm::SmallString<32> addr_copy{address};
|
||||
inst.SetServer(addr_copy.c_str(), s_port);
|
||||
|
||||
// Stop the DS client if we're explicitly connecting to localhost
|
||||
if (address == "localhost" || address == "127.0.0.1")
|
||||
inst.StopDSClient();
|
||||
else if (s_enable_ds)
|
||||
inst.StartDSClient(s_port);
|
||||
}
|
||||
|
||||
void NetworkTable::SetIPAddress(ArrayRef<std::string> addresses) {
|
||||
auto inst = NetworkTableInstance::GetDefault();
|
||||
llvm::SmallVector<StringRef, 8> servers;
|
||||
for (const auto& ip_address : addresses) servers.emplace_back(ip_address);
|
||||
inst.SetServer(servers, s_port);
|
||||
|
||||
// Stop the DS client if we're explicitly connecting to localhost
|
||||
if (!addresses.empty() &&
|
||||
(addresses[0] == "localhost" || addresses[0] == "127.0.0.1"))
|
||||
inst.StopDSClient();
|
||||
else if (s_enable_ds)
|
||||
inst.StartDSClient(s_port);
|
||||
}
|
||||
|
||||
void NetworkTable::SetPort(unsigned int port) { s_port = port; }
|
||||
|
||||
void NetworkTable::SetDSClientEnabled(bool enabled) {
|
||||
auto inst = NetworkTableInstance::GetDefault();
|
||||
s_enable_ds = enabled;
|
||||
if (s_enable_ds)
|
||||
inst.StartDSClient(s_port);
|
||||
else
|
||||
inst.StopDSClient();
|
||||
}
|
||||
|
||||
void NetworkTable::SetPersistentFilename(StringRef filename) {
|
||||
s_persistent_filename = filename;
|
||||
}
|
||||
|
||||
void NetworkTable::SetNetworkIdentity(StringRef name) {
|
||||
NetworkTableInstance::GetDefault().SetNetworkIdentity(name);
|
||||
}
|
||||
|
||||
void NetworkTable::GlobalDeleteAll() {
|
||||
NetworkTableInstance::GetDefault().DeleteAllEntries();
|
||||
}
|
||||
|
||||
void NetworkTable::Flush() { NetworkTableInstance::GetDefault().Flush(); }
|
||||
|
||||
void NetworkTable::SetUpdateRate(double interval) {
|
||||
NetworkTableInstance::GetDefault().SetUpdateRate(interval);
|
||||
}
|
||||
|
||||
const char* NetworkTable::SavePersistent(StringRef filename) {
|
||||
return NetworkTableInstance::GetDefault().SavePersistent(filename);
|
||||
}
|
||||
|
||||
const char* NetworkTable::LoadPersistent(
|
||||
StringRef filename,
|
||||
std::function<void(size_t line, const char* msg)> warn) {
|
||||
return NetworkTableInstance::GetDefault().LoadPersistent(filename, warn);
|
||||
}
|
||||
|
||||
std::shared_ptr<NetworkTable> NetworkTable::GetTable(StringRef key) {
|
||||
if (!s_running) Initialize();
|
||||
return NetworkTableInstance::GetDefault().GetTable(key);
|
||||
}
|
||||
|
||||
NetworkTable::NetworkTable(NT_Inst inst, const Twine& path, const private_init&)
|
||||
: m_inst(inst), m_path(path.str()) {}
|
||||
|
||||
NetworkTable::~NetworkTable() {
|
||||
for (auto& i : m_listeners) RemoveEntryListener(i.second);
|
||||
}
|
||||
|
||||
NetworkTableInstance NetworkTable::GetInstance() const {
|
||||
return NetworkTableInstance{m_inst};
|
||||
}
|
||||
|
||||
NetworkTableEntry NetworkTable::GetEntry(const Twine& key) const {
|
||||
llvm::SmallString<128> keyBuf;
|
||||
StringRef keyStr = key.toStringRef(keyBuf);
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
NT_Entry& entry = m_entries[keyStr];
|
||||
if (entry == 0) {
|
||||
entry = nt::GetEntry(m_inst, m_path + Twine(PATH_SEPARATOR_CHAR) + keyStr);
|
||||
}
|
||||
return NetworkTableEntry{entry};
|
||||
}
|
||||
|
||||
NT_EntryListener NetworkTable::AddEntryListener(TableEntryListener listener,
|
||||
unsigned int flags) const {
|
||||
size_t prefix_len = m_path.size() + 1;
|
||||
return nt::AddEntryListener(
|
||||
m_inst, m_path + Twine(PATH_SEPARATOR_CHAR),
|
||||
[=](const EntryNotification& event) {
|
||||
StringRef relative_key = event.name.substr(prefix_len);
|
||||
if (relative_key.find(PATH_SEPARATOR_CHAR) != StringRef::npos) return;
|
||||
listener(const_cast<NetworkTable*>(this), relative_key,
|
||||
NetworkTableEntry{event.entry}, event.value, event.flags);
|
||||
},
|
||||
flags);
|
||||
}
|
||||
|
||||
NT_EntryListener NetworkTable::AddEntryListener(const Twine& key,
|
||||
TableEntryListener listener,
|
||||
unsigned int flags) const {
|
||||
size_t prefix_len = m_path.size() + 1;
|
||||
auto entry = GetEntry(key);
|
||||
return nt::AddEntryListener(entry.GetHandle(),
|
||||
[=](const EntryNotification& event) {
|
||||
listener(const_cast<NetworkTable*>(this),
|
||||
event.name.substr(prefix_len), entry,
|
||||
event.value, event.flags);
|
||||
},
|
||||
flags);
|
||||
}
|
||||
|
||||
void NetworkTable::RemoveEntryListener(NT_EntryListener listener) const {
|
||||
nt::RemoveEntryListener(listener);
|
||||
}
|
||||
|
||||
void NetworkTable::AddTableListener(ITableListener* listener) {
|
||||
AddTableListenerEx(listener, NT_NOTIFY_NEW | NT_NOTIFY_UPDATE);
|
||||
}
|
||||
|
||||
void NetworkTable::AddTableListener(ITableListener* listener,
|
||||
bool immediateNotify) {
|
||||
unsigned int flags = NT_NOTIFY_NEW | NT_NOTIFY_UPDATE;
|
||||
if (immediateNotify) flags |= NT_NOTIFY_IMMEDIATE;
|
||||
AddTableListenerEx(listener, flags);
|
||||
}
|
||||
|
||||
void NetworkTable::AddTableListenerEx(ITableListener* listener,
|
||||
unsigned int flags) {
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
llvm::SmallString<128> path(m_path);
|
||||
path += PATH_SEPARATOR_CHAR;
|
||||
size_t prefix_len = path.size();
|
||||
NT_EntryListener id = nt::AddEntryListener(
|
||||
m_inst, path,
|
||||
[=](const EntryNotification& event) {
|
||||
StringRef relative_key = event.name.substr(prefix_len);
|
||||
if (relative_key.find(PATH_SEPARATOR_CHAR) != StringRef::npos) return;
|
||||
listener->ValueChangedEx(this, relative_key, event.value, event.flags);
|
||||
},
|
||||
flags);
|
||||
m_listeners.emplace_back(listener, id);
|
||||
}
|
||||
|
||||
void NetworkTable::AddTableListener(StringRef key, ITableListener* listener,
|
||||
bool immediateNotify) {
|
||||
unsigned int flags = NT_NOTIFY_NEW | NT_NOTIFY_UPDATE;
|
||||
if (immediateNotify) flags |= NT_NOTIFY_IMMEDIATE;
|
||||
AddTableListenerEx(key, listener, flags);
|
||||
}
|
||||
|
||||
void NetworkTable::AddTableListenerEx(StringRef key, ITableListener* listener,
|
||||
unsigned int flags) {
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
size_t prefix_len = m_path.size() + 1;
|
||||
auto entry = GetEntry(key);
|
||||
NT_EntryListener id = nt::AddEntryListener(
|
||||
entry.GetHandle(),
|
||||
[=](const EntryNotification& event) {
|
||||
listener->ValueChangedEx(this, event.name.substr(prefix_len),
|
||||
event.value, event.flags);
|
||||
},
|
||||
flags);
|
||||
m_listeners.emplace_back(listener, id);
|
||||
}
|
||||
|
||||
void NetworkTable::AddSubTableListener(ITableListener* listener) {
|
||||
AddSubTableListener(listener, false);
|
||||
}
|
||||
|
||||
void NetworkTable::AddSubTableListener(ITableListener* listener,
|
||||
bool localNotify) {
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
size_t prefix_len = m_path.size() + 1;
|
||||
|
||||
// The lambda needs to be copyable, but StringMap is not, so use
|
||||
// a shared_ptr to it.
|
||||
auto notified_tables = std::make_shared<llvm::StringMap<char>>();
|
||||
|
||||
unsigned int flags = NT_NOTIFY_NEW | NT_NOTIFY_IMMEDIATE;
|
||||
if (localNotify) flags |= NT_NOTIFY_LOCAL;
|
||||
NT_EntryListener id = nt::AddEntryListener(
|
||||
m_inst, m_path + Twine(PATH_SEPARATOR_CHAR),
|
||||
[=](const EntryNotification& event) {
|
||||
StringRef relative_key = event.name.substr(prefix_len);
|
||||
auto end_sub_table = relative_key.find(PATH_SEPARATOR_CHAR);
|
||||
if (end_sub_table == StringRef::npos) return;
|
||||
StringRef sub_table_key = relative_key.substr(0, end_sub_table);
|
||||
if (notified_tables->find(sub_table_key) == notified_tables->end())
|
||||
return;
|
||||
notified_tables->insert(std::make_pair(sub_table_key, '\0'));
|
||||
listener->ValueChangedEx(this, sub_table_key, nullptr, event.flags);
|
||||
},
|
||||
flags);
|
||||
m_listeners.emplace_back(listener, id);
|
||||
}
|
||||
|
||||
void NetworkTable::RemoveTableListener(ITableListener* listener) {
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto matches_begin =
|
||||
std::remove_if(m_listeners.begin(), m_listeners.end(),
|
||||
[=](const Listener& x) { return x.first == listener; });
|
||||
|
||||
for (auto i = matches_begin; i != m_listeners.end(); ++i)
|
||||
RemoveEntryListener(i->second);
|
||||
m_listeners.erase(matches_begin, m_listeners.end());
|
||||
}
|
||||
|
||||
std::shared_ptr<NetworkTable> NetworkTable::GetSubTable(
|
||||
const Twine& key) const {
|
||||
return std::make_shared<NetworkTable>(
|
||||
m_inst, m_path + Twine(PATH_SEPARATOR_CHAR) + key, private_init{});
|
||||
}
|
||||
|
||||
bool NetworkTable::ContainsKey(const Twine& key) const {
|
||||
if (key.isTriviallyEmpty() ||
|
||||
(key.isSingleStringRef() && key.getSingleStringRef().empty()))
|
||||
return false;
|
||||
return GetEntry(key).Exists();
|
||||
}
|
||||
|
||||
bool NetworkTable::ContainsSubTable(const Twine& key) const {
|
||||
return !GetEntryInfo(m_inst,
|
||||
m_path + Twine(PATH_SEPARATOR_CHAR) + key +
|
||||
Twine(PATH_SEPARATOR_CHAR),
|
||||
0)
|
||||
.empty();
|
||||
}
|
||||
|
||||
std::vector<std::string> NetworkTable::GetKeys(int types) const {
|
||||
std::vector<std::string> keys;
|
||||
size_t prefix_len = m_path.size() + 1;
|
||||
auto infos = GetEntryInfo(m_inst, m_path + Twine(PATH_SEPARATOR_CHAR), types);
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
for (auto& info : infos) {
|
||||
auto relative_key = StringRef(info.name).substr(prefix_len);
|
||||
if (relative_key.find(PATH_SEPARATOR_CHAR) != StringRef::npos) continue;
|
||||
keys.push_back(relative_key);
|
||||
m_entries[relative_key] = info.entry;
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
std::vector<std::string> NetworkTable::GetSubTables() const {
|
||||
std::vector<std::string> keys;
|
||||
size_t prefix_len = m_path.size() + 1;
|
||||
for (auto& entry :
|
||||
GetEntryInfo(m_inst, m_path + Twine(PATH_SEPARATOR_CHAR), 0)) {
|
||||
auto relative_key = StringRef(entry.name).substr(prefix_len);
|
||||
size_t end_subtable = relative_key.find(PATH_SEPARATOR_CHAR);
|
||||
if (end_subtable == StringRef::npos) continue;
|
||||
keys.push_back(relative_key.substr(0, end_subtable));
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
void NetworkTable::SetPersistent(StringRef key) {
|
||||
GetEntry(key).SetPersistent();
|
||||
}
|
||||
|
||||
void NetworkTable::ClearPersistent(StringRef key) {
|
||||
GetEntry(key).ClearPersistent();
|
||||
}
|
||||
|
||||
bool NetworkTable::IsPersistent(StringRef key) const {
|
||||
return GetEntry(key).IsPersistent();
|
||||
}
|
||||
|
||||
void NetworkTable::SetFlags(StringRef key, unsigned int flags) {
|
||||
GetEntry(key).SetFlags(flags);
|
||||
}
|
||||
|
||||
void NetworkTable::ClearFlags(StringRef key, unsigned int flags) {
|
||||
GetEntry(key).ClearFlags(flags);
|
||||
}
|
||||
|
||||
unsigned int NetworkTable::GetFlags(StringRef key) const {
|
||||
return GetEntry(key).GetFlags();
|
||||
}
|
||||
|
||||
void NetworkTable::Delete(const Twine& key) { GetEntry(key).Delete(); }
|
||||
|
||||
bool NetworkTable::PutNumber(StringRef key, double value) {
|
||||
return GetEntry(key).SetDouble(value);
|
||||
}
|
||||
|
||||
bool NetworkTable::SetDefaultNumber(StringRef key, double defaultValue) {
|
||||
return GetEntry(key).SetDefaultDouble(defaultValue);
|
||||
}
|
||||
|
||||
double NetworkTable::GetNumber(StringRef key, double defaultValue) const {
|
||||
return GetEntry(key).GetDouble(defaultValue);
|
||||
}
|
||||
|
||||
bool NetworkTable::PutString(StringRef key, StringRef value) {
|
||||
return GetEntry(key).SetString(value);
|
||||
}
|
||||
|
||||
bool NetworkTable::SetDefaultString(StringRef key, StringRef defaultValue) {
|
||||
return GetEntry(key).SetDefaultString(defaultValue);
|
||||
}
|
||||
|
||||
std::string NetworkTable::GetString(StringRef key,
|
||||
StringRef defaultValue) const {
|
||||
return GetEntry(key).GetString(defaultValue);
|
||||
}
|
||||
|
||||
bool NetworkTable::PutBoolean(StringRef key, bool value) {
|
||||
return GetEntry(key).SetBoolean(value);
|
||||
}
|
||||
|
||||
bool NetworkTable::SetDefaultBoolean(StringRef key, bool defaultValue) {
|
||||
return GetEntry(key).SetDefaultBoolean(defaultValue);
|
||||
}
|
||||
|
||||
bool NetworkTable::GetBoolean(StringRef key, bool defaultValue) const {
|
||||
return GetEntry(key).GetBoolean(defaultValue);
|
||||
}
|
||||
|
||||
bool NetworkTable::PutBooleanArray(StringRef key, ArrayRef<int> value) {
|
||||
return GetEntry(key).SetBooleanArray(value);
|
||||
}
|
||||
|
||||
bool NetworkTable::SetDefaultBooleanArray(StringRef key,
|
||||
ArrayRef<int> defaultValue) {
|
||||
return GetEntry(key).SetDefaultBooleanArray(defaultValue);
|
||||
}
|
||||
|
||||
std::vector<int> NetworkTable::GetBooleanArray(
|
||||
StringRef key, ArrayRef<int> defaultValue) const {
|
||||
return GetEntry(key).GetBooleanArray(defaultValue);
|
||||
}
|
||||
|
||||
bool NetworkTable::PutNumberArray(StringRef key, ArrayRef<double> value) {
|
||||
return GetEntry(key).SetDoubleArray(value);
|
||||
}
|
||||
|
||||
bool NetworkTable::SetDefaultNumberArray(StringRef key,
|
||||
ArrayRef<double> defaultValue) {
|
||||
return GetEntry(key).SetDefaultDoubleArray(defaultValue);
|
||||
}
|
||||
|
||||
std::vector<double> NetworkTable::GetNumberArray(
|
||||
StringRef key, ArrayRef<double> defaultValue) const {
|
||||
return GetEntry(key).GetDoubleArray(defaultValue);
|
||||
}
|
||||
|
||||
bool NetworkTable::PutStringArray(StringRef key, ArrayRef<std::string> value) {
|
||||
return GetEntry(key).SetStringArray(value);
|
||||
}
|
||||
|
||||
bool NetworkTable::SetDefaultStringArray(StringRef key,
|
||||
ArrayRef<std::string> defaultValue) {
|
||||
return GetEntry(key).SetDefaultStringArray(defaultValue);
|
||||
}
|
||||
|
||||
std::vector<std::string> NetworkTable::GetStringArray(
|
||||
StringRef key, ArrayRef<std::string> defaultValue) const {
|
||||
return GetEntry(key).GetStringArray(defaultValue);
|
||||
}
|
||||
|
||||
bool NetworkTable::PutRaw(StringRef key, StringRef value) {
|
||||
return GetEntry(key).SetRaw(value);
|
||||
}
|
||||
|
||||
bool NetworkTable::SetDefaultRaw(StringRef key, StringRef defaultValue) {
|
||||
return GetEntry(key).SetDefaultRaw(defaultValue);
|
||||
}
|
||||
|
||||
std::string NetworkTable::GetRaw(StringRef key, StringRef defaultValue) const {
|
||||
return GetEntry(key).GetRaw(defaultValue);
|
||||
}
|
||||
|
||||
bool NetworkTable::PutValue(const Twine& key, std::shared_ptr<Value> value) {
|
||||
return GetEntry(key).SetValue(value);
|
||||
}
|
||||
|
||||
bool NetworkTable::SetDefaultValue(const Twine& key,
|
||||
std::shared_ptr<Value> defaultValue) {
|
||||
return GetEntry(key).SetDefaultValue(defaultValue);
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> NetworkTable::GetValue(const Twine& key) const {
|
||||
return GetEntry(key).GetValue();
|
||||
}
|
||||
|
||||
StringRef NetworkTable::GetPath() const { return m_path; }
|
||||
|
||||
const char* NetworkTable::SaveEntries(const Twine& filename) const {
|
||||
return nt::SaveEntries(m_inst, filename, m_path + Twine(PATH_SEPARATOR_CHAR));
|
||||
}
|
||||
|
||||
const char* NetworkTable::LoadEntries(
|
||||
const Twine& filename,
|
||||
std::function<void(size_t line, const char* msg)> warn) {
|
||||
return nt::LoadEntries(m_inst, filename, m_path + Twine(PATH_SEPARATOR_CHAR),
|
||||
warn);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "networktables/NetworkTableEntry.h"
|
||||
|
||||
#include "networktables/NetworkTableInstance.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
NetworkTableInstance NetworkTableEntry::GetInstance() const {
|
||||
return NetworkTableInstance{GetInstanceFromHandle(m_handle)};
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "networktables/NetworkTableInstance.h"
|
||||
|
||||
#include <llvm/SmallString.h>
|
||||
|
||||
using namespace nt;
|
||||
|
||||
std::shared_ptr<NetworkTable> NetworkTableInstance::GetTable(
|
||||
const Twine& key) const {
|
||||
StringRef simple;
|
||||
bool isSimple = key.isSingleStringRef();
|
||||
if (isSimple) simple = key.getSingleStringRef();
|
||||
if (isSimple && (simple.empty() || simple == "/")) {
|
||||
return std::make_shared<NetworkTable>(m_handle, "",
|
||||
NetworkTable::private_init{});
|
||||
} else if (isSimple && simple[0] == NetworkTable::PATH_SEPARATOR_CHAR) {
|
||||
return std::make_shared<NetworkTable>(m_handle, key,
|
||||
NetworkTable::private_init{});
|
||||
} else {
|
||||
return std::make_shared<NetworkTable>(
|
||||
m_handle, Twine(NetworkTable::PATH_SEPARATOR_CHAR) + key,
|
||||
NetworkTable::private_init{});
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkTableInstance::StartClient(ArrayRef<StringRef> servers,
|
||||
unsigned int port) {
|
||||
llvm::SmallVector<std::pair<StringRef, unsigned int>, 8> server_ports;
|
||||
for (const auto& server : servers)
|
||||
server_ports.emplace_back(std::make_pair(server, port));
|
||||
StartClient(server_ports);
|
||||
}
|
||||
|
||||
void NetworkTableInstance::SetServer(ArrayRef<StringRef> servers,
|
||||
unsigned int port) {
|
||||
llvm::SmallVector<std::pair<StringRef, unsigned int>, 8> server_ports;
|
||||
for (const auto& server : servers)
|
||||
server_ports.emplace_back(std::make_pair(server, port));
|
||||
SetServer(server_ports);
|
||||
}
|
||||
|
||||
NT_EntryListener NetworkTableInstance::AddEntryListener(
|
||||
const Twine& prefix,
|
||||
std::function<void(const EntryNotification& event)> callback,
|
||||
unsigned int flags) const {
|
||||
return ::nt::AddEntryListener(m_handle, prefix, callback, flags);
|
||||
}
|
||||
|
||||
NT_ConnectionListener NetworkTableInstance::AddConnectionListener(
|
||||
std::function<void(const ConnectionNotification& event)> callback,
|
||||
bool immediate_notify) const {
|
||||
return ::nt::AddConnectionListener(m_handle, callback, immediate_notify);
|
||||
}
|
||||
16
ntcore/src/main/native/cpp/networktables/RpcCall.cpp
Normal file
16
ntcore/src/main/native/cpp/networktables/RpcCall.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "networktables/RpcCall.h"
|
||||
|
||||
#include "networktables/NetworkTableEntry.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
NetworkTableEntry RpcCall::GetEntry() const {
|
||||
return NetworkTableEntry{m_entry};
|
||||
}
|
||||
1165
ntcore/src/main/native/cpp/ntcore_c.cpp
Normal file
1165
ntcore/src/main/native/cpp/ntcore_c.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1062
ntcore/src/main/native/cpp/ntcore_cpp.cpp
Normal file
1062
ntcore/src/main/native/cpp/ntcore_cpp.cpp
Normal file
File diff suppressed because it is too large
Load Diff
244
ntcore/src/main/native/cpp/ntcore_test.cpp
Normal file
244
ntcore/src/main/native/cpp/ntcore_test.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "ntcore_test.h"
|
||||
|
||||
#include "Value_internal.h"
|
||||
|
||||
extern "C" {
|
||||
struct NT_String* NT_GetStringForTesting(const char* string, int* struct_size) {
|
||||
struct NT_String* str =
|
||||
static_cast<NT_String*>(std::calloc(1, sizeof(NT_String)));
|
||||
nt::ConvertToC(llvm::StringRef(string), str);
|
||||
*struct_size = sizeof(NT_String);
|
||||
return str;
|
||||
}
|
||||
|
||||
struct NT_EntryInfo* NT_GetEntryInfoForTesting(const char* name,
|
||||
enum NT_Type type,
|
||||
unsigned int flags,
|
||||
uint64_t last_change,
|
||||
int* struct_size) {
|
||||
struct NT_EntryInfo* entry_info =
|
||||
static_cast<NT_EntryInfo*>(std::calloc(1, sizeof(NT_EntryInfo)));
|
||||
nt::ConvertToC(llvm::StringRef(name), &entry_info->name);
|
||||
entry_info->type = type;
|
||||
entry_info->flags = flags;
|
||||
entry_info->last_change = last_change;
|
||||
*struct_size = sizeof(NT_EntryInfo);
|
||||
return entry_info;
|
||||
}
|
||||
|
||||
void NT_FreeEntryInfoForTesting(struct NT_EntryInfo* info) {
|
||||
std::free(info->name.str);
|
||||
std::free(info);
|
||||
}
|
||||
|
||||
struct NT_ConnectionInfo* NT_GetConnectionInfoForTesting(
|
||||
const char* remote_id, const char* remote_ip, unsigned int remote_port,
|
||||
uint64_t last_update, unsigned int protocol_version, int* struct_size) {
|
||||
struct NT_ConnectionInfo* conn_info = static_cast<NT_ConnectionInfo*>(
|
||||
std::calloc(1, sizeof(NT_ConnectionInfo)));
|
||||
nt::ConvertToC(llvm::StringRef(remote_id), &conn_info->remote_id);
|
||||
nt::ConvertToC(llvm::StringRef(remote_ip), &conn_info->remote_ip);
|
||||
conn_info->remote_port = remote_port;
|
||||
conn_info->last_update = last_update;
|
||||
conn_info->protocol_version = protocol_version;
|
||||
*struct_size = sizeof(NT_ConnectionInfo);
|
||||
return conn_info;
|
||||
}
|
||||
|
||||
void NT_FreeConnectionInfoForTesting(struct NT_ConnectionInfo* info) {
|
||||
std::free(info->remote_id.str);
|
||||
std::free(info->remote_ip.str);
|
||||
std::free(info);
|
||||
}
|
||||
|
||||
struct NT_Value* NT_GetValueBooleanForTesting(uint64_t last_change, int val,
|
||||
int* struct_size) {
|
||||
struct NT_Value* value =
|
||||
static_cast<NT_Value*>(std::calloc(1, sizeof(NT_Value)));
|
||||
value->type = NT_BOOLEAN;
|
||||
value->last_change = last_change;
|
||||
value->data.v_boolean = val;
|
||||
*struct_size = sizeof(NT_Value);
|
||||
return value;
|
||||
}
|
||||
|
||||
struct NT_Value* NT_GetValueDoubleForTesting(uint64_t last_change, double val,
|
||||
int* struct_size) {
|
||||
struct NT_Value* value =
|
||||
static_cast<NT_Value*>(std::calloc(1, sizeof(NT_Value)));
|
||||
value->type = NT_DOUBLE;
|
||||
value->last_change = last_change;
|
||||
value->data.v_double = val;
|
||||
*struct_size = sizeof(NT_Value);
|
||||
return value;
|
||||
}
|
||||
|
||||
struct NT_Value* NT_GetValueStringForTesting(uint64_t last_change,
|
||||
const char* str,
|
||||
int* struct_size) {
|
||||
struct NT_Value* value =
|
||||
static_cast<NT_Value*>(std::calloc(1, sizeof(NT_Value)));
|
||||
value->type = NT_STRING;
|
||||
value->last_change = last_change;
|
||||
nt::ConvertToC(llvm::StringRef(str), &value->data.v_string);
|
||||
*struct_size = sizeof(NT_Value);
|
||||
return value;
|
||||
}
|
||||
|
||||
struct NT_Value* NT_GetValueRawForTesting(uint64_t last_change, const char* raw,
|
||||
int raw_len, int* struct_size) {
|
||||
struct NT_Value* value =
|
||||
static_cast<NT_Value*>(std::calloc(1, sizeof(NT_Value)));
|
||||
value->type = NT_RAW;
|
||||
value->last_change = last_change;
|
||||
nt::ConvertToC(llvm::StringRef(raw, raw_len), &value->data.v_string);
|
||||
*struct_size = sizeof(NT_Value);
|
||||
return value;
|
||||
}
|
||||
|
||||
struct NT_Value* NT_GetValueBooleanArrayForTesting(uint64_t last_change,
|
||||
const int* arr,
|
||||
size_t array_len,
|
||||
int* struct_size) {
|
||||
struct NT_Value* value =
|
||||
static_cast<NT_Value*>(std::calloc(1, sizeof(NT_Value)));
|
||||
value->type = NT_BOOLEAN_ARRAY;
|
||||
value->last_change = last_change;
|
||||
value->data.arr_boolean.arr = NT_AllocateBooleanArray(array_len);
|
||||
value->data.arr_boolean.size = array_len;
|
||||
std::memcpy(value->data.arr_boolean.arr, arr,
|
||||
value->data.arr_boolean.size * sizeof(int));
|
||||
*struct_size = sizeof(NT_Value);
|
||||
return value;
|
||||
}
|
||||
|
||||
struct NT_Value* NT_GetValueDoubleArrayForTesting(uint64_t last_change,
|
||||
const double* arr,
|
||||
size_t array_len,
|
||||
int* struct_size) {
|
||||
struct NT_Value* value =
|
||||
static_cast<NT_Value*>(std::calloc(1, sizeof(NT_Value)));
|
||||
value->type = NT_BOOLEAN;
|
||||
value->last_change = last_change;
|
||||
value->data.arr_double.arr = NT_AllocateDoubleArray(array_len);
|
||||
value->data.arr_double.size = array_len;
|
||||
std::memcpy(value->data.arr_double.arr, arr,
|
||||
value->data.arr_double.size * sizeof(int));
|
||||
*struct_size = sizeof(NT_Value);
|
||||
return value;
|
||||
}
|
||||
|
||||
struct NT_Value* NT_GetValueStringArrayForTesting(uint64_t last_change,
|
||||
const struct NT_String* arr,
|
||||
size_t array_len,
|
||||
int* struct_size) {
|
||||
struct NT_Value* value =
|
||||
static_cast<NT_Value*>(std::calloc(1, sizeof(NT_Value)));
|
||||
value->type = NT_BOOLEAN;
|
||||
value->last_change = last_change;
|
||||
value->data.arr_string.arr = NT_AllocateStringArray(array_len);
|
||||
value->data.arr_string.size = array_len;
|
||||
for (size_t i = 0; i < value->data.arr_string.size; ++i) {
|
||||
size_t len = arr[i].len;
|
||||
value->data.arr_string.arr[i].len = len;
|
||||
value->data.arr_string.arr[i].str =
|
||||
static_cast<char*>(std::malloc(len + 1));
|
||||
std::memcpy(value->data.arr_string.arr[i].str, arr[i].str, len + 1);
|
||||
}
|
||||
*struct_size = sizeof(NT_Value);
|
||||
return value;
|
||||
}
|
||||
// No need for free as one already exists in the main library
|
||||
|
||||
static void CopyNtValue(const struct NT_Value* copy_from,
|
||||
struct NT_Value* copy_to) {
|
||||
auto cpp_value = nt::ConvertFromC(*copy_from);
|
||||
nt::ConvertToC(*cpp_value, copy_to);
|
||||
}
|
||||
|
||||
static void CopyNtString(const struct NT_String* copy_from,
|
||||
struct NT_String* copy_to) {
|
||||
nt::ConvertToC(llvm::StringRef(copy_from->str, copy_from->len), copy_to);
|
||||
}
|
||||
|
||||
struct NT_RpcParamDef* NT_GetRpcParamDefForTesting(const char* name,
|
||||
const struct NT_Value* val,
|
||||
int* struct_size) {
|
||||
struct NT_RpcParamDef* def =
|
||||
static_cast<NT_RpcParamDef*>(std::calloc(1, sizeof(NT_RpcParamDef)));
|
||||
nt::ConvertToC(llvm::StringRef(name), &def->name);
|
||||
CopyNtValue(val, &def->def_value);
|
||||
*struct_size = sizeof(NT_RpcParamDef);
|
||||
return def;
|
||||
}
|
||||
|
||||
void NT_FreeRpcParamDefForTesting(struct NT_RpcParamDef* def) {
|
||||
NT_DisposeValue(&def->def_value);
|
||||
NT_DisposeString(&def->name);
|
||||
std::free(def);
|
||||
}
|
||||
|
||||
struct NT_RpcResultDef* NT_GetRpcResultsDefForTesting(const char* name,
|
||||
enum NT_Type type,
|
||||
int* struct_size) {
|
||||
struct NT_RpcResultDef* def =
|
||||
static_cast<NT_RpcResultDef*>(std::calloc(1, sizeof(NT_RpcResultDef)));
|
||||
nt::ConvertToC(llvm::StringRef(name), &def->name);
|
||||
def->type = type;
|
||||
*struct_size = sizeof(NT_RpcResultDef);
|
||||
return def;
|
||||
}
|
||||
|
||||
void NT_FreeRpcResultsDefForTesting(struct NT_RpcResultDef* def) {
|
||||
NT_DisposeString(&def->name);
|
||||
std::free(def);
|
||||
}
|
||||
|
||||
struct NT_RpcDefinition* NT_GetRpcDefinitionForTesting(
|
||||
unsigned int version, const char* name, size_t num_params,
|
||||
const struct NT_RpcParamDef* params, size_t num_results,
|
||||
const struct NT_RpcResultDef* results, int* struct_size) {
|
||||
struct NT_RpcDefinition* def =
|
||||
static_cast<NT_RpcDefinition*>(std::calloc(1, sizeof(NT_RpcDefinition)));
|
||||
def->version = version;
|
||||
nt::ConvertToC(llvm::StringRef(name), &def->name);
|
||||
def->num_params = num_params;
|
||||
def->params = static_cast<NT_RpcParamDef*>(
|
||||
std::malloc(num_params * sizeof(NT_RpcParamDef)));
|
||||
for (size_t i = 0; i < num_params; ++i) {
|
||||
CopyNtString(¶ms[i].name, &def->params[i].name);
|
||||
CopyNtValue(¶ms[i].def_value, &def->params[i].def_value);
|
||||
}
|
||||
def->num_results = num_results;
|
||||
def->results = static_cast<NT_RpcResultDef*>(
|
||||
std::malloc(num_results * sizeof(NT_RpcResultDef)));
|
||||
for (size_t i = 0; i < num_results; ++i) {
|
||||
CopyNtString(&results[i].name, &def->results[i].name);
|
||||
def->results[i].type = results[i].type;
|
||||
}
|
||||
*struct_size = sizeof(NT_RpcDefinition);
|
||||
return def;
|
||||
}
|
||||
// No need for free as one already exists in the main library
|
||||
|
||||
struct NT_RpcAnswer* NT_GetRpcAnswerForTesting(
|
||||
unsigned int rpc_id, unsigned int call_uid, const char* name,
|
||||
const char* params, size_t params_len, int* struct_size) {
|
||||
struct NT_RpcAnswer* info =
|
||||
static_cast<NT_RpcAnswer*>(std::calloc(1, sizeof(NT_RpcAnswer)));
|
||||
info->entry = rpc_id;
|
||||
info->call = call_uid;
|
||||
nt::ConvertToC(llvm::StringRef(name), &info->name);
|
||||
nt::ConvertToC(llvm::StringRef(params, params_len), &info->params);
|
||||
*struct_size = sizeof(NT_RpcAnswer);
|
||||
return info;
|
||||
}
|
||||
// No need for free as one already exists in the main library
|
||||
} // extern "C"
|
||||
16
ntcore/src/main/native/cpp/tables/ITableListener.cpp
Normal file
16
ntcore/src/main/native/cpp/tables/ITableListener.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "tables/ITableListener.h"
|
||||
|
||||
#include "ntcore_c.h"
|
||||
|
||||
void ITableListener::ValueChangedEx(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value,
|
||||
unsigned int flags) {
|
||||
ValueChanged(source, key, value, (flags & NT_NOTIFY_NEW) != 0);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_NETWORKTABLES_ENTRYLISTENERFLAGS_H_
|
||||
#define NTCORE_NETWORKTABLES_ENTRYLISTENERFLAGS_H_
|
||||
|
||||
#include "ntcore_c.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
namespace EntryListenerFlags {
|
||||
|
||||
/**
|
||||
* Flag values for use with entry listeners.
|
||||
*
|
||||
* The flags are a bitmask and must be OR'ed together to indicate the
|
||||
* combination of events desired to be received.
|
||||
*
|
||||
* The constants kNew, kDelete, kUpdate, and kFlags represent different events
|
||||
* that can occur to entries.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
enum {
|
||||
/** Initial listener addition.
|
||||
* Set this flag to receive immediate notification of entries matching the
|
||||
* flag criteria (generally only useful when combined with kNew).
|
||||
*/
|
||||
kImmediate = NT_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. Must be combined with some combination of kNew,
|
||||
* kDelete, kUpdate, and kFlags to receive notifications of those respective
|
||||
* events.
|
||||
*/
|
||||
kLocal = NT_NOTIFY_LOCAL,
|
||||
|
||||
/** Newly created entry.
|
||||
* Set this flag to receive a notification when an entry is created.
|
||||
*/
|
||||
kNew = NT_NOTIFY_NEW,
|
||||
|
||||
/** Entry was deleted.
|
||||
* Set this flag to receive a notification when an entry is deleted.
|
||||
*/
|
||||
kDelete = NT_NOTIFY_DELETE,
|
||||
|
||||
/** Entry's value changed.
|
||||
* Set this flag to receive a notification when an entry's value (or type)
|
||||
* changes.
|
||||
*/
|
||||
kUpdate = NT_NOTIFY_UPDATE,
|
||||
|
||||
/** Entry's flags changed.
|
||||
* Set this flag to receive a notification when an entry's flags value
|
||||
* changes.
|
||||
*/
|
||||
kFlags = NT_NOTIFY_FLAGS
|
||||
};
|
||||
|
||||
} // namespace EntryListenerFlags
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_NETWORKTABLES_ENTRYLISTENERFLAGS_H_
|
||||
734
ntcore/src/main/native/include/networktables/NetworkTable.h
Normal file
734
ntcore/src/main/native/include/networktables/NetworkTable.h
Normal file
@@ -0,0 +1,734 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_NETWORKTABLES_NETWORKTABLE_H_
|
||||
#define NTCORE_NETWORKTABLES_NETWORKTABLE_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <llvm/ArrayRef.h>
|
||||
#include <llvm/StringMap.h>
|
||||
#include <llvm/Twine.h>
|
||||
#include <support/mutex.h>
|
||||
|
||||
#include "networktables/NetworkTableEntry.h"
|
||||
#include "networktables/TableEntryListener.h"
|
||||
#include "networktables/TableListener.h"
|
||||
#include "ntcore_c.h"
|
||||
#include "tables/ITable.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
using llvm::ArrayRef;
|
||||
using llvm::StringRef;
|
||||
using llvm::Twine;
|
||||
|
||||
class NetworkTableInstance;
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A network table that knows its subtable path.
|
||||
*/
|
||||
class NetworkTable final : public ITable {
|
||||
private:
|
||||
NT_Inst m_inst;
|
||||
std::string m_path;
|
||||
mutable wpi::mutex m_mutex;
|
||||
mutable llvm::StringMap<NT_Entry> m_entries;
|
||||
typedef std::pair<ITableListener*, NT_EntryListener> Listener;
|
||||
std::vector<Listener> m_listeners;
|
||||
|
||||
static std::vector<std::string> s_ip_addresses;
|
||||
static std::string s_persistent_filename;
|
||||
static bool s_client;
|
||||
static bool s_enable_ds;
|
||||
static bool s_running;
|
||||
static unsigned int s_port;
|
||||
|
||||
struct private_init {};
|
||||
friend class NetworkTableInstance;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Gets the "base name" of a key. For example, "/foo/bar" becomes "bar".
|
||||
* If the key has a trailing slash, returns an empty string.
|
||||
* @param key key
|
||||
* @return base name
|
||||
*/
|
||||
static StringRef BasenameKey(StringRef key);
|
||||
|
||||
/**
|
||||
* Normalizes an network table key to contain no consecutive slashes and
|
||||
* optionally start with a leading slash. For example:
|
||||
*
|
||||
* <pre><code>
|
||||
* normalizeKey("/foo/bar", true) == "/foo/bar"
|
||||
* normalizeKey("foo/bar", true) == "/foo/bar"
|
||||
* normalizeKey("/foo/bar", false) == "foo/bar"
|
||||
* normalizeKey("foo//bar", false) == "foo/bar"
|
||||
* </code></pre>
|
||||
*
|
||||
* @param key the key to normalize
|
||||
* @param withLeadingSlash whether or not the normalized key should begin
|
||||
* with a leading slash
|
||||
* @return normalized key
|
||||
*/
|
||||
static std::string NormalizeKey(const Twine& key,
|
||||
bool withLeadingSlash = true);
|
||||
|
||||
static StringRef NormalizeKey(const Twine& key,
|
||||
llvm::SmallVectorImpl<char>& buf,
|
||||
bool withLeadingSlash = true);
|
||||
|
||||
/**
|
||||
* Gets a list of the names of all the super tables of a given key. For
|
||||
* example, the key "/foo/bar/baz" has a hierarchy of "/", "/foo",
|
||||
* "/foo/bar", and "/foo/bar/baz".
|
||||
* @param key the key
|
||||
* @return List of super tables
|
||||
*/
|
||||
static std::vector<std::string> GetHierarchy(const Twine& key);
|
||||
|
||||
/**
|
||||
* Constructor. Use NetworkTableInstance::GetTable() or GetSubTable()
|
||||
* instead.
|
||||
*/
|
||||
NetworkTable(NT_Inst inst, const Twine& path, const private_init&);
|
||||
virtual ~NetworkTable();
|
||||
|
||||
/**
|
||||
* Gets the instance for the table.
|
||||
* @return Instance
|
||||
*/
|
||||
NetworkTableInstance GetInstance() const;
|
||||
|
||||
/**
|
||||
* The path separator for sub-tables and keys
|
||||
*/
|
||||
static const char PATH_SEPARATOR_CHAR;
|
||||
|
||||
/**
|
||||
* Initializes network tables
|
||||
*/
|
||||
WPI_DEPRECATED(
|
||||
"use NetworkTableInstance::StartServer() or "
|
||||
"NetworkTableInstance::StartClient() instead")
|
||||
static void Initialize();
|
||||
|
||||
/**
|
||||
* Shuts down network tables
|
||||
*/
|
||||
WPI_DEPRECATED(
|
||||
"use NetworkTableInstance::StopServer() or "
|
||||
"NetworkTableInstance::StopClient() instead")
|
||||
static void Shutdown();
|
||||
|
||||
/**
|
||||
* set that network tables should be a client
|
||||
* This must be called before initialize or GetTable
|
||||
*/
|
||||
WPI_DEPRECATED("use NetworkTableInstance::StartClient() instead")
|
||||
static void SetClientMode();
|
||||
|
||||
/**
|
||||
* set that network tables should be a server
|
||||
* This must be called before initialize or GetTable
|
||||
*/
|
||||
WPI_DEPRECATED("use NetworkTableInstance::StartServer() instead")
|
||||
static void SetServerMode();
|
||||
|
||||
/**
|
||||
* set the team the robot is configured for (this will set the mdns address
|
||||
* that network tables will connect to in client mode)
|
||||
* This must be called before initialize or GetTable
|
||||
* @param team the team number
|
||||
*/
|
||||
WPI_DEPRECATED(
|
||||
"use NetworkTableInstance::SetServerTeam() or "
|
||||
"NetworkTableInstance::StartClientTeam() instead")
|
||||
static void SetTeam(int team);
|
||||
|
||||
/**
|
||||
* @param address the adress that network tables will connect to in client
|
||||
* mode
|
||||
*/
|
||||
WPI_DEPRECATED(
|
||||
"use NetworkTableInstance::SetServer() or "
|
||||
"NetworkTableInstance::StartClient() instead")
|
||||
static void SetIPAddress(StringRef address);
|
||||
|
||||
/**
|
||||
* @param addresses the addresses that network tables will connect to in
|
||||
* client mode (in round robin order)
|
||||
*/
|
||||
WPI_DEPRECATED(
|
||||
"use NetworkTableInstance::SetServer() or "
|
||||
"NetworkTableInstance::StartClient() instead")
|
||||
static void SetIPAddress(ArrayRef<std::string> addresses);
|
||||
|
||||
/**
|
||||
* Set the port number that network tables will connect to in client
|
||||
* mode or listen to in server mode.
|
||||
* @param port the port number
|
||||
*/
|
||||
WPI_DEPRECATED(
|
||||
"use the appropriate parameters to NetworkTableInstance::SetServer(), "
|
||||
"NetworkTableInstance::StartClient(), "
|
||||
"NetworkTableInstance::StartServer(), and "
|
||||
"NetworkTableInstance::StartDSClient() instead")
|
||||
static void SetPort(unsigned int port);
|
||||
|
||||
/**
|
||||
* Enable requesting the server address from the Driver Station.
|
||||
* @param enabled whether to enable the connection to the local DS
|
||||
*/
|
||||
WPI_DEPRECATED(
|
||||
"use NetworkTableInstance::StartDSClient() and "
|
||||
"NetworkTableInstance::StopDSClient() instead")
|
||||
static void SetDSClientEnabled(bool enabled);
|
||||
|
||||
/**
|
||||
* Sets the persistent filename.
|
||||
* @param filename the filename that the network tables server uses for
|
||||
* automatic loading and saving of persistent values
|
||||
*/
|
||||
WPI_DEPRECATED(
|
||||
"use the appropriate parameter to NetworkTableInstance::StartServer() "
|
||||
"instead")
|
||||
static void SetPersistentFilename(StringRef filename);
|
||||
|
||||
/**
|
||||
* Sets the network identity.
|
||||
* This is provided in the connection info on the remote end.
|
||||
* @param name identity
|
||||
*/
|
||||
WPI_DEPRECATED("use NetworkTableInstance::SetNetworkIdentity() instead")
|
||||
static void SetNetworkIdentity(StringRef name);
|
||||
|
||||
/**
|
||||
* Deletes ALL keys in ALL subtables. Use with caution!
|
||||
*/
|
||||
WPI_DEPRECATED("use NetworkTableInstance::DeleteAllEntries() instead")
|
||||
static void GlobalDeleteAll();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
WPI_DEPRECATED("use NetworkTableInstance::Flush() instead")
|
||||
static void Flush();
|
||||
|
||||
/**
|
||||
* Set the periodic update rate.
|
||||
* Sets how frequently updates are sent to other nodes over the network.
|
||||
*
|
||||
* @param interval update interval in seconds (range 0.01 to 1.0)
|
||||
*/
|
||||
WPI_DEPRECATED("use NetworkTableInstance::SetUpdateRate() instead")
|
||||
static void SetUpdateRate(double interval);
|
||||
|
||||
/**
|
||||
* Saves persistent keys to a file. The server does this automatically.
|
||||
*
|
||||
* @param filename file name
|
||||
* @return Error (or nullptr).
|
||||
*/
|
||||
WPI_DEPRECATED("use NetworkTableInstance::SavePersistent() instead")
|
||||
static const char* SavePersistent(StringRef filename);
|
||||
|
||||
/**
|
||||
* Loads persistent keys from a file. The server does this automatically.
|
||||
*
|
||||
* @param filename file name
|
||||
* @param warn callback function called for warnings
|
||||
* @return Error (or nullptr).
|
||||
*/
|
||||
WPI_DEPRECATED("use NetworkTableInstance::LoadPersistent() instead")
|
||||
static const char* LoadPersistent(
|
||||
StringRef filename,
|
||||
std::function<void(size_t line, const char* msg)> warn);
|
||||
|
||||
/**
|
||||
* Gets the table with the specified key. If the table does not exist, a new
|
||||
* table will be created.<br>
|
||||
* This will automatically initialize network tables if it has not been
|
||||
* already.
|
||||
*
|
||||
* @param key the key name
|
||||
* @return the network table requested
|
||||
*/
|
||||
WPI_DEPRECATED(
|
||||
"use NetworkTableInstance::GetTable() or "
|
||||
"NetworkTableInstance::GetEntry() instead")
|
||||
static std::shared_ptr<NetworkTable> GetTable(StringRef key);
|
||||
|
||||
/**
|
||||
* Gets the entry for a subkey.
|
||||
* @param key the key name
|
||||
* @return Network table entry.
|
||||
*/
|
||||
NetworkTableEntry GetEntry(const Twine& key) const;
|
||||
|
||||
/**
|
||||
* Listen to keys only within this table.
|
||||
* @param listener listener to add
|
||||
* @param flags EntryListenerFlags bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_EntryListener AddEntryListener(TableEntryListener listener,
|
||||
unsigned int flags) const;
|
||||
|
||||
/**
|
||||
* Listen to a single key.
|
||||
* @param key the key name
|
||||
* @param listener listener to add
|
||||
* @param flags EntryListenerFlags bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_EntryListener AddEntryListener(const Twine& key,
|
||||
TableEntryListener listener,
|
||||
unsigned int flags) const;
|
||||
|
||||
/**
|
||||
* Remove an entry listener.
|
||||
* @param listener listener handle
|
||||
*/
|
||||
void RemoveEntryListener(NT_EntryListener listener) const;
|
||||
|
||||
/**
|
||||
* Listen for sub-table creation.
|
||||
* This calls the listener once for each newly created sub-table.
|
||||
* It immediately calls the listener for any existing sub-tables.
|
||||
* @param listener listener to add
|
||||
* @param localNotify notify local changes as well as remote
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_EntryListener AddSubTableListener(TableListener listener,
|
||||
bool localNotify = false) const;
|
||||
|
||||
/**
|
||||
* Remove a sub-table listener.
|
||||
* @param listener listener handle
|
||||
*/
|
||||
void RemoveTableListener(NT_EntryListener listener) const;
|
||||
|
||||
WPI_DEPRECATED(
|
||||
"use AddEntryListener() instead with flags value of NT_NOTIFY_NEW | "
|
||||
"NT_NOTIFY_UPDATE")
|
||||
void AddTableListener(ITableListener* listener) override;
|
||||
|
||||
WPI_DEPRECATED(
|
||||
"use AddEntryListener() instead with flags value of NT_NOTIFY_NEW | "
|
||||
"NT_NOTIFY_UPDATE | NT_NOTIFY_IMMEDIATE")
|
||||
void AddTableListener(ITableListener* listener,
|
||||
bool immediateNotify) override;
|
||||
|
||||
WPI_DEPRECATED("use AddEntryListener() instead")
|
||||
void AddTableListenerEx(ITableListener* listener,
|
||||
unsigned int flags) override;
|
||||
|
||||
WPI_DEPRECATED("use AddEntryListener() instead")
|
||||
void AddTableListener(StringRef key, ITableListener* listener,
|
||||
bool immediateNotify) override;
|
||||
|
||||
WPI_DEPRECATED("use AddEntryListener() instead")
|
||||
void AddTableListenerEx(StringRef key, ITableListener* listener,
|
||||
unsigned int flags) override;
|
||||
|
||||
WPI_DEPRECATED("use AddSubTableListener(TableListener, bool) instead")
|
||||
void AddSubTableListener(ITableListener* listener) override;
|
||||
|
||||
WPI_DEPRECATED("use AddSubTableListener(TableListener, bool) instead")
|
||||
void AddSubTableListener(ITableListener* listener, bool localNotify) override;
|
||||
|
||||
WPI_DEPRECATED("use RemoveTableListener(NT_EntryListener) instead")
|
||||
void RemoveTableListener(ITableListener* listener) override;
|
||||
|
||||
/**
|
||||
* Returns the table at the specified key. If there is no table at the
|
||||
* specified key, it will create a new table
|
||||
*
|
||||
* @param key the key name
|
||||
* @return the networktable to be returned
|
||||
*/
|
||||
std::shared_ptr<NetworkTable> GetSubTable(const Twine& key) const override;
|
||||
|
||||
/**
|
||||
* Determines whether the given key is in this table.
|
||||
*
|
||||
* @param key the key to search for
|
||||
* @return true if the table as a value assigned to the given key
|
||||
*/
|
||||
bool ContainsKey(const Twine& key) const override;
|
||||
|
||||
/**
|
||||
* Determines whether there exists a non-empty subtable for this key
|
||||
* in this table.
|
||||
*
|
||||
* @param key the key to search for
|
||||
* @return true if there is a subtable with the key which contains at least
|
||||
* one key/subtable of its own
|
||||
*/
|
||||
bool ContainsSubTable(const Twine& key) const override;
|
||||
|
||||
/**
|
||||
* Gets all keys in the table (not including sub-tables).
|
||||
* @param types bitmask of types; 0 is treated as a "don't care".
|
||||
* @return keys currently in the table
|
||||
*/
|
||||
std::vector<std::string> GetKeys(int types = 0) const override;
|
||||
|
||||
/**
|
||||
* Gets the names of all subtables in the table.
|
||||
* @return subtables currently in the table
|
||||
*/
|
||||
std::vector<std::string> GetSubTables() const override;
|
||||
|
||||
/**
|
||||
* Makes a key's value persistent through program restarts.
|
||||
*
|
||||
* @param key the key to make persistent
|
||||
*/
|
||||
void SetPersistent(StringRef key) override;
|
||||
|
||||
/**
|
||||
* Stop making a key's value persistent through program restarts.
|
||||
* The key cannot be null.
|
||||
*
|
||||
* @param key the key name
|
||||
*/
|
||||
void ClearPersistent(StringRef key) override;
|
||||
|
||||
/**
|
||||
* Returns whether the value is persistent through program restarts.
|
||||
* The key cannot be null.
|
||||
*
|
||||
* @param key the key name
|
||||
*/
|
||||
bool IsPersistent(StringRef key) const override;
|
||||
|
||||
/**
|
||||
* Sets flags on the specified key in this table. The key can
|
||||
* not be null.
|
||||
*
|
||||
* @param key the key name
|
||||
* @param flags the flags to set (bitmask)
|
||||
*/
|
||||
void SetFlags(StringRef key, unsigned int flags) override;
|
||||
|
||||
/**
|
||||
* Clears flags on the specified key in this table. The key can
|
||||
* not be null.
|
||||
*
|
||||
* @param key the key name
|
||||
* @param flags the flags to clear (bitmask)
|
||||
*/
|
||||
void ClearFlags(StringRef key, unsigned int flags) override;
|
||||
|
||||
/**
|
||||
* Returns the flags for the specified key.
|
||||
*
|
||||
* @param key the key name
|
||||
* @return the flags, or 0 if the key is not defined
|
||||
*/
|
||||
unsigned int GetFlags(StringRef key) const override;
|
||||
|
||||
/**
|
||||
* Deletes the specified key in this table.
|
||||
*
|
||||
* @param key the key name
|
||||
*/
|
||||
void Delete(const Twine& key) override;
|
||||
|
||||
/**
|
||||
* Put a number in the table
|
||||
*
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
bool PutNumber(StringRef key, double value) override;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
bool SetDefaultNumber(StringRef key, double defaultValue) override;
|
||||
|
||||
/**
|
||||
* Gets the number associated with the given name.
|
||||
*
|
||||
* @param key the key to look up
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the value associated with the given key or the given default value
|
||||
* if there is no value associated with the key
|
||||
*/
|
||||
double GetNumber(StringRef key, double defaultValue) const override;
|
||||
|
||||
/**
|
||||
* Put a string in the table
|
||||
*
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
bool PutString(StringRef key, StringRef value) override;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
bool SetDefaultString(StringRef key, StringRef defaultValue) override;
|
||||
|
||||
/**
|
||||
* Gets the string associated with the given name. If the key does not
|
||||
* exist or is of different type, it will return the default value.
|
||||
*
|
||||
* @param key the key to look up
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the value associated with the given key or the given default value
|
||||
* if there is no value associated with the key
|
||||
*/
|
||||
std::string GetString(StringRef key, StringRef defaultValue) const override;
|
||||
|
||||
/**
|
||||
* Put a boolean in the table
|
||||
*
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
bool PutBoolean(StringRef key, bool value) override;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
bool SetDefaultBoolean(StringRef key, bool defaultValue) override;
|
||||
|
||||
/**
|
||||
* Gets the boolean associated with the given name. If the key does not
|
||||
* exist or is of different type, it will return the default value.
|
||||
*
|
||||
* @param key the key to look up
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the value associated with the given key or the given default value
|
||||
* if there is no value associated with the key
|
||||
*/
|
||||
bool GetBoolean(StringRef key, bool defaultValue) const override;
|
||||
|
||||
/**
|
||||
* Put a boolean array in the table
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*
|
||||
* @note The array must be of int's rather than of bool's because
|
||||
* std::vector<bool> is special-cased in C++. 0 is false, any
|
||||
* non-zero value is true.
|
||||
*/
|
||||
bool PutBooleanArray(StringRef key, ArrayRef<int> value) override;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
bool SetDefaultBooleanArray(StringRef key,
|
||||
ArrayRef<int> defaultValue) override;
|
||||
|
||||
/**
|
||||
* Returns the boolean array the key maps to. If the key does not exist or is
|
||||
* of different type, it will return the default value.
|
||||
* @param key the key to look up
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the value associated with the given key or the given default value
|
||||
* if there is no value associated with the key
|
||||
*
|
||||
* @note This makes a copy of the array. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*
|
||||
* @note The returned array is std::vector<int> instead of std::vector<bool>
|
||||
* because std::vector<bool> is special-cased in C++. 0 is false, any
|
||||
* non-zero value is true.
|
||||
*/
|
||||
std::vector<int> GetBooleanArray(StringRef key,
|
||||
ArrayRef<int> defaultValue) const override;
|
||||
|
||||
/**
|
||||
* Put a number array in the table
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
bool PutNumberArray(StringRef key, ArrayRef<double> value) override;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
bool SetDefaultNumberArray(StringRef key,
|
||||
ArrayRef<double> defaultValue) override;
|
||||
|
||||
/**
|
||||
* Returns the number array the key maps to. If the key does not exist or is
|
||||
* of different type, it will return the default value.
|
||||
* @param key the key to look up
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the value associated with the given key or the given default value
|
||||
* if there is no value associated with the key
|
||||
*
|
||||
* @note This makes a copy of the array. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*/
|
||||
std::vector<double> GetNumberArray(
|
||||
StringRef key, ArrayRef<double> defaultValue) const override;
|
||||
|
||||
/**
|
||||
* Put a string array in the table
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
bool PutStringArray(StringRef key, ArrayRef<std::string> value) override;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
bool SetDefaultStringArray(StringRef key,
|
||||
ArrayRef<std::string> defaultValue) override;
|
||||
|
||||
/**
|
||||
* Returns the string array the key maps to. If the key does not exist or is
|
||||
* of different type, it will return the default value.
|
||||
* @param key the key to look up
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the value associated with the given key or the given default value
|
||||
* if there is no value associated with the key
|
||||
*
|
||||
* @note This makes a copy of the array. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*/
|
||||
std::vector<std::string> GetStringArray(
|
||||
StringRef key, ArrayRef<std::string> defaultValue) const override;
|
||||
|
||||
/**
|
||||
* Put a raw value (byte array) in the table
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
bool PutRaw(StringRef key, StringRef value) override;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
bool SetDefaultRaw(StringRef key, StringRef defaultValue) override;
|
||||
|
||||
/**
|
||||
* Returns the raw value (byte array) the key maps to. If the key does not
|
||||
* exist or is of different type, it will return the default value.
|
||||
* @param key the key to look up
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the value associated with the given key or the given default value
|
||||
* if there is no value associated with the key
|
||||
*
|
||||
* @note This makes a copy of the raw contents. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*/
|
||||
std::string GetRaw(StringRef key, StringRef defaultValue) const override;
|
||||
|
||||
/**
|
||||
* Put a value in the table
|
||||
*
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
bool PutValue(const Twine& key, std::shared_ptr<Value> value) override;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
bool SetDefaultValue(const Twine& key,
|
||||
std::shared_ptr<Value> defaultValue) override;
|
||||
|
||||
/**
|
||||
* Gets the value associated with a key as an object
|
||||
*
|
||||
* @param key the key of the value to look up
|
||||
* @return the value associated with the given key, or nullptr if the key
|
||||
* does not exist
|
||||
*/
|
||||
std::shared_ptr<Value> GetValue(const Twine& key) const override;
|
||||
|
||||
/**
|
||||
* Gets the full path of this table. Does not include the trailing "/".
|
||||
* @return The path (e.g "", "/foo").
|
||||
*/
|
||||
StringRef GetPath() const override;
|
||||
|
||||
/**
|
||||
* Save table values to a file. The file format used is identical to
|
||||
* that used for SavePersistent.
|
||||
* @param filename filename
|
||||
* @return error string, or nullptr if successful
|
||||
*/
|
||||
const char* SaveEntries(const Twine& filename) const;
|
||||
|
||||
/**
|
||||
* Load table values from a file. The file format used is identical to
|
||||
* that used for SavePersistent / LoadPersistent.
|
||||
* @param filename filename
|
||||
* @param warn callback function for warnings
|
||||
* @return error string, or nullptr if successful
|
||||
*/
|
||||
const char* LoadEntries(
|
||||
const Twine& filename,
|
||||
std::function<void(size_t line, const char* msg)> warn);
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
} // namespace nt
|
||||
|
||||
// For backwards compatability
|
||||
#ifndef NAMESPACED_NT
|
||||
using nt::NetworkTable; // NOLINT
|
||||
#endif
|
||||
|
||||
#endif // NTCORE_NETWORKTABLES_NETWORKTABLE_H_
|
||||
454
ntcore/src/main/native/include/networktables/NetworkTableEntry.h
Normal file
454
ntcore/src/main/native/include/networktables/NetworkTableEntry.h
Normal file
@@ -0,0 +1,454 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_NETWORKTABLES_NETWORKTABLEENTRY_H_
|
||||
#define NTCORE_NETWORKTABLES_NETWORKTABLEENTRY_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <llvm/StringRef.h>
|
||||
#include <llvm/Twine.h>
|
||||
|
||||
#include "networktables/NetworkTableType.h"
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
#include "networktables/RpcCall.h"
|
||||
#include "ntcore_c.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
using llvm::ArrayRef;
|
||||
using llvm::StringRef;
|
||||
using llvm::Twine;
|
||||
|
||||
class NetworkTableInstance;
|
||||
|
||||
/** NetworkTables Entry */
|
||||
class NetworkTableEntry final {
|
||||
public:
|
||||
/**
|
||||
* Flag values (as returned by {@link #getFlags()}).
|
||||
*/
|
||||
enum Flags { kPersistent = NT_PERSISTENT };
|
||||
|
||||
/**
|
||||
* Construct invalid instance.
|
||||
*/
|
||||
NetworkTableEntry();
|
||||
|
||||
/**
|
||||
* Construct from native handle.
|
||||
* @param handle Native handle
|
||||
*/
|
||||
explicit NetworkTableEntry(NT_Entry handle);
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle for the entry.
|
||||
* @return Native handle
|
||||
*/
|
||||
NT_Entry GetHandle() const;
|
||||
|
||||
/**
|
||||
* Gets the instance for the entry.
|
||||
* @return Instance
|
||||
*/
|
||||
NetworkTableInstance GetInstance() const;
|
||||
|
||||
/**
|
||||
* Determines if the entry currently exists.
|
||||
* @return True if the entry exists, false otherwise.
|
||||
*/
|
||||
bool Exists() const;
|
||||
|
||||
/**
|
||||
* Gets the name of the entry (the key).
|
||||
* @return the entry's name
|
||||
*/
|
||||
std::string GetName() const;
|
||||
|
||||
/**
|
||||
* Gets the type of the entry.
|
||||
* @return the entry's type
|
||||
*/
|
||||
NetworkTableType GetType() const;
|
||||
|
||||
/**
|
||||
* Returns the flags.
|
||||
* @return the flags (bitmask)
|
||||
*/
|
||||
unsigned int GetFlags() const;
|
||||
|
||||
/**
|
||||
* Gets the last time the entry's value was changed.
|
||||
* @return Entry last change time
|
||||
*/
|
||||
uint64_t GetLastChange() const;
|
||||
|
||||
/**
|
||||
* Gets combined information about the entry.
|
||||
* @return Entry information
|
||||
*/
|
||||
EntryInfo GetInfo() const;
|
||||
|
||||
/**
|
||||
* Gets the entry's value. If the entry does not exist, returns nullptr.
|
||||
*
|
||||
* @return the entry's value or nullptr if it does not exist.
|
||||
*/
|
||||
std::shared_ptr<Value> GetValue() const;
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a boolean. If the entry does not exist or is of
|
||||
* different type, it will return the default value.
|
||||
*
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
bool GetBoolean(bool defaultValue) const;
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a double. If the entry does not exist or is of
|
||||
* different type, it will return the default value.
|
||||
*
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
double GetDouble(double defaultValue) const;
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a string. If the entry does not exist or is of
|
||||
* different type, it will return the default value.
|
||||
*
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
std::string GetString(StringRef defaultValue) const;
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a raw. If the entry does not exist or is of
|
||||
* different type, it will return the default value.
|
||||
*
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
std::string GetRaw(StringRef defaultValue) const;
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a boolean array. If the entry does not exist
|
||||
* or is of different type, it will return the default value.
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*
|
||||
* @note This makes a copy of the array. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*
|
||||
* @note The returned array is std::vector<int> instead of std::vector<bool>
|
||||
* because std::vector<bool> is special-cased in C++. 0 is false, any
|
||||
* non-zero value is true.
|
||||
*/
|
||||
std::vector<int> GetBooleanArray(ArrayRef<int> defaultValue) const;
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a double array. If the entry does not exist
|
||||
* or is of different type, it will return the default value.
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*
|
||||
* @note This makes a copy of the array. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*/
|
||||
std::vector<double> GetDoubleArray(ArrayRef<double> defaultValue) const;
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a string array. If the entry does not exist
|
||||
* or is of different type, it will return the default value.
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*
|
||||
* @note This makes a copy of the array. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*/
|
||||
std::vector<std::string> GetStringArray(
|
||||
ArrayRef<std::string> defaultValue) const;
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDefaultValue(std::shared_ptr<Value> value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDefaultBoolean(bool defaultValue);
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDefaultDouble(double defaultValue);
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDefaultString(const Twine& defaultValue);
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDefaultRaw(StringRef defaultValue);
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDefaultBooleanArray(ArrayRef<int> defaultValue);
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDefaultDoubleArray(ArrayRef<double> defaultValue);
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDefaultStringArray(ArrayRef<std::string> defaultValue);
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetValue(std::shared_ptr<Value> value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetBoolean(bool value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDouble(double value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetString(const Twine& value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetRaw(StringRef value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetBooleanArray(ArrayRef<int> value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDoubleArray(ArrayRef<double> value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetStringArray(ArrayRef<std::string> value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
void ForceSetValue(std::shared_ptr<Value> value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
void ForceSetBoolean(bool value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
void ForceSetDouble(double value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
void ForceSetString(const Twine& value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
void ForceSetRaw(StringRef value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
void ForceSetBooleanArray(ArrayRef<int> value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
void ForceSetDoubleArray(ArrayRef<double> value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
void ForceSetStringArray(ArrayRef<std::string> value);
|
||||
|
||||
/**
|
||||
* Sets flags.
|
||||
* @param flags the flags to set (bitmask)
|
||||
*/
|
||||
void SetFlags(unsigned int flags);
|
||||
|
||||
/**
|
||||
* Clears flags.
|
||||
* @param flags the flags to clear (bitmask)
|
||||
*/
|
||||
void ClearFlags(unsigned int flags);
|
||||
|
||||
/**
|
||||
* Make value persistent through program restarts.
|
||||
*/
|
||||
void SetPersistent();
|
||||
|
||||
/**
|
||||
* Stop making value persistent through program restarts.
|
||||
*/
|
||||
void ClearPersistent();
|
||||
|
||||
/**
|
||||
* Returns whether the value is persistent through program restarts.
|
||||
* @return True if the value is persistent.
|
||||
*/
|
||||
bool IsPersistent() const;
|
||||
|
||||
/**
|
||||
* Deletes the entry.
|
||||
*/
|
||||
void Delete();
|
||||
|
||||
/**
|
||||
* Create a callback-based RPC entry point. Only valid to use on the server.
|
||||
* The callback function will be called when the RPC is called.
|
||||
* This function creates RPC version 0 definitions (raw data in and out).
|
||||
* @param callback callback function
|
||||
*/
|
||||
void CreateRpc(std::function<void(const RpcAnswer& answer)> callback);
|
||||
|
||||
/**
|
||||
* Create a polled RPC entry point. Only valid to use on the server.
|
||||
* The caller is responsible for calling NetworkTableInstance::PollRpc()
|
||||
* to poll for servicing incoming RPC calls.
|
||||
* This function creates RPC version 0 definitions (raw data in and out).
|
||||
*/
|
||||
void CreatePolledRpc();
|
||||
|
||||
/**
|
||||
* Call a RPC function. May be used on either the client or server.
|
||||
* This function is non-blocking. Either RpcCall::GetResult() or
|
||||
* RpcCall::CancelResult() must be called on the return value to either
|
||||
* get or ignore the result of the call.
|
||||
* @param params parameter
|
||||
* @return RPC call object.
|
||||
*/
|
||||
RpcCall CallRpc(StringRef params);
|
||||
|
||||
/**
|
||||
* Add a listener for changes to this entry.
|
||||
*
|
||||
* @param callback listener to add
|
||||
* @param flags NotifyKind bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_EntryListener AddListener(
|
||||
std::function<void(const EntryNotification& event)> callback,
|
||||
unsigned int flags) const;
|
||||
|
||||
/**
|
||||
* Remove an entry listener.
|
||||
* @param entry_listener Listener handle to remove
|
||||
*/
|
||||
void RemoveListener(NT_EntryListener entry_listener);
|
||||
|
||||
/**
|
||||
* Equality operator. Returns true if both instances refer to the same
|
||||
* native handle.
|
||||
*/
|
||||
bool operator==(const NetworkTableEntry& oth) const {
|
||||
return m_handle == oth.m_handle;
|
||||
}
|
||||
|
||||
/** Inequality operator. */
|
||||
bool operator!=(const NetworkTableEntry& oth) const {
|
||||
return !(*this == oth);
|
||||
}
|
||||
|
||||
protected:
|
||||
/* Native handle */
|
||||
NT_Entry m_handle;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#include "networktables/NetworkTableEntry.inl"
|
||||
|
||||
#endif // NTCORE_NETWORKTABLES_NETWORKTABLEENTRY_H_
|
||||
@@ -0,0 +1,232 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NT_ENTRY_INL_
|
||||
#define NT_ENTRY_INL_
|
||||
|
||||
namespace nt {
|
||||
|
||||
inline NetworkTableEntry::NetworkTableEntry() : m_handle{0} {}
|
||||
|
||||
inline NetworkTableEntry::NetworkTableEntry(NT_Entry handle)
|
||||
: m_handle{handle} {}
|
||||
|
||||
inline NT_Entry NetworkTableEntry::GetHandle() const { return m_handle; }
|
||||
|
||||
inline bool NetworkTableEntry::Exists() const {
|
||||
return GetEntryType(m_handle) != NT_UNASSIGNED;
|
||||
}
|
||||
|
||||
inline std::string NetworkTableEntry::GetName() const {
|
||||
return GetEntryName(m_handle);
|
||||
}
|
||||
|
||||
inline NetworkTableType NetworkTableEntry::GetType() const {
|
||||
return static_cast<NetworkTableType>(GetEntryType(m_handle));
|
||||
}
|
||||
|
||||
inline unsigned int NetworkTableEntry::GetFlags() const {
|
||||
return GetEntryFlags(m_handle);
|
||||
}
|
||||
|
||||
inline uint64_t NetworkTableEntry::GetLastChange() const {
|
||||
return GetEntryLastChange(m_handle);
|
||||
}
|
||||
|
||||
inline EntryInfo NetworkTableEntry::GetInfo() const {
|
||||
return GetEntryInfo(m_handle);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<Value> NetworkTableEntry::GetValue() const {
|
||||
return GetEntryValue(m_handle);
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::GetBoolean(bool defaultValue) const {
|
||||
auto value = GetEntryValue(m_handle);
|
||||
if (!value || value->type() != NT_BOOLEAN) return defaultValue;
|
||||
return value->GetBoolean();
|
||||
}
|
||||
|
||||
inline double NetworkTableEntry::GetDouble(double defaultValue) const {
|
||||
auto value = GetEntryValue(m_handle);
|
||||
if (!value || value->type() != NT_DOUBLE) return defaultValue;
|
||||
return value->GetDouble();
|
||||
}
|
||||
|
||||
inline std::string NetworkTableEntry::GetString(StringRef defaultValue) const {
|
||||
auto value = GetEntryValue(m_handle);
|
||||
if (!value || value->type() != NT_STRING) return defaultValue;
|
||||
return value->GetString();
|
||||
}
|
||||
|
||||
inline std::string NetworkTableEntry::GetRaw(StringRef defaultValue) const {
|
||||
auto value = GetEntryValue(m_handle);
|
||||
if (!value || value->type() != NT_RAW) return defaultValue;
|
||||
return value->GetString();
|
||||
}
|
||||
|
||||
inline std::vector<int> NetworkTableEntry::GetBooleanArray(
|
||||
ArrayRef<int> defaultValue) const {
|
||||
auto value = GetEntryValue(m_handle);
|
||||
if (!value || value->type() != NT_BOOLEAN_ARRAY) return defaultValue;
|
||||
return value->GetBooleanArray();
|
||||
}
|
||||
|
||||
inline std::vector<double> NetworkTableEntry::GetDoubleArray(
|
||||
ArrayRef<double> defaultValue) const {
|
||||
auto value = GetEntryValue(m_handle);
|
||||
if (!value || value->type() != NT_DOUBLE_ARRAY) return defaultValue;
|
||||
return value->GetDoubleArray();
|
||||
}
|
||||
|
||||
inline std::vector<std::string> NetworkTableEntry::GetStringArray(
|
||||
ArrayRef<std::string> defaultValue) const {
|
||||
auto value = GetEntryValue(m_handle);
|
||||
if (!value || value->type() != NT_STRING_ARRAY) return defaultValue;
|
||||
return value->GetStringArray();
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDefaultValue(std::shared_ptr<Value> value) {
|
||||
return SetDefaultEntryValue(m_handle, value);
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDefaultBoolean(bool defaultValue) {
|
||||
return SetDefaultEntryValue(m_handle, Value::MakeBoolean(defaultValue));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDefaultDouble(double defaultValue) {
|
||||
return SetDefaultEntryValue(m_handle, Value::MakeDouble(defaultValue));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDefaultString(const Twine& defaultValue) {
|
||||
return SetDefaultEntryValue(m_handle, Value::MakeString(defaultValue));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDefaultRaw(StringRef defaultValue) {
|
||||
return SetDefaultEntryValue(m_handle, Value::MakeRaw(defaultValue));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDefaultBooleanArray(
|
||||
ArrayRef<int> defaultValue) {
|
||||
return SetDefaultEntryValue(m_handle, Value::MakeBooleanArray(defaultValue));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDefaultDoubleArray(
|
||||
ArrayRef<double> defaultValue) {
|
||||
return SetDefaultEntryValue(m_handle, Value::MakeDoubleArray(defaultValue));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDefaultStringArray(
|
||||
ArrayRef<std::string> defaultValue) {
|
||||
return SetDefaultEntryValue(m_handle, Value::MakeStringArray(defaultValue));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetValue(std::shared_ptr<Value> value) {
|
||||
return SetEntryValue(m_handle, value);
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetBoolean(bool value) {
|
||||
return SetEntryValue(m_handle, Value::MakeBoolean(value));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDouble(double value) {
|
||||
return SetEntryValue(m_handle, Value::MakeDouble(value));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetString(const Twine& value) {
|
||||
return SetEntryValue(m_handle, Value::MakeString(value));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetRaw(StringRef value) {
|
||||
return SetEntryValue(m_handle, Value::MakeRaw(value));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetBooleanArray(ArrayRef<int> value) {
|
||||
return SetEntryValue(m_handle, Value::MakeBooleanArray(value));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDoubleArray(ArrayRef<double> value) {
|
||||
return SetEntryValue(m_handle, Value::MakeDoubleArray(value));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetStringArray(ArrayRef<std::string> value) {
|
||||
return SetEntryValue(m_handle, Value::MakeStringArray(value));
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ForceSetValue(std::shared_ptr<Value> value) {
|
||||
SetEntryTypeValue(m_handle, value);
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ForceSetBoolean(bool value) {
|
||||
SetEntryTypeValue(m_handle, Value::MakeBoolean(value));
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ForceSetDouble(double value) {
|
||||
SetEntryTypeValue(m_handle, Value::MakeDouble(value));
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ForceSetString(const Twine& value) {
|
||||
SetEntryTypeValue(m_handle, Value::MakeString(value));
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ForceSetRaw(StringRef value) {
|
||||
SetEntryTypeValue(m_handle, Value::MakeRaw(value));
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ForceSetBooleanArray(ArrayRef<int> value) {
|
||||
SetEntryTypeValue(m_handle, Value::MakeBooleanArray(value));
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ForceSetDoubleArray(ArrayRef<double> value) {
|
||||
SetEntryTypeValue(m_handle, Value::MakeDoubleArray(value));
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ForceSetStringArray(
|
||||
ArrayRef<std::string> value) {
|
||||
SetEntryTypeValue(m_handle, Value::MakeStringArray(value));
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::SetFlags(unsigned int flags) {
|
||||
SetEntryFlags(m_handle, GetFlags() | flags);
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ClearFlags(unsigned int flags) {
|
||||
SetEntryFlags(m_handle, GetFlags() & ~flags);
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::SetPersistent() { SetFlags(kPersistent); }
|
||||
|
||||
inline void NetworkTableEntry::ClearPersistent() { ClearFlags(kPersistent); }
|
||||
|
||||
inline bool NetworkTableEntry::IsPersistent() const {
|
||||
return (GetFlags() & kPersistent) != 0;
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::Delete() { DeleteEntry(m_handle); }
|
||||
|
||||
inline void NetworkTableEntry::CreateRpc(
|
||||
std::function<void(const RpcAnswer& answer)> callback) {
|
||||
::nt::CreateRpc(m_handle, StringRef("\0", 1), callback);
|
||||
}
|
||||
|
||||
inline RpcCall NetworkTableEntry::CallRpc(StringRef params) {
|
||||
return RpcCall{m_handle, ::nt::CallRpc(m_handle, params)};
|
||||
}
|
||||
|
||||
inline NT_EntryListener NetworkTableEntry::AddListener(
|
||||
std::function<void(const EntryNotification& event)> callback,
|
||||
unsigned int flags) const {
|
||||
return AddEntryListener(m_handle, callback, flags);
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::RemoveListener(NT_EntryListener entry_listener) {
|
||||
RemoveEntryListener(entry_listener);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NT_ENTRY_INL_
|
||||
@@ -0,0 +1,548 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_NETWORKTABLES_NETWORKTABLEINSTANCE_H_
|
||||
#define NTCORE_NETWORKTABLES_NETWORKTABLEINSTANCE_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <llvm/ArrayRef.h>
|
||||
#include <llvm/StringRef.h>
|
||||
#include <llvm/Twine.h>
|
||||
|
||||
#include "networktables/NetworkTable.h"
|
||||
#include "networktables/NetworkTableEntry.h"
|
||||
#include "ntcore_c.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
#ifndef NT_NOEXCEPT
|
||||
#ifdef _MSC_VER
|
||||
#if _MSC_VER >= 1900
|
||||
#define NT_NOEXCEPT noexcept
|
||||
#else
|
||||
#define NT_NOEXCEPT throw()
|
||||
#endif
|
||||
#else
|
||||
#define NT_NOEXCEPT noexcept
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace nt {
|
||||
|
||||
using llvm::ArrayRef;
|
||||
using llvm::StringRef;
|
||||
using llvm::Twine;
|
||||
|
||||
/** NetworkTables Instance.
|
||||
*
|
||||
* Instances are completely independent from each other. Table operations on
|
||||
* one instance will not be visible to other instances unless the instances are
|
||||
* connected via the network. The main limitation on instances is that you
|
||||
* cannot have two servers on the same network port. The main utility of
|
||||
* instances is for unit testing, but they can also enable one program to
|
||||
* connect to two different NetworkTables networks.
|
||||
*
|
||||
* The global "default" instance (as returned by GetDefault()) is
|
||||
* always available, and is intended for the common case when there is only
|
||||
* a single NetworkTables instance being used in the program. The
|
||||
* default instance cannot be destroyed.
|
||||
*
|
||||
* Additional instances can be created with the Create() function.
|
||||
* Instances are not reference counted or RAII. Instead, they must be
|
||||
* explicitly destroyed (with Destroy()).
|
||||
*/
|
||||
class NetworkTableInstance final {
|
||||
public:
|
||||
/**
|
||||
* Client/server mode flag values (as returned by GetNetworkMode()).
|
||||
* This is a bitmask.
|
||||
*/
|
||||
enum NetworkMode {
|
||||
kNetModeNone = NT_NET_MODE_NONE,
|
||||
kNetModeServer = NT_NET_MODE_SERVER,
|
||||
kNetModeClient = NT_NET_MODE_CLIENT,
|
||||
kNetModeStarting = NT_NET_MODE_STARTING,
|
||||
kNetModeFailure = NT_NET_MODE_FAILURE
|
||||
};
|
||||
|
||||
/**
|
||||
* Logging levels (as used by SetLogger()).
|
||||
*/
|
||||
enum LogLevel {
|
||||
kLogCritical = NT_LOG_CRITICAL,
|
||||
kLogError = NT_LOG_ERROR,
|
||||
kLogWarning = NT_LOG_WARNING,
|
||||
kLogInfo = NT_LOG_INFO,
|
||||
kLogDebug = NT_LOG_DEBUG,
|
||||
kLogDebug1 = NT_LOG_DEBUG1,
|
||||
kLogDebug2 = NT_LOG_DEBUG2,
|
||||
kLogDebug3 = NT_LOG_DEBUG3,
|
||||
kLogDebug4 = NT_LOG_DEBUG4
|
||||
};
|
||||
|
||||
/**
|
||||
* The default port that network tables operates on.
|
||||
*/
|
||||
enum { kDefaultPort = NT_DEFAULT_PORT };
|
||||
|
||||
/**
|
||||
* Construct invalid instance.
|
||||
*/
|
||||
NetworkTableInstance() NT_NOEXCEPT;
|
||||
|
||||
/**
|
||||
* Construct from native handle.
|
||||
* @param handle Native handle
|
||||
*/
|
||||
explicit NetworkTableInstance(NT_Inst inst) NT_NOEXCEPT;
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Get global default instance.
|
||||
* @return Global default instance
|
||||
*/
|
||||
static NetworkTableInstance GetDefault();
|
||||
|
||||
/**
|
||||
* Create an instance.
|
||||
* @return Newly created instance
|
||||
*/
|
||||
static NetworkTableInstance Create();
|
||||
|
||||
/**
|
||||
* Destroys an instance (note: this has global effect).
|
||||
* @param inst Instance
|
||||
*/
|
||||
static void Destroy(NetworkTableInstance inst);
|
||||
|
||||
/**
|
||||
* Gets the native handle for the entry.
|
||||
* @return Native handle
|
||||
*/
|
||||
NT_Inst GetHandle() const;
|
||||
|
||||
/**
|
||||
* Gets the entry for a key.
|
||||
* @param name Key
|
||||
* @return Network table entry.
|
||||
*/
|
||||
NetworkTableEntry GetEntry(const Twine& name);
|
||||
|
||||
/**
|
||||
* Get entries starting with the given prefix.
|
||||
* The results are optionally filtered by string prefix and entry type to
|
||||
* only return a subset of all entries.
|
||||
*
|
||||
* @param prefix entry name required prefix; only entries whose name
|
||||
* starts with this string are returned
|
||||
* @param types bitmask of types; 0 is treated as a "don't care"
|
||||
* @return Array of entries.
|
||||
*/
|
||||
std::vector<NetworkTableEntry> GetEntries(const Twine& prefix,
|
||||
unsigned int types);
|
||||
|
||||
/**
|
||||
* Get information about entries starting with the given prefix.
|
||||
* The results are optionally filtered by string prefix and entry type to
|
||||
* only return a subset of all entries.
|
||||
*
|
||||
* @param prefix entry name required prefix; only entries whose name
|
||||
* starts with this string are returned
|
||||
* @param types bitmask of types; 0 is treated as a "don't care"
|
||||
* @return Array of entry information.
|
||||
*/
|
||||
std::vector<EntryInfo> GetEntryInfo(const Twine& prefix,
|
||||
unsigned int types) const;
|
||||
|
||||
/**
|
||||
* Gets the table with the specified key.
|
||||
*
|
||||
* @param key the key name
|
||||
* @return The network table
|
||||
*/
|
||||
std::shared_ptr<NetworkTable> GetTable(const Twine& key) const;
|
||||
|
||||
/**
|
||||
* Deletes ALL keys in ALL subtables (except persistent values).
|
||||
* Use with caution!
|
||||
*/
|
||||
void DeleteAllEntries();
|
||||
|
||||
/**
|
||||
* @defgroup EntryListenerFunctions Entry Listener Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add a listener for all entries starting with a certain prefix.
|
||||
*
|
||||
* @param prefix UTF-8 string prefix
|
||||
* @param callback listener to add
|
||||
* @param flags EntryListenerFlags bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_EntryListener AddEntryListener(
|
||||
const Twine& prefix,
|
||||
std::function<void(const EntryNotification& event)> callback,
|
||||
unsigned int flags) const;
|
||||
|
||||
/**
|
||||
* Remove an entry listener.
|
||||
* @param entry_listener Listener handle to remove
|
||||
*/
|
||||
static void RemoveEntryListener(NT_EntryListener entry_listener);
|
||||
|
||||
/**
|
||||
* Wait for the entry listener queue to be empty. This is primarily useful
|
||||
* for deterministic testing. This blocks until either the entry 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 WaitForEntryListenerQueue(double timeout);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup ConnectionListenerFunctions Connection Listener Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add a connection listener.
|
||||
*
|
||||
* @param callback listener to add
|
||||
* @param immediate_notify notify listener of all existing connections
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ConnectionListener AddConnectionListener(
|
||||
std::function<void(const ConnectionNotification& event)> callback,
|
||||
bool immediate_notify) 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);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup RpcFunctions Remote Procedure Call Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Wait for the incoming RPC call queue to be empty. This is primarily useful
|
||||
* for deterministic testing. This blocks until either the RPC call
|
||||
* 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 WaitForRpcCallQueue(double timeout);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup NetworkFunctions Client/Server Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set the network identity of this node.
|
||||
* This is the name used during the initial connection handshake, and is
|
||||
* visible through ConnectionInfo on the remote node.
|
||||
* @param name identity to advertise
|
||||
*/
|
||||
void SetNetworkIdentity(const Twine& name);
|
||||
|
||||
/**
|
||||
* Get the current network mode.
|
||||
* @return Bitmask of NetworkMode.
|
||||
*/
|
||||
unsigned int GetNetworkMode() const;
|
||||
|
||||
/**
|
||||
* Starts a server using the specified filename, listening address, and port.
|
||||
*
|
||||
* @param persist_filename the name of the persist file to use (UTF-8 string,
|
||||
* null terminated)
|
||||
* @param listen_address the address to listen on, or null to listen on any
|
||||
* address (UTF-8 string, null terminated)
|
||||
* @param port port to communicate over
|
||||
*/
|
||||
void StartServer(const Twine& persist_filename = "networktables.ini",
|
||||
const char* listen_address = "",
|
||||
unsigned int port = kDefaultPort);
|
||||
|
||||
/**
|
||||
* Stops the server if it is running.
|
||||
*/
|
||||
void StopServer();
|
||||
|
||||
/**
|
||||
* Starts a client. Use SetServer to set the server name and port.
|
||||
*/
|
||||
void StartClient();
|
||||
|
||||
/**
|
||||
* Starts a client using the specified server and port
|
||||
*
|
||||
* @param server_name server name (UTF-8 string, null terminated)
|
||||
* @param port port to communicate over
|
||||
*/
|
||||
void StartClient(const char* server_name, unsigned int port = kDefaultPort);
|
||||
|
||||
/**
|
||||
* Starts a client using the specified (server, port) combinations. The
|
||||
* client will attempt to connect to each server in round robin fashion.
|
||||
*
|
||||
* @param servers array of server name and port pairs
|
||||
*/
|
||||
void StartClient(ArrayRef<std::pair<StringRef, unsigned int>> servers);
|
||||
|
||||
/**
|
||||
* Starts a client using the specified servers and port. The
|
||||
* client will attempt to connect to each server in round robin fashion.
|
||||
*
|
||||
* @param servers array of server names
|
||||
* @param port port to communicate over
|
||||
*/
|
||||
void StartClient(ArrayRef<StringRef> servers,
|
||||
unsigned int port = kDefaultPort);
|
||||
|
||||
/**
|
||||
* Starts a client using commonly known robot addresses for the specified
|
||||
* team.
|
||||
*
|
||||
* @param team team number
|
||||
* @param port port to communicate over
|
||||
*/
|
||||
void StartClientTeam(unsigned int team, unsigned int port = kDefaultPort);
|
||||
|
||||
/**
|
||||
* Stops the client if it is running.
|
||||
*/
|
||||
void StopClient();
|
||||
|
||||
/**
|
||||
* Sets server address and port for client (without restarting client).
|
||||
*
|
||||
* @param server_name server name (UTF-8 string, null terminated)
|
||||
* @param port port to communicate over
|
||||
*/
|
||||
void SetServer(const char* server_name, unsigned int port = kDefaultPort);
|
||||
|
||||
/**
|
||||
* Sets server addresses and ports for client (without restarting client).
|
||||
* The client will attempt to connect to each server in round robin fashion.
|
||||
*
|
||||
* @param servers array of server name and port pairs
|
||||
*/
|
||||
void SetServer(ArrayRef<std::pair<StringRef, unsigned int>> servers);
|
||||
|
||||
/**
|
||||
* Sets server addresses and port for client (without restarting client).
|
||||
* The client will attempt to connect to each server in round robin fashion.
|
||||
*
|
||||
* @param servers array of server names
|
||||
* @param port port to communicate over
|
||||
*/
|
||||
void SetServer(ArrayRef<StringRef> servers, unsigned int port = kDefaultPort);
|
||||
|
||||
/**
|
||||
* Sets server addresses and port for client (without restarting client).
|
||||
* Connects using commonly known robot addresses for the specified team.
|
||||
*
|
||||
* @param team team number
|
||||
* @param port port to communicate over
|
||||
*/
|
||||
void SetServerTeam(unsigned int team, unsigned int port = kDefaultPort);
|
||||
|
||||
/**
|
||||
* Starts requesting server address from Driver Station.
|
||||
* This connects to the Driver Station running on localhost to obtain the
|
||||
* server IP address.
|
||||
*
|
||||
* @param port server port to use in combination with IP from DS
|
||||
*/
|
||||
void StartDSClient(unsigned int port = kDefaultPort);
|
||||
|
||||
/**
|
||||
* Stops requesting server address from Driver Station.
|
||||
*/
|
||||
void StopDSClient();
|
||||
|
||||
/**
|
||||
* Set the periodic update rate.
|
||||
* Sets how frequently updates are sent to other nodes over the network.
|
||||
*
|
||||
* @param interval update interval in seconds (range 0.01 to 1.0)
|
||||
*/
|
||||
void SetUpdateRate(double interval);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void Flush() const;
|
||||
|
||||
/**
|
||||
* Get information on the currently established network connections.
|
||||
* If operating as a client, this will return either zero or one values.
|
||||
* @return array of connection information
|
||||
*/
|
||||
std::vector<ConnectionInfo> GetConnections() const;
|
||||
|
||||
/**
|
||||
* Return whether or not the instance is connected to another node.
|
||||
* @return True if connected.
|
||||
*/
|
||||
bool IsConnected() const;
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup FileFunctions File Save/Load Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Save persistent values to a file. The server automatically does this,
|
||||
* but this function provides a way to save persistent values in the same
|
||||
* format to a file on either a client or a server.
|
||||
* @param filename filename
|
||||
* @return error string, or nullptr if successful
|
||||
*/
|
||||
const char* SavePersistent(const Twine& filename) const;
|
||||
|
||||
/**
|
||||
* Load persistent values from a file. The server automatically does this
|
||||
* at startup, but this function provides a way to restore persistent values
|
||||
* in the same format from a file at any time on either a client or a server.
|
||||
* @param filename filename
|
||||
* @param warn callback function for warnings
|
||||
* @return error string, or nullptr if successful
|
||||
*/
|
||||
const char* LoadPersistent(
|
||||
const Twine& filename,
|
||||
std::function<void(size_t line, const char* msg)> warn);
|
||||
|
||||
/**
|
||||
* Save table values to a file. The file format used is identical to
|
||||
* that used for SavePersistent.
|
||||
* @param filename filename
|
||||
* @param prefix save only keys starting with this prefix
|
||||
* @return error string, or nullptr if successful
|
||||
*/
|
||||
const char* SaveEntries(const Twine& filename, const Twine& prefix) const;
|
||||
|
||||
/**
|
||||
* Load table values from a file. The file format used is identical to
|
||||
* that used for SavePersistent / LoadPersistent.
|
||||
* @param filename filename
|
||||
* @param prefix load only keys starting with this prefix
|
||||
* @param warn callback function for warnings
|
||||
* @return error string, or nullptr if successful
|
||||
*/
|
||||
const char* LoadEntries(
|
||||
const Twine& filename, const Twine& prefix,
|
||||
std::function<void(size_t line, const char* msg)> warn);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup LoggerFunctions Logger Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
NT_Logger AddLogger(std::function<void(const LogMessage& msg)> func,
|
||||
unsigned int min_level, unsigned int max_level);
|
||||
|
||||
/**
|
||||
* Remove a logger.
|
||||
* @param logger Logger handle to remove
|
||||
*/
|
||||
static void RemoveLogger(NT_Logger logger);
|
||||
|
||||
/**
|
||||
* Wait for the incoming log event queue to be empty. This is primarily
|
||||
* useful
|
||||
* for deterministic testing. This blocks until either the log event
|
||||
* 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 WaitForLoggerQueue(double timeout);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Equality operator. Returns true if both instances refer to the same
|
||||
* native handle.
|
||||
*/
|
||||
bool operator==(const NetworkTableInstance& other) const {
|
||||
return m_handle == other.m_handle;
|
||||
}
|
||||
|
||||
/** Inequality operator. */
|
||||
bool operator!=(const NetworkTableInstance& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
/* Native handle */
|
||||
NT_Inst m_handle;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#include "networktables/NetworkTableInstance.inl"
|
||||
|
||||
#endif // NTCORE_NETWORKTABLES_NETWORKTABLEINSTANCE_H_
|
||||
@@ -0,0 +1,187 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NT_INSTANCE_INL_
|
||||
#define NT_INSTANCE_INL_
|
||||
|
||||
namespace nt {
|
||||
|
||||
inline NetworkTableInstance::NetworkTableInstance() NT_NOEXCEPT : m_handle{0} {}
|
||||
|
||||
inline NetworkTableInstance::NetworkTableInstance(NT_Inst handle) NT_NOEXCEPT
|
||||
: m_handle{handle} {}
|
||||
|
||||
inline NetworkTableInstance NetworkTableInstance::GetDefault() {
|
||||
return NetworkTableInstance{GetDefaultInstance()};
|
||||
}
|
||||
|
||||
inline NetworkTableInstance NetworkTableInstance::Create() {
|
||||
return NetworkTableInstance{CreateInstance()};
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::Destroy(NetworkTableInstance inst) {
|
||||
if (inst.m_handle != 0) DestroyInstance(inst.m_handle);
|
||||
}
|
||||
|
||||
inline NT_Inst NetworkTableInstance::GetHandle() const { return m_handle; }
|
||||
|
||||
inline NetworkTableEntry NetworkTableInstance::GetEntry(const Twine& name) {
|
||||
return NetworkTableEntry{::nt::GetEntry(m_handle, name)};
|
||||
}
|
||||
|
||||
inline std::vector<NetworkTableEntry> NetworkTableInstance::GetEntries(
|
||||
const Twine& prefix, unsigned int types) {
|
||||
std::vector<NetworkTableEntry> entries;
|
||||
for (auto entry : ::nt::GetEntries(m_handle, prefix, types))
|
||||
entries.emplace_back(entry);
|
||||
return entries;
|
||||
}
|
||||
|
||||
inline std::vector<EntryInfo> NetworkTableInstance::GetEntryInfo(
|
||||
const Twine& prefix, unsigned int types) const {
|
||||
return ::nt::GetEntryInfo(m_handle, prefix, types);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::DeleteAllEntries() {
|
||||
::nt::DeleteAllEntries(m_handle);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::RemoveEntryListener(
|
||||
NT_EntryListener entry_listener) {
|
||||
::nt::RemoveEntryListener(entry_listener);
|
||||
}
|
||||
|
||||
inline bool NetworkTableInstance::WaitForEntryListenerQueue(double timeout) {
|
||||
return ::nt::WaitForEntryListenerQueue(m_handle, timeout);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::RemoveConnectionListener(
|
||||
NT_ConnectionListener conn_listener) {
|
||||
::nt::RemoveConnectionListener(conn_listener);
|
||||
}
|
||||
|
||||
inline bool NetworkTableInstance::WaitForConnectionListenerQueue(
|
||||
double timeout) {
|
||||
return ::nt::WaitForConnectionListenerQueue(m_handle, timeout);
|
||||
}
|
||||
|
||||
inline bool NetworkTableInstance::WaitForRpcCallQueue(double timeout) {
|
||||
return ::nt::WaitForRpcCallQueue(m_handle, timeout);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::SetNetworkIdentity(const Twine& name) {
|
||||
::nt::SetNetworkIdentity(m_handle, name);
|
||||
}
|
||||
|
||||
inline unsigned int NetworkTableInstance::GetNetworkMode() const {
|
||||
return ::nt::GetNetworkMode(m_handle);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::StartServer(const Twine& persist_filename,
|
||||
const char* listen_address,
|
||||
unsigned int port) {
|
||||
::nt::StartServer(m_handle, persist_filename, listen_address, port);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::StopServer() { ::nt::StopServer(m_handle); }
|
||||
|
||||
inline void NetworkTableInstance::StartClient() { ::nt::StartClient(m_handle); }
|
||||
|
||||
inline void NetworkTableInstance::StartClient(const char* server_name,
|
||||
unsigned int port) {
|
||||
::nt::StartClient(m_handle, server_name, port);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::StartClient(
|
||||
ArrayRef<std::pair<StringRef, unsigned int>> servers) {
|
||||
::nt::StartClient(m_handle, servers);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::StartClientTeam(unsigned int team,
|
||||
unsigned int port) {
|
||||
::nt::StartClientTeam(m_handle, team, port);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::StopClient() { ::nt::StopClient(m_handle); }
|
||||
|
||||
inline void NetworkTableInstance::SetServer(const char* server_name,
|
||||
unsigned int port) {
|
||||
::nt::SetServer(m_handle, server_name, port);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::SetServer(
|
||||
ArrayRef<std::pair<StringRef, unsigned int>> servers) {
|
||||
::nt::SetServer(m_handle, servers);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::SetServerTeam(unsigned int team,
|
||||
unsigned int port) {
|
||||
::nt::SetServerTeam(m_handle, team, port);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::StartDSClient(unsigned int port) {
|
||||
::nt::StartDSClient(m_handle, port);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::StopDSClient() {
|
||||
::nt::StopDSClient(m_handle);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::SetUpdateRate(double interval) {
|
||||
::nt::SetUpdateRate(m_handle, interval);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::Flush() const { ::nt::Flush(m_handle); }
|
||||
|
||||
inline std::vector<ConnectionInfo> NetworkTableInstance::GetConnections()
|
||||
const {
|
||||
return ::nt::GetConnections(m_handle);
|
||||
}
|
||||
|
||||
inline bool NetworkTableInstance::IsConnected() const {
|
||||
return ::nt::IsConnected(m_handle);
|
||||
}
|
||||
|
||||
inline const char* NetworkTableInstance::SavePersistent(
|
||||
const Twine& filename) const {
|
||||
return ::nt::SavePersistent(m_handle, filename);
|
||||
}
|
||||
|
||||
inline const char* NetworkTableInstance::LoadPersistent(
|
||||
const Twine& filename,
|
||||
std::function<void(size_t line, const char* msg)> warn) {
|
||||
return ::nt::LoadPersistent(m_handle, filename, warn);
|
||||
}
|
||||
|
||||
inline const char* NetworkTableInstance::SaveEntries(
|
||||
const Twine& filename, const Twine& prefix) const {
|
||||
return ::nt::SaveEntries(m_handle, filename, prefix);
|
||||
}
|
||||
|
||||
inline const char* NetworkTableInstance::LoadEntries(
|
||||
const Twine& filename, const Twine& prefix,
|
||||
std::function<void(size_t line, const char* msg)> warn) {
|
||||
return ::nt::LoadEntries(m_handle, filename, prefix, warn);
|
||||
}
|
||||
|
||||
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 bool NetworkTableInstance::WaitForLoggerQueue(double timeout) {
|
||||
return ::nt::WaitForLoggerQueue(m_handle, timeout);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NT_INSTANCE_INL_
|
||||
@@ -0,0 +1,29 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_NETWORKTABLES_NETWORKTABLETYPE_H_
|
||||
#define NTCORE_NETWORKTABLES_NETWORKTABLETYPE_H_
|
||||
|
||||
#include "ntcore_c.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
enum class NetworkTableType {
|
||||
kUnassigned = NT_UNASSIGNED,
|
||||
kBoolean = NT_BOOLEAN,
|
||||
kDouble = NT_DOUBLE,
|
||||
kString = NT_STRING,
|
||||
kRaw = NT_RAW,
|
||||
kBooleanArray = NT_BOOLEAN_ARRAY,
|
||||
kDoubleArray = NT_DOUBLE_ARRAY,
|
||||
kStringArray = NT_STRING_ARRAY,
|
||||
kRpc = NT_RPC
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_NETWORKTABLES_NETWORKTABLETYPE_H_
|
||||
402
ntcore/src/main/native/include/networktables/NetworkTableValue.h
Normal file
402
ntcore/src/main/native/include/networktables/NetworkTableValue.h
Normal file
@@ -0,0 +1,402 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_NETWORKTABLES_NETWORKTABLEVALUE_H_
|
||||
#define NTCORE_NETWORKTABLES_NETWORKTABLEVALUE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <llvm/ArrayRef.h>
|
||||
#include <llvm/StringRef.h>
|
||||
#include <llvm/Twine.h>
|
||||
|
||||
#include "ntcore_c.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
using llvm::ArrayRef;
|
||||
using llvm::StringRef;
|
||||
using llvm::Twine;
|
||||
|
||||
/**
|
||||
* A network table entry value.
|
||||
*/
|
||||
class Value final {
|
||||
struct private_init {};
|
||||
|
||||
public:
|
||||
Value();
|
||||
Value(NT_Type type, uint64_t time, const private_init&);
|
||||
~Value();
|
||||
|
||||
/**
|
||||
* Get the data type.
|
||||
* @return The type.
|
||||
*/
|
||||
NT_Type type() const { return m_val.type; }
|
||||
|
||||
/**
|
||||
* Get the data value stored.
|
||||
* @return The type.
|
||||
*/
|
||||
const NT_Value& value() const { return m_val; }
|
||||
|
||||
/**
|
||||
* Get the creation time of the value.
|
||||
* @return The time, in the units returned by nt::Now().
|
||||
*/
|
||||
uint64_t last_change() const { return m_val.last_change; }
|
||||
|
||||
/**
|
||||
* Get the creation time of the value.
|
||||
* @return The time, in the units returned by nt::Now().
|
||||
*/
|
||||
uint64_t time() const { return m_val.last_change; }
|
||||
|
||||
/**
|
||||
* @defgroup TypeCheckers Type Checkers
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a value or is unassigned.
|
||||
* @return True if the entry value contains a value.
|
||||
*/
|
||||
bool IsValid() const { return m_val.type != NT_UNASSIGNED; }
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a boolean.
|
||||
* @return True if the entry value is of boolean type.
|
||||
*/
|
||||
bool IsBoolean() const { return m_val.type == NT_BOOLEAN; }
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a double.
|
||||
* @return True if the entry value is of double type.
|
||||
*/
|
||||
bool IsDouble() const { return m_val.type == NT_DOUBLE; }
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a string.
|
||||
* @return True if the entry value is of string type.
|
||||
*/
|
||||
bool IsString() const { return m_val.type == NT_STRING; }
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a raw.
|
||||
* @return True if the entry value is of raw type.
|
||||
*/
|
||||
bool IsRaw() const { return m_val.type == NT_RAW; }
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a rpc definition.
|
||||
* @return True if the entry value is of rpc definition type.
|
||||
*/
|
||||
bool IsRpc() const { return m_val.type == NT_RPC; }
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a boolean array.
|
||||
* @return True if the entry value is of boolean array type.
|
||||
*/
|
||||
bool IsBooleanArray() const { return m_val.type == NT_BOOLEAN_ARRAY; }
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a double array.
|
||||
* @return True if the entry value is of double array type.
|
||||
*/
|
||||
bool IsDoubleArray() const { return m_val.type == NT_DOUBLE_ARRAY; }
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a string array.
|
||||
* @return True if the entry value is of string array type.
|
||||
*/
|
||||
bool IsStringArray() const { return m_val.type == NT_STRING_ARRAY; }
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup TypeSafeGetters Type-Safe Getters
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the entry's boolean value.
|
||||
* @return The boolean value.
|
||||
*/
|
||||
bool GetBoolean() const {
|
||||
assert(m_val.type == NT_BOOLEAN);
|
||||
return m_val.data.v_boolean != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's double value.
|
||||
* @return The double value.
|
||||
*/
|
||||
double GetDouble() const {
|
||||
assert(m_val.type == NT_DOUBLE);
|
||||
return m_val.data.v_double;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's string value.
|
||||
* @return The string value.
|
||||
*/
|
||||
StringRef GetString() const {
|
||||
assert(m_val.type == NT_STRING);
|
||||
return m_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's raw value.
|
||||
* @return The raw value.
|
||||
*/
|
||||
StringRef GetRaw() const {
|
||||
assert(m_val.type == NT_RAW);
|
||||
return m_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's rpc definition value.
|
||||
* @return The rpc definition value.
|
||||
*/
|
||||
StringRef GetRpc() const {
|
||||
assert(m_val.type == NT_RPC);
|
||||
return m_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's boolean array value.
|
||||
* @return The boolean array value.
|
||||
*/
|
||||
ArrayRef<int> GetBooleanArray() const {
|
||||
assert(m_val.type == NT_BOOLEAN_ARRAY);
|
||||
return ArrayRef<int>(m_val.data.arr_boolean.arr,
|
||||
m_val.data.arr_boolean.size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's double array value.
|
||||
* @return The double array value.
|
||||
*/
|
||||
ArrayRef<double> GetDoubleArray() const {
|
||||
assert(m_val.type == NT_DOUBLE_ARRAY);
|
||||
return ArrayRef<double>(m_val.data.arr_double.arr,
|
||||
m_val.data.arr_double.size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's string array value.
|
||||
* @return The string array value.
|
||||
*/
|
||||
ArrayRef<std::string> GetStringArray() const {
|
||||
assert(m_val.type == NT_STRING_ARRAY);
|
||||
return m_string_array;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup Factories Factory functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a boolean entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
static std::shared_ptr<Value> MakeBoolean(bool value, uint64_t time = 0) {
|
||||
auto val = std::make_shared<Value>(NT_BOOLEAN, time, private_init());
|
||||
val->m_val.data.v_boolean = value;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a double entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
static std::shared_ptr<Value> MakeDouble(double value, uint64_t time = 0) {
|
||||
auto val = std::make_shared<Value>(NT_DOUBLE, time, private_init());
|
||||
val->m_val.data.v_double = value;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
static std::shared_ptr<Value> MakeString(const Twine& value,
|
||||
uint64_t time = 0) {
|
||||
auto val = std::make_shared<Value>(NT_STRING, time, private_init());
|
||||
val->m_string = value.str();
|
||||
val->m_val.data.v_string.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_string.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
template <typename T,
|
||||
typename = std::enable_if_t<std::is_same<T, std::string>>>
|
||||
#else
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_same<T, std::string>::value>::type>
|
||||
#endif
|
||||
static std::shared_ptr<Value> MakeString(T&& value, uint64_t time = 0) {
|
||||
auto val = std::make_shared<Value>(NT_STRING, time, private_init());
|
||||
val->m_string = std::move(value);
|
||||
val->m_val.data.v_string.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_string.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a raw entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
static std::shared_ptr<Value> MakeRaw(StringRef value, uint64_t time = 0) {
|
||||
auto val = std::make_shared<Value>(NT_RAW, time, private_init());
|
||||
val->m_string = value;
|
||||
val->m_val.data.v_raw.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_raw.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a raw entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
template <typename T,
|
||||
typename = std::enable_if_t<std::is_same<T, std::string>>>
|
||||
#else
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_same<T, std::string>::value>::type>
|
||||
#endif
|
||||
static std::shared_ptr<Value> MakeRaw(T&& value, uint64_t time = 0) {
|
||||
auto val = std::make_shared<Value>(NT_RAW, time, private_init());
|
||||
val->m_string = std::move(value);
|
||||
val->m_val.data.v_raw.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_raw.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rpc entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
static std::shared_ptr<Value> MakeRpc(StringRef value, uint64_t time = 0) {
|
||||
auto val = std::make_shared<Value>(NT_RPC, time, private_init());
|
||||
val->m_string = value;
|
||||
val->m_val.data.v_raw.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_raw.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rpc entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
template <typename T>
|
||||
static std::shared_ptr<Value> MakeRpc(T&& value, uint64_t time = 0) {
|
||||
auto val = std::make_shared<Value>(NT_RPC, time, private_init());
|
||||
val->m_string = std::move(value);
|
||||
val->m_val.data.v_raw.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_raw.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boolean array entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
static std::shared_ptr<Value> MakeBooleanArray(ArrayRef<int> value,
|
||||
uint64_t time = 0);
|
||||
|
||||
/**
|
||||
* Creates a double array entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
static std::shared_ptr<Value> MakeDoubleArray(ArrayRef<double> value,
|
||||
uint64_t time = 0);
|
||||
|
||||
/**
|
||||
* Creates a string array entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
static std::shared_ptr<Value> MakeStringArray(ArrayRef<std::string> value,
|
||||
uint64_t time = 0);
|
||||
|
||||
// Note: This function moves the values out of the vector.
|
||||
static std::shared_ptr<Value> MakeStringArray(
|
||||
std::vector<std::string>&& value, uint64_t time = 0);
|
||||
|
||||
/** @} */
|
||||
|
||||
Value(const Value&) = delete;
|
||||
Value& operator=(const Value&) = delete;
|
||||
friend bool operator==(const Value& lhs, const Value& rhs);
|
||||
|
||||
private:
|
||||
NT_Value m_val;
|
||||
std::string m_string;
|
||||
std::vector<std::string> m_string_array;
|
||||
};
|
||||
|
||||
bool operator==(const Value& lhs, const Value& rhs);
|
||||
inline bool operator!=(const Value& lhs, const Value& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/** NetworkTable Value alias for similarity with Java. */
|
||||
typedef Value NetworkTableValue;
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_NETWORKTABLES_NETWORKTABLEVALUE_H_
|
||||
100
ntcore/src/main/native/include/networktables/RpcCall.h
Normal file
100
ntcore/src/main/native/include/networktables/RpcCall.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_NETWORKTABLES_RPCCALL_H_
|
||||
#define NTCORE_NETWORKTABLES_RPCCALL_H_
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "ntcore_c.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class NetworkTableEntry;
|
||||
|
||||
/** NetworkTables Remote Procedure Call */
|
||||
class RpcCall final {
|
||||
public:
|
||||
/**
|
||||
* Construct invalid instance.
|
||||
*/
|
||||
RpcCall() : m_entry(0), m_call(0) {}
|
||||
|
||||
/**
|
||||
* Construct from native handles.
|
||||
* @param entry Entry handle
|
||||
* @param call Call handle
|
||||
*/
|
||||
RpcCall(NT_Entry entry, NT_RpcCall call) : m_entry(entry), m_call(call) {}
|
||||
|
||||
RpcCall(RpcCall&& other);
|
||||
RpcCall(const RpcCall&) = delete;
|
||||
RpcCall& operator=(const RpcCall&) = delete;
|
||||
|
||||
/**
|
||||
* Destructor. Cancels the result if no other action taken.
|
||||
*/
|
||||
~RpcCall();
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
explicit operator bool() const { return m_call != 0; }
|
||||
|
||||
/**
|
||||
* Get the RPC entry.
|
||||
* @return NetworkTableEntry for the RPC.
|
||||
*/
|
||||
NetworkTableEntry GetEntry() const;
|
||||
|
||||
/**
|
||||
* Get the call native handle.
|
||||
* @return Native handle.
|
||||
*/
|
||||
NT_RpcCall GetCall() const { return m_call; }
|
||||
|
||||
/**
|
||||
* Get the result (return value). This function blocks until
|
||||
* the result is received.
|
||||
* @param result received result (output)
|
||||
* @return False on error, true otherwise.
|
||||
*/
|
||||
bool GetResult(std::string* result);
|
||||
|
||||
/**
|
||||
* Get the result (return value). This function blocks until
|
||||
* the result is received or it times out.
|
||||
* @param result received result (output)
|
||||
* @param timeout timeout, in seconds
|
||||
* @param timed_out true if the timeout period elapsed (output)
|
||||
* @return False on error or timeout, true otherwise.
|
||||
*/
|
||||
bool GetResult(std::string* result, double timeout, bool* timed_out);
|
||||
|
||||
/**
|
||||
* Ignore the result. This function is non-blocking.
|
||||
*/
|
||||
void CancelResult();
|
||||
|
||||
friend void swap(RpcCall& first, RpcCall& second) {
|
||||
using std::swap;
|
||||
swap(first.m_entry, second.m_entry);
|
||||
swap(first.m_call, second.m_call);
|
||||
}
|
||||
|
||||
private:
|
||||
NT_Entry m_entry;
|
||||
NT_RpcCall m_call;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#include "networktables/RpcCall.inl"
|
||||
|
||||
#endif // NTCORE_NETWORKTABLES_RPCCALL_H_
|
||||
48
ntcore/src/main/native/include/networktables/RpcCall.inl
Normal file
48
ntcore/src/main/native/include/networktables/RpcCall.inl
Normal file
@@ -0,0 +1,48 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NT_RPCCALL_INL_
|
||||
#define NT_RPCCALL_INL_
|
||||
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
inline RpcCall::RpcCall(RpcCall&& other) : RpcCall() {
|
||||
swap(*this, other);
|
||||
}
|
||||
|
||||
inline RpcCall::~RpcCall() {
|
||||
// automatically cancel result if user didn't request it
|
||||
if (m_call != 0) CancelResult();
|
||||
}
|
||||
|
||||
inline bool RpcCall::GetResult(std::string* result) {
|
||||
if (GetRpcResult(m_entry, m_call, result)) {
|
||||
m_call = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool RpcCall::GetResult(std::string* result, double timeout,
|
||||
bool* timed_out) {
|
||||
if (GetRpcResult(m_entry, m_call, result, timeout, timed_out)) {
|
||||
m_call = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void RpcCall::CancelResult() {
|
||||
CancelRpcResult(m_entry, m_call);
|
||||
m_call = 0;
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NT_RPCCALL_INL_
|
||||
@@ -0,0 +1,43 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_NETWORKTABLES_TABLEENTRYLISTENER_H_
|
||||
#define NTCORE_NETWORKTABLES_TABLEENTRYLISTENER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include <llvm/StringRef.h>
|
||||
|
||||
namespace nt {
|
||||
|
||||
class NetworkTable;
|
||||
class NetworkTableEntry;
|
||||
class Value;
|
||||
|
||||
using llvm::StringRef;
|
||||
|
||||
/**
|
||||
* A listener that listens to changes in values in a NetworkTable.
|
||||
*
|
||||
* Called when a key-value pair is changed in a NetworkTable.
|
||||
*
|
||||
* @param table the table the key-value pair exists in
|
||||
* @param key the key associated with the value that changed
|
||||
* @param entry the entry associated with the value that changed
|
||||
* @param value the new value
|
||||
* @param flags update flags; for example, EntryListenerFlags.kNew if the key
|
||||
* did not previously exist
|
||||
*/
|
||||
typedef std::function<void(NetworkTable* table, StringRef name,
|
||||
NetworkTableEntry entry,
|
||||
std::shared_ptr<Value> value, int flags)>
|
||||
TableEntryListener;
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_NETWORKTABLES_TABLEENTRYLISTENER_H_
|
||||
37
ntcore/src/main/native/include/networktables/TableListener.h
Normal file
37
ntcore/src/main/native/include/networktables/TableListener.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_NETWORKTABLES_TABLELISTENER_H_
|
||||
#define NTCORE_NETWORKTABLES_TABLELISTENER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include <llvm/StringRef.h>
|
||||
|
||||
namespace nt {
|
||||
|
||||
class NetworkTable;
|
||||
|
||||
using llvm::StringRef;
|
||||
|
||||
/**
|
||||
* A listener that listens to new sub-tables in a NetworkTable.
|
||||
*
|
||||
* Called when a new table is created.
|
||||
*
|
||||
* @param parent the parent of the table
|
||||
* @param name the name of the new table
|
||||
* @param table the new table
|
||||
*/
|
||||
typedef std::function<void(NetworkTable* parent, StringRef name,
|
||||
NetworkTable* table)>
|
||||
TableListener;
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_NETWORKTABLES_TABLELISTENER_H_
|
||||
19
ntcore/src/main/native/include/ntcore.h
Normal file
19
ntcore/src/main/native/include/ntcore.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_NTCORE_H_
|
||||
#define NTCORE_NTCORE_H_
|
||||
|
||||
/* C API */
|
||||
#include "ntcore_c.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
/* C++ API */
|
||||
#include "ntcore_cpp.h"
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif // NTCORE_NTCORE_H_
|
||||
1955
ntcore/src/main/native/include/ntcore_c.h
Normal file
1955
ntcore/src/main/native/include/ntcore_c.h
Normal file
File diff suppressed because it is too large
Load Diff
1489
ntcore/src/main/native/include/ntcore_cpp.h
Normal file
1489
ntcore/src/main/native/include/ntcore_cpp.h
Normal file
File diff suppressed because it is too large
Load Diff
89
ntcore/src/main/native/include/ntcore_test.h
Normal file
89
ntcore/src/main/native/include/ntcore_test.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_NTCORE_TEST_H_
|
||||
#define NTCORE_NTCORE_TEST_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ntcore.h"
|
||||
|
||||
// Functions in this header are to be used only for testing
|
||||
|
||||
extern "C" {
|
||||
struct NT_String* NT_GetStringForTesting(const char* string, int* struct_size);
|
||||
// No need for free as one already exists in main library
|
||||
|
||||
struct NT_EntryInfo* NT_GetEntryInfoForTesting(const char* name,
|
||||
enum NT_Type type,
|
||||
unsigned int flags,
|
||||
uint64_t last_change,
|
||||
int* struct_size);
|
||||
|
||||
void NT_FreeEntryInfoForTesting(struct NT_EntryInfo* info);
|
||||
|
||||
struct NT_ConnectionInfo* NT_GetConnectionInfoForTesting(
|
||||
const char* remote_id, const char* remote_ip, unsigned int remote_port,
|
||||
uint64_t last_update, unsigned int protocol_version, int* struct_size);
|
||||
|
||||
void NT_FreeConnectionInfoForTesting(struct NT_ConnectionInfo* info);
|
||||
|
||||
struct NT_Value* NT_GetValueBooleanForTesting(uint64_t last_change, int val,
|
||||
int* struct_size);
|
||||
|
||||
struct NT_Value* NT_GetValueDoubleForTesting(uint64_t last_change, double val,
|
||||
int* struct_size);
|
||||
|
||||
struct NT_Value* NT_GetValueStringForTesting(uint64_t last_change,
|
||||
const char* str, int* struct_size);
|
||||
|
||||
struct NT_Value* NT_GetValueRawForTesting(uint64_t last_change, const char* raw,
|
||||
int raw_len, int* struct_size);
|
||||
|
||||
struct NT_Value* NT_GetValueBooleanArrayForTesting(uint64_t last_change,
|
||||
const int* arr,
|
||||
size_t array_len,
|
||||
int* struct_size);
|
||||
|
||||
struct NT_Value* NT_GetValueDoubleArrayForTesting(uint64_t last_change,
|
||||
const double* arr,
|
||||
size_t array_len,
|
||||
int* struct_size);
|
||||
|
||||
struct NT_Value* NT_GetValueStringArrayForTesting(uint64_t last_change,
|
||||
const struct NT_String* arr,
|
||||
size_t array_len,
|
||||
int* struct_size);
|
||||
// No need for free as one already exists in the main library
|
||||
|
||||
struct NT_RpcParamDef* NT_GetRpcParamDefForTesting(const char* name,
|
||||
const struct NT_Value* val,
|
||||
int* struct_size);
|
||||
|
||||
void NT_FreeRpcParamDefForTesting(struct NT_RpcParamDef* def);
|
||||
|
||||
struct NT_RpcResultDef* NT_GetRpcResultsDefForTesting(const char* name,
|
||||
enum NT_Type type,
|
||||
int* struct_size);
|
||||
|
||||
void NT_FreeRpcResultsDefForTesting(struct NT_RpcResultDef* def);
|
||||
|
||||
struct NT_RpcDefinition* NT_GetRpcDefinitionForTesting(
|
||||
unsigned int version, const char* name, size_t num_params,
|
||||
const struct NT_RpcParamDef* params, size_t num_results,
|
||||
const struct NT_RpcResultDef* results, int* struct_size);
|
||||
// No need for free as one already exists in the main library
|
||||
|
||||
struct NT_RpcCallInfo* NT_GetRpcCallInfoForTesting(
|
||||
unsigned int rpc_id, unsigned int call_uid, const char* name,
|
||||
const char* params, size_t params_len, int* struct_size);
|
||||
// No need for free as one already exists in the main library
|
||||
} // extern "C"
|
||||
|
||||
#endif // NTCORE_NTCORE_TEST_H_
|
||||
456
ntcore/src/main/native/include/tables/ITable.h
Normal file
456
ntcore/src/main/native/include/tables/ITable.h
Normal file
@@ -0,0 +1,456 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_TABLES_ITABLE_H_
|
||||
#define NTCORE_TABLES_ITABLE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <llvm/StringRef.h>
|
||||
#include <llvm/Twine.h>
|
||||
#include <support/deprecated.h>
|
||||
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
|
||||
namespace nt {
|
||||
class NetworkTable;
|
||||
} // namespace nt
|
||||
|
||||
class ITableListener;
|
||||
|
||||
/**
|
||||
* A table whose values can be read and written to
|
||||
*/
|
||||
class WPI_DEPRECATED("Use NetworkTable directly") ITable {
|
||||
public:
|
||||
/**
|
||||
* Determines whether the given key is in this table.
|
||||
*
|
||||
* @param key the key to search for
|
||||
* @return true if the table as a value assigned to the given key
|
||||
*/
|
||||
virtual bool ContainsKey(const llvm::Twine& key) const = 0;
|
||||
|
||||
/**
|
||||
* Determines whether there exists a non-empty subtable for this key
|
||||
* in this table.
|
||||
*
|
||||
* @param key the key to search for
|
||||
* @return true if there is a subtable with the key which contains at least
|
||||
* one key/subtable of its own
|
||||
*/
|
||||
virtual bool ContainsSubTable(const llvm::Twine& key) const = 0;
|
||||
|
||||
/**
|
||||
* Gets the subtable in this table for the given name.
|
||||
*
|
||||
* @param key the name of the table relative to this one
|
||||
* @return a sub table relative to this one
|
||||
*/
|
||||
virtual std::shared_ptr<nt::NetworkTable> GetSubTable(
|
||||
const llvm::Twine& key) const = 0;
|
||||
|
||||
/**
|
||||
* @param types bitmask of types; 0 is treated as a "don't care".
|
||||
* @return keys currently in the table
|
||||
*/
|
||||
virtual std::vector<std::string> GetKeys(int types = 0) const = 0;
|
||||
|
||||
/**
|
||||
* @return subtables currently in the table
|
||||
*/
|
||||
virtual std::vector<std::string> GetSubTables() const = 0;
|
||||
|
||||
/**
|
||||
* Makes a key's value persistent through program restarts.
|
||||
*
|
||||
* @param key the key to make persistent
|
||||
*/
|
||||
virtual void SetPersistent(llvm::StringRef key) = 0;
|
||||
|
||||
/**
|
||||
* Stop making a key's value persistent through program restarts.
|
||||
* The key cannot be null.
|
||||
*
|
||||
* @param key the key name
|
||||
*/
|
||||
virtual void ClearPersistent(llvm::StringRef key) = 0;
|
||||
|
||||
/**
|
||||
* Returns whether the value is persistent through program restarts.
|
||||
* The key cannot be null.
|
||||
*
|
||||
* @param key the key name
|
||||
*/
|
||||
virtual bool IsPersistent(llvm::StringRef key) const = 0;
|
||||
|
||||
/**
|
||||
* Sets flags on the specified key in this table. The key can
|
||||
* not be null.
|
||||
*
|
||||
* @param key the key name
|
||||
* @param flags the flags to set (bitmask)
|
||||
*/
|
||||
virtual void SetFlags(llvm::StringRef key, unsigned int flags) = 0;
|
||||
|
||||
/**
|
||||
* Clears flags on the specified key in this table. The key can
|
||||
* not be null.
|
||||
*
|
||||
* @param key the key name
|
||||
* @param flags the flags to clear (bitmask)
|
||||
*/
|
||||
virtual void ClearFlags(llvm::StringRef key, unsigned int flags) = 0;
|
||||
|
||||
/**
|
||||
* Returns the flags for the specified key.
|
||||
*
|
||||
* @param key the key name
|
||||
* @return the flags, or 0 if the key is not defined
|
||||
*/
|
||||
virtual unsigned int GetFlags(llvm::StringRef key) const = 0;
|
||||
|
||||
/**
|
||||
* Deletes the specified key in this table.
|
||||
*
|
||||
* @param key the key name
|
||||
*/
|
||||
virtual void Delete(const llvm::Twine& key) = 0;
|
||||
|
||||
/**
|
||||
* Gets the value associated with a key as an object
|
||||
*
|
||||
* @param key the key of the value to look up
|
||||
* @return the value associated with the given key, or nullptr if the key
|
||||
* does not exist
|
||||
*/
|
||||
virtual std::shared_ptr<nt::Value> GetValue(const llvm::Twine& key) const = 0;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
virtual bool SetDefaultValue(const llvm::Twine& key,
|
||||
std::shared_ptr<nt::Value> defaultValue) = 0;
|
||||
|
||||
/**
|
||||
* Put a value in the table
|
||||
*
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
virtual bool PutValue(const llvm::Twine& key,
|
||||
std::shared_ptr<nt::Value> value) = 0;
|
||||
|
||||
/**
|
||||
* Put a number in the table
|
||||
*
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
virtual bool PutNumber(llvm::StringRef key, double value) = 0;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
virtual bool SetDefaultNumber(llvm::StringRef key, double defaultValue) = 0;
|
||||
|
||||
/**
|
||||
* Gets the number associated with the given name.
|
||||
*
|
||||
* @param key the key to look up
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the value associated with the given key or the given default value
|
||||
* if there is no value associated with the key
|
||||
*/
|
||||
virtual double GetNumber(llvm::StringRef key, double defaultValue) const = 0;
|
||||
|
||||
/**
|
||||
* Put a string in the table
|
||||
*
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
virtual bool PutString(llvm::StringRef key, llvm::StringRef value) = 0;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
virtual bool SetDefaultString(llvm::StringRef key,
|
||||
llvm::StringRef defaultValue) = 0;
|
||||
|
||||
/**
|
||||
* Gets the string associated with the given name. If the key does not
|
||||
* exist or is of different type, it will return the default value.
|
||||
*
|
||||
* @param key the key to look up
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the value associated with the given key or the given default value
|
||||
* if there is no value associated with the key
|
||||
*
|
||||
* @note This makes a copy of the string. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*/
|
||||
virtual std::string GetString(llvm::StringRef key,
|
||||
llvm::StringRef defaultValue) const = 0;
|
||||
|
||||
/**
|
||||
* Put a boolean in the table
|
||||
*
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
virtual bool PutBoolean(llvm::StringRef key, bool value) = 0;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
virtual bool SetDefaultBoolean(llvm::StringRef key, bool defaultValue) = 0;
|
||||
|
||||
/**
|
||||
* Gets the boolean associated with the given name. If the key does not
|
||||
* exist or is of different type, it will return the default value.
|
||||
*
|
||||
* @param key the key to look up
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the value associated with the given key or the given default value
|
||||
* if there is no value associated with the key
|
||||
*/
|
||||
virtual bool GetBoolean(llvm::StringRef key, bool defaultValue) const = 0;
|
||||
|
||||
/**
|
||||
* Put a boolean array in the table
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*
|
||||
* @note The array must be of int's rather than of bool's because
|
||||
* std::vector<bool> is special-cased in C++. 0 is false, any
|
||||
* non-zero value is true.
|
||||
*/
|
||||
virtual bool PutBooleanArray(llvm::StringRef key,
|
||||
llvm::ArrayRef<int> value) = 0;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
virtual bool SetDefaultBooleanArray(llvm::StringRef key,
|
||||
llvm::ArrayRef<int> defaultValue) = 0;
|
||||
|
||||
/**
|
||||
* Returns the boolean array the key maps to. If the key does not exist or is
|
||||
* of different type, it will return the default value.
|
||||
* @param key the key to look up
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the value associated with the given key or the given default value
|
||||
* if there is no value associated with the key
|
||||
*
|
||||
* @note This makes a copy of the array. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*
|
||||
* @note The returned array is std::vector<int> instead of std::vector<bool>
|
||||
* because std::vector<bool> is special-cased in C++. 0 is false, any
|
||||
* non-zero value is true.
|
||||
*/
|
||||
virtual std::vector<int> GetBooleanArray(
|
||||
llvm::StringRef key, llvm::ArrayRef<int> defaultValue) const = 0;
|
||||
|
||||
/**
|
||||
* Put a number array in the table
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
virtual bool PutNumberArray(llvm::StringRef key,
|
||||
llvm::ArrayRef<double> value) = 0;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
virtual bool SetDefaultNumberArray(llvm::StringRef key,
|
||||
llvm::ArrayRef<double> defaultValue) = 0;
|
||||
|
||||
/**
|
||||
* Returns the number array the key maps to. If the key does not exist or is
|
||||
* of different type, it will return the default value.
|
||||
* @param key the key to look up
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the value associated with the given key or the given default value
|
||||
* if there is no value associated with the key
|
||||
*
|
||||
* @note This makes a copy of the array. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*/
|
||||
virtual std::vector<double> GetNumberArray(
|
||||
llvm::StringRef key, llvm::ArrayRef<double> defaultValue) const = 0;
|
||||
|
||||
/**
|
||||
* Put a string array in the table
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
virtual bool PutStringArray(llvm::StringRef key,
|
||||
llvm::ArrayRef<std::string> value) = 0;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
virtual bool SetDefaultStringArray(
|
||||
llvm::StringRef key, llvm::ArrayRef<std::string> defaultValue) = 0;
|
||||
|
||||
/**
|
||||
* Returns the string array the key maps to. If the key does not exist or is
|
||||
* of different type, it will return the default value.
|
||||
* @param key the key to look up
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the value associated with the given key or the given default value
|
||||
* if there is no value associated with the key
|
||||
*
|
||||
* @note This makes a copy of the array. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*/
|
||||
virtual std::vector<std::string> GetStringArray(
|
||||
llvm::StringRef key, llvm::ArrayRef<std::string> defaultValue) const = 0;
|
||||
|
||||
/**
|
||||
* Put a raw value (byte array) in the table
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
virtual bool PutRaw(llvm::StringRef key, llvm::StringRef value) = 0;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
virtual bool SetDefaultRaw(llvm::StringRef key,
|
||||
llvm::StringRef defaultValue) = 0;
|
||||
|
||||
/**
|
||||
* Returns the raw value (byte array) the key maps to. If the key does not
|
||||
* exist or is of different type, it will return the default value.
|
||||
* @param key the key to look up
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the value associated with the given key or the given default value
|
||||
* if there is no value associated with the key
|
||||
*
|
||||
* @note This makes a copy of the raw contents. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*/
|
||||
virtual std::string GetRaw(llvm::StringRef key,
|
||||
llvm::StringRef defaultValue) const = 0;
|
||||
|
||||
/**
|
||||
* Add a listener for changes to the table
|
||||
*
|
||||
* @param listener the listener to add
|
||||
*/
|
||||
virtual void AddTableListener(ITableListener* listener) = 0;
|
||||
|
||||
/**
|
||||
* Add a listener for changes to the table
|
||||
*
|
||||
* @param listener the listener to add
|
||||
* @param immediateNotify if true then this listener will be notified of all
|
||||
* current entries (marked as new)
|
||||
*/
|
||||
virtual void AddTableListener(ITableListener* listener,
|
||||
bool immediateNotify) = 0;
|
||||
|
||||
/**
|
||||
* Add a listener for changes to the table
|
||||
*
|
||||
* @param listener the listener to add
|
||||
* @param immediateNotify if true then this listener will be notified of all
|
||||
* current entries (marked as new)
|
||||
* @param flags bitmask of NT_NotifyKind specifying desired notifications
|
||||
*/
|
||||
virtual void AddTableListenerEx(ITableListener* listener,
|
||||
unsigned int flags) = 0;
|
||||
|
||||
/**
|
||||
* Add a listener for changes to a specific key the table
|
||||
*
|
||||
* @param key the key to listen for
|
||||
* @param listener the listener to add
|
||||
* @param immediateNotify if true then this listener will be notified of all
|
||||
* current entries (marked as new)
|
||||
*/
|
||||
virtual void AddTableListener(llvm::StringRef key, ITableListener* listener,
|
||||
bool immediateNotify) = 0;
|
||||
|
||||
/**
|
||||
* Add a listener for changes to a specific key the table
|
||||
*
|
||||
* @param key the key to listen for
|
||||
* @param listener the listener to add
|
||||
* @param immediateNotify if true then this listener will be notified of all
|
||||
* current entries (marked as new)
|
||||
* @param flags bitmask of NT_NotifyKind specifying desired notifications
|
||||
*/
|
||||
virtual void AddTableListenerEx(llvm::StringRef key, ITableListener* listener,
|
||||
unsigned int flags) = 0;
|
||||
|
||||
/**
|
||||
* This will immediately notify the listener of all current sub tables
|
||||
* @param listener the listener to add
|
||||
*/
|
||||
virtual void AddSubTableListener(ITableListener* listener) = 0;
|
||||
|
||||
/**
|
||||
* This will immediately notify the listener of all current sub tables
|
||||
* @param listener the listener to add
|
||||
* @param localNotify if true then this listener will be notified of all
|
||||
* local changes in addition to all remote changes
|
||||
*/
|
||||
virtual void AddSubTableListener(ITableListener* listener,
|
||||
bool localNotify) = 0;
|
||||
|
||||
/**
|
||||
* Remove a listener from receiving table events
|
||||
*
|
||||
* @param listener the listener to be removed
|
||||
*/
|
||||
virtual void RemoveTableListener(ITableListener* listener) = 0;
|
||||
|
||||
/**
|
||||
* Gets the full path of this table.
|
||||
*/
|
||||
virtual llvm::StringRef GetPath() const = 0;
|
||||
};
|
||||
|
||||
#endif // NTCORE_TABLES_ITABLE_H_
|
||||
63
ntcore/src/main/native/include/tables/ITableListener.h
Normal file
63
ntcore/src/main/native/include/tables/ITableListener.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_TABLES_ITABLELISTENER_H_
|
||||
#define NTCORE_TABLES_ITABLELISTENER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <llvm/StringRef.h>
|
||||
#include <support/deprecated.h>
|
||||
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
class ITable;
|
||||
|
||||
/**
|
||||
* A listener that listens to changes in values in a {@link ITable}
|
||||
*/
|
||||
class WPI_DEPRECATED(
|
||||
"Use EntryListener, TableEntryListener, or TableListener as appropriate")
|
||||
ITableListener {
|
||||
public:
|
||||
virtual ~ITableListener() = default;
|
||||
/**
|
||||
* Called when a key-value pair is changed in a {@link ITable}
|
||||
* @param source the table the key-value pair exists in
|
||||
* @param key the key associated with the value that changed
|
||||
* @param value the new value
|
||||
* @param isNew true if the key did not previously exist in the table,
|
||||
* otherwise it is false
|
||||
*/
|
||||
virtual void ValueChanged(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value, bool isNew) = 0;
|
||||
|
||||
/**
|
||||
* Extended version of ValueChanged. Called when a key-value pair is
|
||||
* changed in a {@link ITable}. The default implementation simply calls
|
||||
* ValueChanged(). If this is overridden, ValueChanged() will not be called.
|
||||
* @param source the table the key-value pair exists in
|
||||
* @param key the key associated with the value that changed
|
||||
* @param value the new value
|
||||
* @param flags update flags; for example, NT_NOTIFY_NEW if the key did not
|
||||
* previously exist in the table
|
||||
*/
|
||||
virtual void ValueChangedEx(ITable* source, llvm::StringRef key,
|
||||
std::shared_ptr<nt::Value> value,
|
||||
unsigned int flags);
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // NTCORE_TABLES_ITABLELISTENER_H_
|
||||
Reference in New Issue
Block a user