Implement independent instances.

Previously, most of the classes were implemented as singletons so only one
instance was possible.

This change adds an instance handle-based API.  In Java, this API is located
in a different package than the old API (edu.wpi.first.networktables).

Backwards compatibility with ITable and the old NetworkTable API is largely
maintained, but a handful of classes have moved to the new package in Java
(ConnectionInfo and PersistentException), and the old JNI has been completed
replaced.

Also:
- Move SetTeam implementation to Dispatcher.
- Consistently pass time through Java and C++ Value API.
- Rename nt_Value.h to NetworkTableValue.h for consistency with Java.
- Improve documentation
- Make C++ and Java APIs more consistent
- Document RPC functions and support RPC in Java.
- Add polling features for entry and connection listeners and use them to
  move callback threads to Java level.
- Remove thread start and stop hooks (as polling is available).
- Make Notifiers, RpcServer, Dispatcher, and Storage mockable.
- Set NOTIFY_NEW on immediate entry notifications.
- Make GetTable("/") and GetTable("") equivalent.
- Generate local notification for flags update when loading persistent file.

And many unit test updates/changes:
- Use InitGoogleMock instead of InitGoogleTest in test main.
- Move test printers to TestPrinter.h/cpp.
- Provide printers for StringRef, EntryNotifier, and Handle.
- StorageTest: Check notifications.
- Add entry notifier unit tests.
- Storage: Add test for incoming entry assignment.
- Update connection listener tests.
- Add entry listener unit tests.

Fixes #11, #140, #189, #190, #192, #193, #221
This commit is contained in:
Peter Johnson
2017-04-23 10:26:17 -07:00
parent 8125a179fb
commit 5ab20bb27c
118 changed files with 15638 additions and 4640 deletions

View File

@@ -0,0 +1,338 @@
/*----------------------------------------------------------------------------*/
/* 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_CALLBACKMANAGER_H_
#define NT_CALLBACKMANAGER_H_
#include <atomic>
#include <climits>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <queue>
#include <utility>
#include "llvm/raw_ostream.h"
#include "support/SafeThread.h"
#include "support/UidVector.h"
namespace nt {
namespace impl {
template <typename Callback>
class ListenerData {
public:
ListenerData() = default;
ListenerData(Callback callback_) : callback(callback_) {}
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 (std::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;
std::condition_variable m_queue_empty;
struct Poller {
void Terminate() {
{
std::lock_guard<std::mutex> lock(poll_mutex);
terminating = true;
}
poll_cond.notify_all();
}
std::queue<NotifierData> poll_queue;
std::mutex poll_mutex;
std::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<std::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<std::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 (std::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<std::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<std::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 // NT_CALLBACKMANAGER_H_

View File

@@ -0,0 +1,29 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. 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);
}

View File

@@ -0,0 +1,73 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. 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_CONNECTIONNOTIFIER_H_
#define NT_CONNECTIONNOTIFIER_H_
#include "ntcore_cpp.h"
#include "CallbackManager.h"
#include "Handle.h"
#include "IConnectionNotifier.h"
namespace nt {
namespace impl {
class ConnectionNotifierThread
: public CallbackThread<ConnectionNotifierThread, ConnectionNotification> {
public:
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);
unsigned int AddPolled(unsigned int poller_uid);
void NotifyConnection(bool connected, const ConnectionInfo& conn_info,
unsigned int only_listener = UINT_MAX) override;
private:
int m_inst;
};
} // namespace nt
#endif // NT_CONNECTIONNOTIFIER_H_

View File

@@ -10,28 +10,28 @@
#include <algorithm>
#include <iterator>
#include "Log.h"
#include "tcpsockets/TCPAcceptor.h"
#include "tcpsockets/TCPConnector.h"
using namespace nt;
#include "IConnectionNotifier.h"
#include "Log.h"
#include "IStorage.h"
ATOMIC_STATIC_INIT(Dispatcher)
using namespace nt;
void Dispatcher::StartServer(llvm::StringRef 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, Logger::GetInstance())));
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),
Logger::GetInstance(), 1);
static_cast<int>(port), m_logger, 1);
});
}
@@ -46,39 +46,72 @@ void Dispatcher::SetServer(
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,
Logger::GetInstance(), 1);
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),
Logger::GetInstance(), 1);
static_cast<int>(port), m_logger, 1);
});
}
void Dispatcher::ClearServerOverride() { ClearConnectorOverride(); }
Dispatcher::Dispatcher()
: Dispatcher(Storage::GetInstance(), Notifier::GetInstance()) {}
DispatcherBase::DispatcherBase(Storage& storage, Notifier& notifier)
: m_storage(storage), m_notifier(notifier) {
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() {
Logger::GetInstance().SetLogger(nullptr);
Stop();
}
DispatcherBase::~DispatcherBase() { Stop(); }
unsigned int DispatcherBase::GetNetworkMode() const {
return m_networkMode;
}
unsigned int DispatcherBase::GetNetworkMode() const { return m_networkMode; }
void DispatcherBase::StartServer(
StringRef persist_filename,
@@ -106,9 +139,7 @@ void DispatcherBase::StartServer(
});
}
using namespace std::placeholders;
m_storage.SetOutgoing(std::bind(&Dispatcher::QueueOutgoing, this, _1, _2, _3),
(m_networkMode & NT_NET_MODE_SERVER) != 0);
m_storage.SetDispatcher(this, true);
m_dispatch_thread = std::thread(&Dispatcher::DispatchThreadMain, this);
m_clientserver_thread = std::thread(&Dispatcher::ServerThreadMain, this);
@@ -121,9 +152,7 @@ void DispatcherBase::StartClient() {
m_active = true;
}
m_networkMode = NT_NET_MODE_CLIENT | NT_NET_MODE_STARTING;
using namespace std::placeholders;
m_storage.SetOutgoing(std::bind(&Dispatcher::QueueOutgoing, this, _1, _2, _3),
(m_networkMode & NT_NET_MODE_SERVER) != 0);
m_storage.SetDispatcher(this, false);
m_dispatch_thread = std::thread(&Dispatcher::DispatchThreadMain, this);
m_clientserver_thread = std::thread(&Dispatcher::ClientThreadMain, this);
@@ -198,10 +227,15 @@ std::vector<ConnectionInfo> DispatcherBase::GetConnections() const {
return conns;
}
void DispatcherBase::NotifyConnections(
ConnectionListenerCallback callback) const {
bool DispatcherBase::IsConnected() const {
if (!m_active) return false;
std::lock_guard<std::mutex> lock(m_user_mutex);
for (const auto& conn : m_connections) conn->NotifyIfActive(callback);
for (auto& conn : m_connections) {
if (conn->state() == NetworkConnection::kActive) return true;
}
return false;
}
void DispatcherBase::SetConnector(Connector connector) {
@@ -242,7 +276,8 @@ void DispatcherBase::DispatchThreadMain() {
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) {
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;
@@ -266,7 +301,8 @@ void DispatcherBase::DispatchThreadMain() {
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)
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)
@@ -316,11 +352,11 @@ void DispatcherBase::ServerThreadMain() {
// add to connections list
using namespace std::placeholders;
auto conn = std::make_shared<NetworkConnection>(
std::move(stream), m_notifier,
++m_connections_uid, std::move(stream), m_notifier, m_logger,
std::bind(&Dispatcher::ServerHandshake, this, _1, _2, _3),
std::bind(&Storage::GetEntryType, &m_storage, _1));
std::bind(&IStorage::GetMessageEntryType, &m_storage, _1));
conn->set_process_incoming(
std::bind(&Storage::ProcessIncoming, &m_storage, _1, _2,
std::bind(&IStorage::ProcessIncoming, &m_storage, _1, _2,
std::weak_ptr<NetworkConnection>(conn)));
{
std::lock_guard<std::mutex> lock(m_user_mutex);
@@ -373,11 +409,11 @@ void DispatcherBase::ClientThreadMain() {
std::unique_lock<std::mutex> lock(m_user_mutex);
using namespace std::placeholders;
auto conn = std::make_shared<NetworkConnection>(
std::move(stream), m_notifier,
++m_connections_uid, std::move(stream), m_notifier, m_logger,
std::bind(&Dispatcher::ClientHandshake, this, _1, _2, _3),
std::bind(&Storage::GetEntryType, &m_storage, _1));
std::bind(&IStorage::GetMessageEntryType, &m_storage, _1));
conn->set_process_incoming(
std::bind(&Storage::ProcessIncoming, &m_storage, _1, _2,
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);

View File

@@ -19,24 +19,28 @@
#include "llvm/StringRef.h"
#include "support/atomic_static.h"
#include "IDispatcher.h"
#include "NetworkConnection.h"
#include "Notifier.h"
#include "Storage.h"
namespace wpi {
class Logger;
class NetworkAcceptor;
class NetworkStream;
}
namespace nt {
class DispatcherBase {
class IConnectionNotifier;
class IStorage;
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;
@@ -48,7 +52,7 @@ class DispatcherBase {
void SetIdentity(llvm::StringRef name);
void Flush();
std::vector<ConnectionInfo> GetConnections() const;
void NotifyConnections(ConnectionListenerCallback callback) const;
bool IsConnected() const;
void SetConnector(Connector connector);
void SetConnectorOverride(Connector connector);
@@ -59,9 +63,6 @@ class DispatcherBase {
DispatcherBase(const DispatcherBase&) = delete;
DispatcherBase& operator=(const DispatcherBase&) = delete;
protected:
DispatcherBase(Storage& storage, Notifier& notifier);
private:
void DispatchThreadMain();
void ServerThreadMain();
@@ -79,10 +80,10 @@ class DispatcherBase {
void ClientReconnect(unsigned int proto_rev = 0x0300);
void QueueOutgoing(std::shared_ptr<Message> msg, NetworkConnection* only,
NetworkConnection* except);
NetworkConnection* except) override;
Storage& m_storage;
Notifier& m_notifier;
IStorage& m_storage;
IConnectionNotifier& m_notifier;
unsigned int m_networkMode = NT_NET_MODE_NONE;
std::string m_persist_filename;
std::thread m_dispatch_thread;
@@ -91,6 +92,7 @@ class DispatcherBase {
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 std::mutex m_user_mutex;
@@ -110,32 +112,28 @@ class DispatcherBase {
std::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:
static Dispatcher& GetInstance() {
ATOMIC_STATIC(Dispatcher, instance);
return instance;
}
Dispatcher(IStorage& storage, IConnectionNotifier& notifier,
wpi::Logger& logger)
: DispatcherBase(storage, notifier, logger) {}
void StartServer(StringRef 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();
private:
Dispatcher();
Dispatcher(Storage& storage, Notifier& notifier)
: DispatcherBase(storage, notifier) {}
ATOMIC_STATIC_DECL(Dispatcher)
};
} // namespace nt

View File

@@ -17,22 +17,26 @@
using namespace nt;
ATOMIC_STATIC_INIT(DsClient)
class DsClient::Thread : public wpi::SafeThread {
public:
Thread(unsigned int port) : m_port(port) {}
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(port));
m_owner.Start(new Thread(m_dispatcher, m_logger, port));
else
thr->m_port = port;
}
@@ -122,7 +126,7 @@ void DsClient::Thread::Main() {
// If zero, clear the server override
if (ip == 0) {
Dispatcher::GetInstance().ClearServerOverride();
m_dispatcher.ClearServerOverride();
oldip = 0;
continue;
}
@@ -137,14 +141,14 @@ void DsClient::Thread::Main() {
os << ((ip >> 24) & 0xff) << "." << ((ip >> 16) & 0xff) << "."
<< ((ip >> 8) & 0xff) << "." << (ip & 0xff);
INFO("client: DS overriding server IP to " << os.str());
Dispatcher::GetInstance().SetServerOverride(json.c_str(), port);
m_dispatcher.SetServerOverride(json.c_str(), port);
}
// We disconnected from the DS, clear the server override
Dispatcher::GetInstance().ClearServerOverride();
m_dispatcher.ClearServerOverride();
oldip = 0;
}
done:
Dispatcher::GetInstance().ClearServerOverride();
m_dispatcher.ClearServerOverride();
}

View File

@@ -8,29 +8,27 @@
#ifndef NT_DSCLIENT_H_
#define NT_DSCLIENT_H_
#include "support/atomic_static.h"
#include "support/SafeThread.h"
#include "Log.h"
namespace nt {
class Dispatcher;
class DsClient {
public:
static DsClient& GetInstance() {
ATOMIC_STATIC(DsClient, instance);
return instance;
}
DsClient(Dispatcher& dispatcher, wpi::Logger& logger);
~DsClient() = default;
void Start(unsigned int port);
void Stop();
private:
DsClient() = default;
class Thread;
wpi::SafeThreadOwner<Thread> m_owner;
ATOMIC_STATIC_DECL(DsClient)
Dispatcher& m_dispatcher;
wpi::Logger& m_logger;
};
} // namespace nt

View File

@@ -0,0 +1,89 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. 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);
}

View File

@@ -0,0 +1,108 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. 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_ENTRYNOTIFIER_H_
#define NT_ENTRYNOTIFIER_H_
#include "ntcore_cpp.h"
#include <atomic>
#include "CallbackManager.h"
#include "Handle.h"
#include "IEntryNotifier.h"
namespace wpi {
class Logger;
}
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:
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);
unsigned int Add(std::function<void(const EntryNotification& event)> callback,
unsigned int local_id, unsigned int flags);
unsigned int AddPolled(unsigned int poller_uid, llvm::StringRef prefix,
unsigned int flags);
unsigned int AddPolled(unsigned int poller_uid, unsigned int local_id,
unsigned int flags);
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 // NT_ENTRYNOTIFIER_H_

View File

@@ -0,0 +1,65 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2016-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_HANDLE_H_
#define NT_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 };
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 // NT_HANDLE_H_

View File

@@ -0,0 +1,29 @@
/*----------------------------------------------------------------------------*/
/* 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_ICONNECTIONNOTIFIER_H_
#define NT_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 void NotifyConnection(bool connected, const ConnectionInfo& conn_info,
unsigned int only_listener = UINT_MAX) = 0;
};
} // namespace nt
#endif // NT_ICONNECTIONNOTIFIER_H_

View File

@@ -0,0 +1,34 @@
/*----------------------------------------------------------------------------*/
/* 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_IDISPATCHER_H_
#define NT_IDISPATCHER_H_
#include <memory>
#include "Message.h"
namespace nt {
class NetworkConnection;
// 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,
NetworkConnection* only,
NetworkConnection* except) = 0;
};
} // namespace nt
#endif // NT_IDISPATCHER_H_

View File

@@ -0,0 +1,31 @@
/*----------------------------------------------------------------------------*/
/* 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_IENTRYNOTIFIER_H_
#define NT_IENTRYNOTIFIER_H_
#include <climits>
#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 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 // NT_IENTRYNOTIFIER_H_

View File

@@ -0,0 +1,38 @@
/*----------------------------------------------------------------------------*/
/* 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_IRPCSERVER_H_
#define NT_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 // NT_IRPCSERVER_H_

View File

@@ -0,0 +1,64 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. 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_ISTORAGE_H_
#define NT_ISTORAGE_H_
#include <functional>
#include <memory>
#include <vector>
#include "llvm/ArrayRef.h"
#include "llvm/StringRef.h"
#include "Message.h"
#include "ntcore_cpp.h"
namespace nt {
class IDispatcher;
class NetworkConnection;
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,
NetworkConnection* conn,
std::weak_ptr<NetworkConnection> conn_weak) = 0;
virtual void GetInitialAssignments(
NetworkConnection& conn, std::vector<std::shared_ptr<Message>>* msgs) = 0;
virtual void ApplyInitialAssignments(
NetworkConnection& 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(StringRef filename,
bool periodic) const = 0;
virtual const char* LoadPersistent(
StringRef filename,
std::function<void(std::size_t line, const char* msg)> warn) = 0;
};
} // namespace nt
#endif // NT_ISTORAGE_H_

View File

@@ -0,0 +1,106 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015-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. */
/*----------------------------------------------------------------------------*/
#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;
std::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) {}
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<std::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<std::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<std::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<std::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);
}

View File

@@ -0,0 +1,60 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2016-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_INSTANCEIMPL_H_
#define NT_INSTANCEIMPL_H_
#include <atomic>
#include <memory>
#include <mutex>
#include "support/UidVector.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 std::mutex s_mutex;
};
} // namespace nt
#endif // NT_INSTANCEIMPL_H_

View File

@@ -1,40 +0,0 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. 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 "Log.h"
#include "llvm/Path.h"
#include "llvm/StringRef.h"
#include "llvm/raw_ostream.h"
using namespace nt;
ATOMIC_STATIC_INIT(Logger)
static void def_log_func(unsigned int level, const char* file,
unsigned int line, const char* msg) {
if (level == 20) {
llvm::errs() << "NT: " << msg << '\n';
return;
}
llvm::StringRef levelmsg;
if (level >= 50)
levelmsg = "CRITICAL: ";
else if (level >= 40)
levelmsg = "ERROR: ";
else if (level >= 30)
levelmsg = "WARNING: ";
else
return;
llvm::errs() << "NT: " << levelmsg << msg << " ("
<< llvm::sys::path::filename(file) << ':' << line << ")\n";
}
Logger::Logger() { SetLogger(def_log_func); }
Logger::~Logger() {}

View File

@@ -8,38 +8,19 @@
#ifndef NT_LOG_H_
#define NT_LOG_H_
#include "support/atomic_static.h"
#include "support/Logger.h"
namespace nt {
class Logger : public wpi::Logger {
public:
static Logger& GetInstance() {
ATOMIC_STATIC(Logger, instance);
return instance;
}
~Logger();
private:
Logger();
ATOMIC_STATIC_DECL(Logger)
};
#define LOG(level, x) WPI_LOG(nt::Logger::GetInstance(), level, x)
#define LOG(level, x) WPI_LOG(m_logger, level, x)
#undef ERROR
#define ERROR(x) WPI_ERROR(nt::Logger::GetInstance(), x)
#define WARNING(x) WPI_WARNING(nt::Logger::GetInstance(), x)
#define INFO(x) WPI_INFO(nt::Logger::GetInstance(), x)
#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(nt::Logger::GetInstance(), x)
#define DEBUG1(x) WPI_DEBUG1(nt::Logger::GetInstance(), x)
#define DEBUG2(x) WPI_DEBUG2(nt::Logger::GetInstance(), x)
#define DEBUG3(x) WPI_DEBUG3(nt::Logger::GetInstance(), x)
#define DEBUG4(x) WPI_DEBUG4(nt::Logger::GetInstance(), x)
} // namespace nt
#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 // NT_LOG_H_

View File

@@ -0,0 +1,77 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. 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);
}

View File

@@ -0,0 +1,84 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. 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_LOGGERIMPL_H_
#define NT_LOGGERIMPL_H_
#include "ntcore_cpp.h"
#include "CallbackManager.h"
#include "Handle.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:
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 // NT_CONNECTIONNOTIFIER_H_

View File

@@ -77,7 +77,7 @@ std::shared_ptr<Message> Message::Read(WireDecoder& decoder,
} else {
type = get_entry_type(msg->m_id);
}
DEBUG4("update message data type: " << type);
WPI_DEBUG4(decoder.logger(), "update message data type: " << type);
msg->m_value = decoder.ReadValue(type);
if (!msg->m_value) return nullptr;
break;
@@ -143,7 +143,7 @@ std::shared_ptr<Message> Message::Read(WireDecoder& decoder,
}
default:
decoder.set_error("unrecognized message type");
INFO("unrecognized message type: " << msg_type);
WPI_INFO(decoder.logger(), "unrecognized message type: " << msg_type);
return nullptr;
}
return msg;
@@ -211,9 +211,9 @@ std::shared_ptr<Message> Message::ExecuteRpc(unsigned int id, unsigned int uid,
}
std::shared_ptr<Message> Message::RpcResponse(unsigned int id, unsigned int uid,
llvm::StringRef results) {
llvm::StringRef result) {
auto msg = std::make_shared<Message>(kRpcResponse, private_init());
msg->m_str = results;
msg->m_str = result;
msg->m_id = id;
msg->m_seq_num_uid = uid;
return msg;

View File

@@ -12,7 +12,7 @@
#include <memory>
#include <string>
#include "nt_Value.h"
#include "networktables/NetworkTableValue.h"
namespace nt {
@@ -96,7 +96,7 @@ class Message {
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 results);
llvm::StringRef result);
Message(const Message&) = delete;
Message& operator=(const Message&) = delete;

View File

@@ -10,22 +10,24 @@
#include "support/raw_socket_istream.h"
#include "support/timestamp.h"
#include "tcpsockets/NetworkStream.h"
#include "IConnectionNotifier.h"
#include "Log.h"
#include "Notifier.h"
#include "WireDecoder.h"
#include "WireEncoder.h"
using namespace nt;
std::atomic_uint NetworkConnection::s_uid;
NetworkConnection::NetworkConnection(std::unique_ptr<wpi::NetworkStream> stream,
Notifier& notifier,
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(s_uid.fetch_add(1)),
: 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) {
@@ -112,12 +114,6 @@ void NetworkConnection::set_state(State state) {
m_state = state;
}
void NetworkConnection::NotifyIfActive(
ConnectionListenerCallback callback) const {
std::lock_guard<std::mutex> lock(m_state_mutex);
if (m_state == kActive) m_notifier.NotifyConnection(true, info(), callback);
}
std::string NetworkConnection::remote_id() const {
std::lock_guard<std::mutex> lock(m_remote_id_mutex);
return m_remote_id;
@@ -130,7 +126,7 @@ void NetworkConnection::set_remote_id(StringRef remote_id) {
void NetworkConnection::ReadThreadMain() {
wpi::raw_socket_istream is(*m_stream);
WireDecoder decoder(is, m_proto_rev);
WireDecoder decoder(is, m_proto_rev, m_logger);
set_state(kHandshake);
if (!m_handshake(*this,

View File

@@ -18,12 +18,13 @@
#include "ntcore_cpp.h"
namespace wpi {
class Logger;
class NetworkStream;
}
namespace nt {
class Notifier;
class IConnectionNotifier;
class NetworkConnection {
public:
@@ -40,8 +41,10 @@ class NetworkConnection {
typedef std::vector<std::shared_ptr<Message>> Outgoing;
typedef wpi::ConcurrentQueue<Outgoing> OutgoingQueue;
NetworkConnection(std::unique_ptr<wpi::NetworkStream> stream,
Notifier& notifier, HandshakeFunc handshake,
NetworkConnection(unsigned int uid,
std::unique_ptr<wpi::NetworkStream> stream,
IConnectionNotifier& notifier, wpi::Logger& logger,
HandshakeFunc handshake,
Message::GetEntryTypeFunc get_entry_type);
~NetworkConnection();
@@ -60,7 +63,6 @@ class NetworkConnection {
void QueueOutgoing(std::shared_ptr<Message> msg);
void PostOutgoing(bool keep_alive);
void NotifyIfActive(ConnectionListenerCallback callback) const;
unsigned int uid() const { return m_uid; }
@@ -82,11 +84,10 @@ class NetworkConnection {
void ReadThreadMain();
void WriteThreadMain();
static std::atomic_uint s_uid;
unsigned int m_uid;
std::unique_ptr<wpi::NetworkStream> m_stream;
Notifier& m_notifier;
IConnectionNotifier& m_notifier;
wpi::Logger& m_logger;
OutgoingQueue m_outgoing;
HandshakeFunc m_handshake;
Message::GetEntryTypeFunc m_get_entry_type;

View File

@@ -1,258 +0,0 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. 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 "Notifier.h"
#include <queue>
#include <vector>
using namespace nt;
ATOMIC_STATIC_INIT(Notifier)
bool Notifier::s_destroyed = false;
namespace {
// Vector which provides an integrated freelist for removal and reuse of
// individual elements.
template <typename T>
class UidVector {
public:
typedef typename std::vector<T>::size_type size_type;
size_type size() const { return m_vector.size(); }
T& operator[](size_type i) { return m_vector[i]; }
const T& operator[](size_type i) const { return m_vector[i]; }
// Add a new T to the vector. If there are elements on the freelist,
// reuses the last one; otherwise adds to the end of the vector.
// Returns the resulting element index (+1).
template <class... Args>
unsigned int emplace_back(Args&&... args) {
unsigned int uid;
if (m_free.empty()) {
uid = m_vector.size();
m_vector.emplace_back(std::forward<Args>(args)...);
} else {
uid = m_free.back();
m_free.pop_back();
m_vector[uid] = T(std::forward<Args>(args)...);
}
return uid + 1;
}
// Removes the identified element by replacing it with a default-constructed
// one. The element is added to the freelist for later reuse.
void erase(unsigned int uid) {
--uid;
if (uid >= m_vector.size() || !m_vector[uid]) return;
m_free.push_back(uid);
m_vector[uid] = T();
}
private:
std::vector<T> m_vector;
std::vector<unsigned int> m_free;
};
} // anonymous namespace
class Notifier::Thread : public wpi::SafeThread {
public:
Thread(std::function<void()> on_start, std::function<void()> on_exit)
: m_on_start(on_start), m_on_exit(on_exit) {}
void Main();
struct EntryListener {
EntryListener() = default;
EntryListener(StringRef prefix_, EntryListenerCallback callback_,
unsigned int flags_)
: prefix(prefix_), callback(callback_), flags(flags_) {}
explicit operator bool() const { return bool(callback); }
std::string prefix;
EntryListenerCallback callback;
unsigned int flags;
};
UidVector<EntryListener> m_entry_listeners;
UidVector<ConnectionListenerCallback> m_conn_listeners;
struct EntryNotification {
EntryNotification(llvm::StringRef name_, std::shared_ptr<Value> value_,
unsigned int flags_, EntryListenerCallback only_)
: name(name_), value(value_), flags(flags_), only(only_) {}
std::string name;
std::shared_ptr<Value> value;
unsigned int flags;
EntryListenerCallback only;
};
std::queue<EntryNotification> m_entry_notifications;
struct ConnectionNotification {
ConnectionNotification(bool connected_, const ConnectionInfo& conn_info_,
ConnectionListenerCallback only_)
: connected(connected_), conn_info(conn_info_), only(only_) {}
bool connected;
ConnectionInfo conn_info;
ConnectionListenerCallback only;
};
std::queue<ConnectionNotification> m_conn_notifications;
std::function<void()> m_on_start;
std::function<void()> m_on_exit;
};
Notifier::Notifier() {
m_local_notifiers = false;
s_destroyed = false;
}
Notifier::~Notifier() { s_destroyed = true; }
void Notifier::Start() {
auto thr = m_owner.GetThread();
if (!thr) m_owner.Start(new Thread(m_on_start, m_on_exit));
}
void Notifier::Stop() { m_owner.Stop(); }
void Notifier::Thread::Main() {
if (m_on_start) m_on_start();
std::unique_lock<std::mutex> lock(m_mutex);
while (m_active) {
while (m_entry_notifications.empty() && m_conn_notifications.empty()) {
m_cond.wait(lock);
if (!m_active) goto done;
}
// Entry notifications
while (!m_entry_notifications.empty()) {
if (!m_active) goto done;
auto item = std::move(m_entry_notifications.front());
m_entry_notifications.pop();
if (!item.value) continue;
StringRef name(item.name);
if (item.only) {
// Don't hold mutex during callback execution!
lock.unlock();
item.only(0, name, item.value, item.flags);
lock.lock();
continue;
}
// Use index because iterator might get invalidated.
for (std::size_t i = 0; i < m_entry_listeners.size(); ++i) {
if (!m_entry_listeners[i]) continue; // removed
// 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 = m_entry_listeners[i].flags;
unsigned int flags = item.flags;
unsigned int assign_both = NT_NOTIFY_UPDATE | NT_NOTIFY_FLAGS;
if ((flags & assign_both) == assign_both) {
if ((listen_flags & assign_both) == 0) continue;
listen_flags &= ~assign_both;
flags &= ~assign_both;
}
if ((flags & ~listen_flags) != 0) continue;
// must match prefix
if (!name.startswith(m_entry_listeners[i].prefix)) continue;
// make a copy of the callback so we can safely release the mutex
auto callback = m_entry_listeners[i].callback;
// Don't hold mutex during callback execution!
lock.unlock();
callback(i + 1, name, item.value, item.flags);
lock.lock();
}
}
// Connection notifications
while (!m_conn_notifications.empty()) {
if (!m_active) goto done;
auto item = std::move(m_conn_notifications.front());
m_conn_notifications.pop();
if (item.only) {
// Don't hold mutex during callback execution!
lock.unlock();
item.only(0, item.connected, item.conn_info);
lock.lock();
continue;
}
// Use index because iterator might get invalidated.
for (std::size_t i = 0; i < m_conn_listeners.size(); ++i) {
if (!m_conn_listeners[i]) continue; // removed
auto callback = m_conn_listeners[i];
// Don't hold mutex during callback execution!
lock.unlock();
callback(i + 1, item.connected, item.conn_info);
lock.lock();
}
}
}
done:
if (m_on_exit) m_on_exit();
}
unsigned int Notifier::AddEntryListener(StringRef prefix,
EntryListenerCallback callback,
unsigned int flags) {
Start();
auto thr = m_owner.GetThread();
if ((flags & NT_NOTIFY_LOCAL) != 0) m_local_notifiers = true;
return thr->m_entry_listeners.emplace_back(prefix, callback, flags);
}
void Notifier::RemoveEntryListener(unsigned int entry_listener_uid) {
auto thr = m_owner.GetThread();
if (!thr) return;
thr->m_entry_listeners.erase(entry_listener_uid);
}
void Notifier::NotifyEntry(StringRef name, std::shared_ptr<Value> value,
unsigned int flags, EntryListenerCallback only) {
// 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;
auto thr = m_owner.GetThread();
if (!thr) return;
thr->m_entry_notifications.emplace(name, value, flags, only);
thr->m_cond.notify_one();
}
unsigned int Notifier::AddConnectionListener(
ConnectionListenerCallback callback) {
Start();
auto thr = m_owner.GetThread();
return thr->m_conn_listeners.emplace_back(callback);
}
void Notifier::RemoveConnectionListener(unsigned int conn_listener_uid) {
auto thr = m_owner.GetThread();
if (!thr) return;
thr->m_conn_listeners.erase(conn_listener_uid);
}
void Notifier::NotifyConnection(bool connected, const ConnectionInfo& conn_info,
ConnectionListenerCallback only) {
auto thr = m_owner.GetThread();
if (!thr) return;
thr->m_conn_notifications.emplace(connected, conn_info, only);
thr->m_cond.notify_one();
}

View File

@@ -1,69 +0,0 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. 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_NOTIFIER_H_
#define NT_NOTIFIER_H_
#include <functional>
#include "support/atomic_static.h"
#include "support/SafeThread.h"
#include "ntcore_cpp.h"
namespace nt {
class Notifier {
friend class NotifierTest;
public:
static Notifier& GetInstance() {
ATOMIC_STATIC(Notifier, instance);
return instance;
}
~Notifier();
void Start();
void Stop();
bool local_notifiers() const { return m_local_notifiers; }
static bool destroyed() { return s_destroyed; }
void SetOnStart(std::function<void()> on_start) { m_on_start = on_start; }
void SetOnExit(std::function<void()> on_exit) { m_on_exit = on_exit; }
unsigned int AddEntryListener(llvm::StringRef prefix,
EntryListenerCallback callback,
unsigned int flags);
void RemoveEntryListener(unsigned int entry_listener_uid);
void NotifyEntry(StringRef name, std::shared_ptr<Value> value,
unsigned int flags, EntryListenerCallback only = nullptr);
unsigned int AddConnectionListener(ConnectionListenerCallback callback);
void RemoveConnectionListener(unsigned int conn_listener_uid);
void NotifyConnection(bool connected, const ConnectionInfo& conn_info,
ConnectionListenerCallback only = nullptr);
private:
Notifier();
class Thread;
wpi::SafeThreadOwner<Thread> m_owner;
std::atomic_bool m_local_notifiers;
std::function<void()> m_on_start;
std::function<void()> m_on_exit;
ATOMIC_STATIC_DECL(Notifier)
static bool s_destroyed;
};
} // namespace nt
#endif // NT_NOTIFIER_H_

View File

@@ -7,145 +7,42 @@
#include "RpcServer.h"
#include <queue>
#include "Log.h"
using namespace nt;
ATOMIC_STATIC_INIT(RpcServer)
RpcServer::RpcServer(int inst, wpi::Logger& logger)
: m_inst(inst), m_logger(logger) {}
class RpcServer::Thread : public wpi::SafeThread {
public:
Thread(std::function<void()> on_start, std::function<void()> on_exit)
: m_on_start(on_start), m_on_exit(on_exit) {}
void RpcServer::Start() { DoStart(m_inst, m_logger); }
void Main();
std::queue<RpcCall> m_call_queue;
std::function<void()> m_on_start;
std::function<void()> m_on_exit;
};
RpcServer::RpcServer() { m_terminating = false; }
RpcServer::~RpcServer() {
Logger::GetInstance().SetLogger(nullptr);
m_terminating = true;
m_poll_cond.notify_all();
unsigned int RpcServer::Add(
std::function<void(const RpcAnswer& answer)> callback) {
return DoAdd(callback);
}
void RpcServer::Start() {
auto thr = m_owner.GetThread();
if (!thr) m_owner.Start(new Thread(m_on_start, m_on_exit));
unsigned int RpcServer::AddPolled(unsigned int poller_uid) {
return DoAdd(poller_uid);
}
void RpcServer::Stop() { m_owner.Stop(); }
void RpcServer::RemoveRpc(unsigned int rpc_uid) { Remove(rpc_uid); }
void RpcServer::ProcessRpc(StringRef name, std::shared_ptr<Message> msg,
RpcCallback func, unsigned int conn_id,
SendMsgFunc send_response,
const ConnectionInfo& conn_info) {
if (func) {
auto thr = m_owner.GetThread();
if (!thr) return;
thr->m_call_queue.emplace(name, msg, func, conn_id, send_response,
conn_info);
thr->m_cond.notify_one();
} else {
std::lock_guard<std::mutex> lock(m_mutex);
m_poll_queue.emplace(name, msg, func, conn_id, send_response, conn_info);
m_poll_cond.notify_one();
}
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);
}
bool RpcServer::PollRpc(bool blocking, RpcCallInfo* call_info) {
return PollRpc(blocking, kTimeout_Indefinite, call_info);
}
bool RpcServer::PollRpc(bool blocking, double time_out,
RpcCallInfo* call_info) {
std::unique_lock<std::mutex> lock(m_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>(time_out * 1e9));
#else
auto timeout_time = std::chrono::steady_clock::now() +
std::chrono::duration<double>(time_out);
#endif
while (m_poll_queue.empty()) {
if (!blocking || m_terminating) return false;
if (time_out < 0) {
m_poll_cond.wait(lock);
} else {
auto timed_out = m_poll_cond.wait_until(lock, timeout_time);
if (timed_out == std::cv_status::timeout) {
return false;
}
}
if (m_terminating) return false;
}
auto& item = m_poll_queue.front();
unsigned int call_uid;
// do not include conn id if the result came from the server
if (item.conn_id != 0xffff)
call_uid = (item.conn_id << 16) | item.msg->seq_num_uid();
else
call_uid = item.msg->seq_num_uid();
call_info->rpc_id = item.msg->id();
call_info->call_uid = call_uid;
call_info->name = std::move(item.name);
call_info->params = item.msg->str();
m_response_map.insert(std::make_pair(std::make_pair(item.msg->id(), call_uid),
item.send_response));
m_poll_queue.pop();
return true;
}
void RpcServer::PostRpcResponse(unsigned int rpc_id, unsigned int call_uid,
void RpcServer::PostRpcResponse(unsigned int local_id, unsigned int call_uid,
llvm::StringRef result) {
auto i = m_response_map.find(std::make_pair(rpc_id, call_uid));
if (i == m_response_map.end()) {
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())(Message::RpcResponse(rpc_id, call_uid, result));
m_response_map.erase(i);
}
void RpcServer::Thread::Main() {
if (m_on_start) m_on_start();
std::unique_lock<std::mutex> lock(m_mutex);
std::string tmp;
while (m_active) {
while (m_call_queue.empty()) {
m_cond.wait(lock);
if (!m_active) goto done;
}
while (!m_call_queue.empty()) {
if (!m_active) goto done;
auto item = std::move(m_call_queue.front());
m_call_queue.pop();
DEBUG4("rpc calling " << item.name);
if (item.name.empty() || !item.msg || !item.func || !item.send_response)
continue;
// Don't hold mutex during callback execution!
lock.unlock();
auto result = item.func(item.name, item.msg->str(), item.conn_info);
item.send_response(Message::RpcResponse(item.msg->id(),
item.msg->seq_num_uid(), result));
lock.lock();
}
}
done:
if (m_on_exit) m_on_exit();
(i->getSecond())(result);
thr->m_response_map.erase(i);
}

View File

@@ -8,86 +8,100 @@
#ifndef NT_RPCSERVER_H_
#define NT_RPCSERVER_H_
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <utility>
#include "llvm/DenseMap.h"
#include "support/atomic_static.h"
#include "support/SafeThread.h"
#include "Message.h"
#include "ntcore_cpp.h"
#include "CallbackManager.h"
#include "Handle.h"
#include "IRpcServer.h"
#include "Log.h"
namespace nt {
class RpcServer {
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<std::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:
static RpcServer& GetInstance() {
ATOMIC_STATIC(RpcServer, instance);
return instance;
}
~RpcServer();
typedef std::function<void(std::shared_ptr<Message>)> SendMsgFunc;
RpcServer(int inst, wpi::Logger& logger);
void Start();
void Stop();
void SetOnStart(std::function<void()> on_start) { m_on_start = on_start; }
void SetOnExit(std::function<void()> on_exit) { m_on_exit = on_exit; }
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(StringRef name, std::shared_ptr<Message> msg,
RpcCallback func, unsigned int conn_id,
SendMsgFunc send_response, const ConnectionInfo& conn_info);
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;
bool PollRpc(bool blocking, RpcCallInfo* call_info);
bool PollRpc(bool blocking, double time_out, RpcCallInfo* call_info);
void PostRpcResponse(unsigned int rpc_id, unsigned int call_uid,
void PostRpcResponse(unsigned int local_id, unsigned int call_uid,
llvm::StringRef result);
private:
RpcServer();
class Thread;
wpi::SafeThreadOwner<Thread> m_owner;
struct RpcCall {
RpcCall(StringRef name_, std::shared_ptr<Message> msg_, RpcCallback func_,
unsigned int conn_id_, SendMsgFunc send_response_,
const ConnectionInfo conn_info_)
: name(name_),
msg(msg_),
func(func_),
conn_id(conn_id_),
send_response(send_response_),
conn_info(conn_info_) {}
std::string name;
std::shared_ptr<Message> msg;
RpcCallback func;
unsigned int conn_id;
SendMsgFunc send_response;
ConnectionInfo conn_info;
};
std::mutex m_mutex;
std::queue<RpcCall> m_poll_queue;
llvm::DenseMap<std::pair<unsigned int, unsigned int>, SendMsgFunc>
m_response_map;
std::condition_variable m_poll_cond;
std::atomic_bool m_terminating;
std::function<void()> m_on_start;
std::function<void()> m_on_exit;
ATOMIC_STATIC_DECL(RpcServer)
int m_inst;
wpi::Logger& m_logger;
};
} // namespace nt

File diff suppressed because it is too large Load Diff

View File

@@ -9,84 +9,107 @@
#define NT_STORAGE_H_
#include <atomic>
#include <condition_variable>
#include <cstddef>
#include <fstream>
#include <functional>
#include <iosfwd>
#include <memory>
#include <mutex>
#include <vector>
#include "llvm/DenseMap.h"
#include "llvm/SmallSet.h"
#include "llvm/StringMap.h"
#include "support/atomic_static.h"
#include "Message.h"
#include "Notifier.h"
#include "ntcore_cpp.h"
#include "RpcServer.h"
#include "SequenceNumber.h"
#include "IStorage.h"
namespace wpi {
class Logger;
}
namespace nt {
class NetworkConnection;
class StorageTest;
class IEntryNotifier;
class IRpcServer;
class IStorageTest;
class Storage {
class Storage : public IStorage {
friend class StorageTest;
public:
static Storage& GetInstance() {
ATOMIC_STATIC(Storage, instance);
return instance;
}
Storage(IEntryNotifier& notifier, IRpcServer& rpcserver, wpi::Logger& logger);
Storage(const Storage&) = delete;
Storage& operator=(const Storage&) = delete;
~Storage();
// Accessors required by Dispatcher. A function pointer is used for
// Accessors required by Dispatcher. An interface is used for
// generation of outgoing messages to break a dependency loop between
// Storage and Dispatcher; in operation this is always set to
// Dispatcher::QueueOutgoing.
typedef std::function<void(std::shared_ptr<Message> msg,
NetworkConnection* only,
NetworkConnection* except)>
QueueOutgoingFunc;
void SetOutgoing(QueueOutgoingFunc queue_outgoing, bool server);
void ClearOutgoing();
// 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 GetEntryType(unsigned int id) const;
NT_Type GetMessageEntryType(unsigned int id) const override;
void ProcessIncoming(std::shared_ptr<Message> msg, NetworkConnection* conn,
std::weak_ptr<NetworkConnection> conn_weak);
void GetInitialAssignments(NetworkConnection& conn,
std::vector<std::shared_ptr<Message>>* msgs);
void ApplyInitialAssignments(NetworkConnection& conn,
llvm::ArrayRef<std::shared_ptr<Message>> msgs,
bool new_server,
std::vector<std::shared_ptr<Message>>* out_msgs);
std::weak_ptr<NetworkConnection> conn_weak) override;
void GetInitialAssignments(
NetworkConnection& conn,
std::vector<std::shared_ptr<Message>>* msgs) override;
void ApplyInitialAssignments(
NetworkConnection& 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(StringRef prefix, unsigned int types);
void NotifyEntries(StringRef prefix,
EntryListenerCallback only = nullptr) const;
std::vector<EntryInfo> GetEntryInfo(int inst, StringRef prefix,
unsigned int types);
// Index-only
unsigned int GetEntry(StringRef name);
std::vector<unsigned int> GetEntries(StringRef 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;
unsigned long long 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(StringRef filename, bool periodic) const;
const char* SavePersistent(StringRef filename, bool periodic) const override;
const char* LoadPersistent(
StringRef filename,
std::function<void(std::size_t line, const char* msg)> warn);
std::function<void(std::size_t line, const char* msg)> warn) override;
// Stream-based save/load functions (exposed for testing purposes). These
// implement the guts of the filename-based functions.
@@ -97,25 +120,18 @@ class Storage {
// RPC configuration needs to come through here as RPC definitions are
// actually special Storage value types.
void CreateRpc(StringRef name, StringRef def, RpcCallback callback);
void CreatePolledRpc(StringRef name, StringRef def);
unsigned int CallRpc(StringRef name, StringRef params);
bool GetRpcResult(bool blocking, unsigned int call_uid, std::string* result);
bool GetRpcResult(bool blocking, unsigned int call_uid, double time_out,
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);
void CancelBlockingRpcResult(unsigned int call_uid);
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:
Storage();
Storage(Notifier& notifier, RpcServer& rpcserver);
Storage(const Storage&) = delete;
Storage& operator=(const Storage&) = delete;
// Data for each table entry.
struct Entry {
Entry(llvm::StringRef name_)
: name(name_), flags(0), id(0xffff), rpc_call_uid(0) {}
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
@@ -124,34 +140,38 @@ class Storage {
// The current value and flags.
std::shared_ptr<Value> value;
unsigned int flags;
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;
unsigned int id{0xffff};
// Local ID.
unsigned int local_id{UINT_MAX};
// Sequence number for update resolution.
SequenceNumber seq_num;
// RPC callback function. Null if either not an RPC or if the RPC is
// polled.
RpcCallback rpc_callback;
// 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;
unsigned int rpc_call_uid{0};
};
typedef llvm::StringMap<std::unique_ptr<Entry>> EntriesMap;
typedef llvm::StringMap<Entry*> EntriesMap;
typedef std::vector<Entry*> IdMap;
typedef llvm::DenseMap<std::pair<unsigned int, unsigned int>, std::string>
RpcResultMap;
typedef llvm::SmallSet<unsigned int, 12> RpcBlockingCallSet;
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 std::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
@@ -162,20 +182,27 @@ class Storage {
std::condition_variable m_rpc_results_cond;
// configured by dispatcher at startup
QueueOutgoingFunc m_queue_outgoing;
IDispatcher* m_dispatcher = nullptr;
bool m_server = true;
// references to singletons (we don't grab them directly for testing purposes)
Notifier& m_notifier;
RpcServer& m_rpc_server;
IEntryNotifier& m_notifier;
IRpcServer& m_rpc_server;
wpi::Logger& m_logger;
bool GetPersistentEntries(
bool periodic,
std::vector<std::pair<std::string, std::shared_ptr<Value>>>* entries)
const;
void DeleteAllEntriesImpl();
void SetEntryValueImpl(Entry* entry, std::shared_ptr<Value> value,
std::unique_lock<std::mutex>& lock, bool local);
void SetEntryFlagsImpl(Entry* entry, unsigned int flags,
std::unique_lock<std::mutex>& lock, bool local);
void DeleteEntryImpl(Entry* entry, EntriesMap::iterator it,
std::unique_lock<std::mutex>& lock, bool local);
ATOMIC_STATIC_DECL(Storage)
// Must be called with m_mutex held
void DeleteAllEntriesImpl(bool local);
Entry* GetOrNew(StringRef name, bool* is_new = nullptr);
};
} // namespace nt

View File

@@ -5,7 +5,7 @@
/* the project. */
/*----------------------------------------------------------------------------*/
#include "nt_Value.h"
#include "networktables/NetworkTableValue.h"
#include "Value_internal.h"
#include "support/timestamp.h"
@@ -16,9 +16,12 @@ Value::Value() {
m_val.last_change = wpi::Now();
}
Value::Value(NT_Type type, const private_init&) {
Value::Value(NT_Type type, unsigned long long time, const private_init&) {
m_val.type = type;
m_val.last_change = wpi::Now();
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)
@@ -36,16 +39,18 @@ Value::~Value() {
delete[] m_val.data.arr_string.arr;
}
std::shared_ptr<Value> Value::MakeBooleanArray(llvm::ArrayRef<int> value) {
auto val = std::make_shared<Value>(NT_BOOLEAN_ARRAY, private_init());
std::shared_ptr<Value> Value::MakeBooleanArray(llvm::ArrayRef<int> value,
unsigned long long 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) {
auto val = std::make_shared<Value>(NT_DOUBLE_ARRAY, private_init());
std::shared_ptr<Value> Value::MakeDoubleArray(llvm::ArrayRef<double> value,
unsigned long long 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);
@@ -53,8 +58,8 @@ std::shared_ptr<Value> Value::MakeDoubleArray(llvm::ArrayRef<double> value) {
}
std::shared_ptr<Value> Value::MakeStringArray(
llvm::ArrayRef<std::string> value) {
auto val = std::make_shared<Value>(NT_STRING_ARRAY, private_init());
llvm::ArrayRef<std::string> value, unsigned long long 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()];
@@ -67,8 +72,8 @@ std::shared_ptr<Value> Value::MakeStringArray(
}
std::shared_ptr<Value> Value::MakeStringArray(
std::vector<std::string>&& value) {
auto val = std::make_shared<Value>(NT_STRING_ARRAY, private_init());
std::vector<std::string>&& value, unsigned long long 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.

View File

@@ -45,8 +45,9 @@ static double ReadDouble(const char*& buf) {
return llvm::BitsToDouble(val);
}
WireDecoder::WireDecoder(wpi::raw_istream& is, unsigned int proto_rev)
: m_is(is) {
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;

View File

@@ -10,10 +10,10 @@
#include <cstddef>
#include "nt_Value.h"
#include "networktables/NetworkTableValue.h"
#include "support/leb128.h"
#include "support/raw_istream.h"
//#include "Log.h"
#include "Log.h"
namespace nt {
@@ -26,7 +26,8 @@ namespace nt {
*/
class WireDecoder {
public:
explicit WireDecoder(wpi::raw_istream& is, unsigned int proto_rev);
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; }
@@ -34,6 +35,9 @@ class WireDecoder {
/* 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; }
@@ -54,8 +58,7 @@ class WireDecoder {
*buf = m_buf;
m_is.read(m_buf, len);
#if 0
nt::Logger& logger = nt::Logger::GetInstance();
if (logger.min_level() <= NT_LOG_DEBUG4 && logger.HasLogger()) {
if (m_logger.min_level() <= NT_LOG_DEBUG4 && m_logger.HasLogger()) {
std::ostringstream oss;
oss << "read " << len << " bytes:" << std::hex;
if (!rv)
@@ -64,7 +67,7 @@ class WireDecoder {
for (std::size_t i=0; i < len; ++i)
oss << ' ' << (unsigned int)((*buf)[i]);
}
logger.Log(NT_LOG_DEBUG4, __FILE__, __LINE__, oss.str().c_str());
m_logger.Log(NT_LOG_DEBUG4, __FILE__, __LINE__, oss.str().c_str());
}
#endif
return !m_is.has_error();
@@ -135,6 +138,9 @@ class WireDecoder {
/* input stream */
wpi::raw_istream& m_is;
/* logger */
wpi::Logger& m_logger;
/* temporary buffer */
char* m_buf;

View File

@@ -13,7 +13,7 @@
#include "llvm/SmallVector.h"
#include "llvm/StringRef.h"
#include "nt_Value.h"
#include "networktables/NetworkTableValue.h"
namespace nt {

File diff suppressed because it is too large Load Diff

View File

@@ -2,14 +2,18 @@
#include <algorithm>
#include "llvm/raw_ostream.h"
#include "llvm/SmallString.h"
#include "llvm/StringMap.h"
#include "tables/ITableListener.h"
#include "llvm/raw_ostream.h"
#include "networktables/NetworkTableInstance.h"
#include "ntcore.h"
#include "tables/ITableListener.h"
using llvm::StringRef;
using nt::NetworkTable;
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";
@@ -20,21 +24,23 @@ unsigned int NetworkTable::s_port = NT_DEFAULT_PORT;
void NetworkTable::Initialize() {
if (s_running) Shutdown();
auto inst = NetworkTableInstance::GetDefault();
if (s_client) {
nt::StartClient();
if (s_enable_ds) nt::StartDSClient(s_port);
inst.StartClient();
if (s_enable_ds) inst.StartDSClient(s_port);
} else
nt::StartServer(s_persistent_filename, "", s_port);
inst.StartServer(s_persistent_filename, "", s_port);
s_running = true;
}
void NetworkTable::Shutdown() {
if (!s_running) return;
auto inst = NetworkTableInstance::GetDefault();
if (s_client) {
nt::StopDSClient();
nt::StopClient();
inst.StopDSClient();
inst.StopClient();
} else
nt::StopServer();
inst.StopServer();
s_running = false;
}
@@ -43,80 +49,46 @@ void NetworkTable::SetClientMode() { s_client = true; }
void NetworkTable::SetServerMode() { s_client = false; }
void NetworkTable::SetTeam(int team) {
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(), s_port);
}
// 172.22.11.2
servers[1] = std::make_pair("172.22.11.2", s_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(), s_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(), s_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(), s_port);
}
nt::SetServer(servers);
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};
nt::SetServer(addr_copy.c_str(), s_port);
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")
nt::StopDSClient();
inst.StopDSClient();
else if (s_enable_ds)
nt::StartDSClient(s_port);
inst.StartDSClient(s_port);
}
void NetworkTable::SetIPAddress(llvm::ArrayRef<std::string> addresses) {
llvm::SmallVector<std::pair<StringRef, unsigned int>, 8> servers;
for (const auto& ip_address : addresses)
servers.emplace_back(std::make_pair(ip_address, s_port));
nt::SetServer(servers);
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"))
nt::StopDSClient();
inst.StopDSClient();
else if (s_enable_ds)
nt::StartDSClient(s_port);
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)
nt::StartDSClient(s_port);
inst.StartDSClient(s_port);
else
nt::StopDSClient();
inst.StopDSClient();
}
void NetworkTable::SetPersistentFilename(StringRef filename) {
@@ -124,44 +96,89 @@ void NetworkTable::SetPersistentFilename(StringRef filename) {
}
void NetworkTable::SetNetworkIdentity(StringRef name) {
nt::SetNetworkIdentity(name);
NetworkTableInstance::GetDefault().SetNetworkIdentity(name);
}
void NetworkTable::GlobalDeleteAll() { nt::DeleteAllEntries(); }
void NetworkTable::GlobalDeleteAll() {
NetworkTableInstance::GetDefault().DeleteAllEntries();
}
void NetworkTable::Flush() { nt::Flush(); }
void NetworkTable::Flush() { NetworkTableInstance::GetDefault().Flush(); }
void NetworkTable::SetUpdateRate(double interval) {
nt::SetUpdateRate(interval);
NetworkTableInstance::GetDefault().SetUpdateRate(interval);
}
const char* NetworkTable::SavePersistent(llvm::StringRef filename) {
return nt::SavePersistent(filename);
return NetworkTableInstance::GetDefault().SavePersistent(filename);
}
const char* NetworkTable::LoadPersistent(
llvm::StringRef filename,
std::function<void(size_t line, const char* msg)> warn) {
return nt::LoadPersistent(filename, warn);
return NetworkTableInstance::GetDefault().LoadPersistent(filename, warn);
}
std::shared_ptr<NetworkTable> NetworkTable::GetTable(StringRef key) {
if (!s_running) Initialize();
if (key.empty() || key[0] == PATH_SEPARATOR_CHAR) {
return std::make_shared<NetworkTable>(key, private_init());
} else {
llvm::SmallString<128> path;
path += PATH_SEPARATOR_CHAR;
path += key;
return std::make_shared<NetworkTable>(path, private_init());
}
return NetworkTableInstance::GetDefault().GetTable(key);
}
NetworkTable::NetworkTable(StringRef path, const private_init&)
: m_path(path) {}
NetworkTable::NetworkTable(NT_Inst inst, StringRef path)
: m_inst(inst), m_path(path) {}
NetworkTable::~NetworkTable() {
for (auto& i : m_listeners) nt::RemoveEntryListener(i.second);
for (auto& i : m_listeners) RemoveEntryListener(i.second);
}
NetworkTableInstance NetworkTable::GetInstance() const {
return NetworkTableInstance{m_inst};
}
NetworkTableEntry NetworkTable::GetEntry(llvm::StringRef key) const {
std::lock_guard<std::mutex> lock(m_mutex);
NT_Entry& entry = m_entries[key];
if (entry == 0) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
entry = nt::GetEntry(m_inst, path);
}
return NetworkTableEntry{entry};
}
NT_EntryListener NetworkTable::AddEntryListener(TableEntryListener listener,
unsigned int flags) const {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
std::size_t prefix_len = path.size();
return 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(const_cast<NetworkTable*>(this), relative_key,
NetworkTableEntry{event.entry}, event.value, event.flags);
},
flags);
}
NT_EntryListener NetworkTable::AddEntryListener(StringRef key,
TableEntryListener listener,
unsigned int flags) const {
std::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) {
@@ -181,13 +198,12 @@ void NetworkTable::AddTableListenerEx(ITableListener* listener,
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
std::size_t prefix_len = path.size();
unsigned int id = nt::AddEntryListener(
path,
[=](unsigned int /*uid*/, StringRef name,
std::shared_ptr<nt::Value> value, unsigned int flags_) {
StringRef relative_key = name.substr(prefix_len);
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, value, flags_);
listener->ValueChangedEx(this, relative_key, event.value, event.flags);
},
flags);
m_listeners.emplace_back(listener, id);
@@ -207,12 +223,12 @@ void NetworkTable::AddTableListenerEx(StringRef key, ITableListener* listener,
path += PATH_SEPARATOR_CHAR;
std::size_t prefix_len = path.size();
path += key;
unsigned int id = nt::AddEntryListener(
path,
[=](unsigned int /*uid*/, StringRef name,
std::shared_ptr<nt::Value> value, unsigned int flags_) {
if (name != path) return;
listener->ValueChangedEx(this, name.substr(prefix_len), value, flags_);
NT_EntryListener id = nt::AddEntryListener(
m_inst, path,
[=](const EntryNotification& event) {
if (event.name != path) return;
listener->ValueChangedEx(this, event.name.substr(prefix_len),
event.value, event.flags);
},
flags);
m_listeners.emplace_back(listener, id);
@@ -235,18 +251,17 @@ void NetworkTable::AddSubTableListener(ITableListener* listener,
unsigned int flags = NT_NOTIFY_NEW | NT_NOTIFY_IMMEDIATE;
if (localNotify) flags |= NT_NOTIFY_LOCAL;
unsigned int id = nt::AddEntryListener(
path,
[=](unsigned int /*uid*/, StringRef name,
std::shared_ptr<nt::Value> /*value*/, unsigned int flags_) mutable {
StringRef relative_key = name.substr(prefix_len);
NT_EntryListener id = nt::AddEntryListener(
m_inst, path,
[=](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, flags_);
listener->ValueChangedEx(this, sub_table_key, nullptr, event.flags);
},
flags);
m_listeners.emplace_back(listener, id);
@@ -259,22 +274,19 @@ void NetworkTable::RemoveTableListener(ITableListener* listener) {
[=](const Listener& x) { return x.first == listener; });
for (auto i = matches_begin; i != m_listeners.end(); ++i)
nt::RemoveEntryListener(i->second);
RemoveEntryListener(i->second);
m_listeners.erase(matches_begin, m_listeners.end());
}
std::shared_ptr<ITable> NetworkTable::GetSubTable(StringRef key) const {
std::shared_ptr<NetworkTable> NetworkTable::GetSubTable(StringRef key) const {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return std::make_shared<NetworkTable>(path, private_init());
return std::make_shared<NetworkTable>(m_inst, path);
}
bool NetworkTable::ContainsKey(StringRef key) const {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::GetEntryValue(path) != nullptr;
return GetEntry(key).Exists();
}
bool NetworkTable::ContainsSubTable(StringRef key) const {
@@ -282,17 +294,20 @@ bool NetworkTable::ContainsSubTable(StringRef key) const {
path += PATH_SEPARATOR_CHAR;
path += key;
path += PATH_SEPARATOR_CHAR;
return !nt::GetEntryInfo(path, 0).empty();
return !GetEntryInfo(m_inst, path, 0).empty();
}
std::vector<std::string> NetworkTable::GetKeys(int types) const {
std::vector<std::string> keys;
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
for (auto& entry : nt::GetEntryInfo(path, types)) {
auto relative_key = StringRef(entry.name).substr(path.size());
auto infos = GetEntryInfo(m_inst, path, types);
std::lock_guard<std::mutex> lock(m_mutex);
for (auto& info : infos) {
auto relative_key = StringRef(info.name).substr(path.size());
if (relative_key.find(PATH_SEPARATOR_CHAR) != StringRef::npos) continue;
keys.push_back(relative_key);
m_entries[relative_key] = info.entry;
}
return keys;
}
@@ -301,7 +316,7 @@ std::vector<std::string> NetworkTable::GetSubTables() const {
std::vector<std::string> keys;
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
for (auto& entry : nt::GetEntryInfo(path, 0)) {
for (auto& entry : GetEntryInfo(m_inst, path, 0)) {
auto relative_key = StringRef(entry.name).substr(path.size());
std::size_t end_subtable = relative_key.find(PATH_SEPARATOR_CHAR);
if (end_subtable == StringRef::npos) continue;
@@ -311,242 +326,137 @@ std::vector<std::string> NetworkTable::GetSubTables() const {
}
void NetworkTable::SetPersistent(StringRef key) {
SetFlags(key, NT_PERSISTENT);
GetEntry(key).SetPersistent();
}
void NetworkTable::ClearPersistent(StringRef key) {
ClearFlags(key, NT_PERSISTENT);
GetEntry(key).ClearPersistent();
}
bool NetworkTable::IsPersistent(StringRef key) const {
return (GetFlags(key) & NT_PERSISTENT) != 0;
return GetEntry(key).IsPersistent();
}
void NetworkTable::SetFlags(StringRef key, unsigned int flags) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
nt::SetEntryFlags(path, nt::GetEntryFlags(path) | flags);
GetEntry(key).SetFlags(flags);
}
void NetworkTable::ClearFlags(StringRef key, unsigned int flags) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
nt::SetEntryFlags(path, nt::GetEntryFlags(path) & ~flags);
GetEntry(key).ClearFlags(flags);
}
unsigned int NetworkTable::GetFlags(StringRef key) const {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::GetEntryFlags(path);
return GetEntry(key).GetFlags();
}
void NetworkTable::Delete(StringRef key) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
nt::DeleteEntry(path);
}
void NetworkTable::Delete(StringRef key) { GetEntry(key).Delete(); }
bool NetworkTable::PutNumber(StringRef key, double value) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::SetEntryValue(path, nt::Value::MakeDouble(value));
return GetEntry(key).SetDouble(value);
}
bool NetworkTable::SetDefaultNumber(StringRef key, double defaultValue) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::SetDefaultEntryValue(path, nt::Value::MakeDouble(defaultValue));
return GetEntry(key).SetDefaultDouble(defaultValue);
}
double NetworkTable::GetNumber(StringRef key, double defaultValue) const {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
auto value = nt::GetEntryValue(path);
if (!value || value->type() != NT_DOUBLE) return defaultValue;
return value->GetDouble();
return GetEntry(key).GetDouble(defaultValue);
}
bool NetworkTable::PutString(StringRef key, StringRef value) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::SetEntryValue(path, nt::Value::MakeString(value));
return GetEntry(key).SetString(value);
}
bool NetworkTable::SetDefaultString(StringRef key, StringRef defaultValue) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::SetDefaultEntryValue(path, nt::Value::MakeString(defaultValue));
return GetEntry(key).SetDefaultString(defaultValue);
}
std::string NetworkTable::GetString(StringRef key,
StringRef defaultValue) const {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
auto value = nt::GetEntryValue(path);
if (!value || value->type() != NT_STRING) return defaultValue;
return value->GetString();
return GetEntry(key).GetString(defaultValue);
}
bool NetworkTable::PutBoolean(StringRef key, bool value) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::SetEntryValue(path, nt::Value::MakeBoolean(value));
return GetEntry(key).SetBoolean(value);
}
bool NetworkTable::SetDefaultBoolean(StringRef key, bool defaultValue) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::SetDefaultEntryValue(path, nt::Value::MakeBoolean(defaultValue));
return GetEntry(key).SetDefaultBoolean(defaultValue);
}
bool NetworkTable::GetBoolean(StringRef key, bool defaultValue) const {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
auto value = nt::GetEntryValue(path);
if (!value || value->type() != NT_BOOLEAN) return defaultValue;
return value->GetBoolean();
return GetEntry(key).GetBoolean(defaultValue);
}
bool NetworkTable::PutBooleanArray(llvm::StringRef key,
llvm::ArrayRef<int> value) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::SetEntryValue(path, nt::Value::MakeBooleanArray(value));
return GetEntry(key).SetBooleanArray(value);
}
bool NetworkTable::SetDefaultBooleanArray(StringRef key,
llvm::ArrayRef<int> defaultValue) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::SetDefaultEntryValue(path,
nt::Value::MakeBooleanArray(defaultValue));
return GetEntry(key).SetDefaultBooleanArray(defaultValue);
}
std::vector<int> NetworkTable::GetBooleanArray(
llvm::StringRef key, llvm::ArrayRef<int> defaultValue) const {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
auto value = nt::GetEntryValue(path);
if (!value || value->type() != NT_BOOLEAN_ARRAY) return defaultValue;
return value->GetBooleanArray();
return GetEntry(key).GetBooleanArray(defaultValue);
}
bool NetworkTable::PutNumberArray(llvm::StringRef key,
llvm::ArrayRef<double> value) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::SetEntryValue(path, nt::Value::MakeDoubleArray(value));
return GetEntry(key).SetDoubleArray(value);
}
bool NetworkTable::SetDefaultNumberArray(StringRef key,
llvm::ArrayRef<double> defaultValue) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::SetDefaultEntryValue(path,
nt::Value::MakeDoubleArray(defaultValue));
return GetEntry(key).SetDefaultDoubleArray(defaultValue);
}
std::vector<double> NetworkTable::GetNumberArray(
llvm::StringRef key, llvm::ArrayRef<double> defaultValue) const {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
auto value = nt::GetEntryValue(path);
if (!value || value->type() != NT_DOUBLE_ARRAY) return defaultValue;
return value->GetDoubleArray();
return GetEntry(key).GetDoubleArray(defaultValue);
}
bool NetworkTable::PutStringArray(llvm::StringRef key,
llvm::ArrayRef<std::string> value) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::SetEntryValue(path, nt::Value::MakeStringArray(value));
return GetEntry(key).SetStringArray(value);
}
bool NetworkTable::SetDefaultStringArray(
StringRef key, llvm::ArrayRef<std::string> defaultValue) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::SetDefaultEntryValue(path,
nt::Value::MakeStringArray(defaultValue));
return GetEntry(key).SetDefaultStringArray(defaultValue);
}
std::vector<std::string> NetworkTable::GetStringArray(
llvm::StringRef key, llvm::ArrayRef<std::string> defaultValue) const {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
auto value = nt::GetEntryValue(path);
if (!value || value->type() != NT_STRING_ARRAY) return defaultValue;
return value->GetStringArray();
return GetEntry(key).GetStringArray(defaultValue);
}
bool NetworkTable::PutRaw(llvm::StringRef key, llvm::StringRef value) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::SetEntryValue(path, nt::Value::MakeRaw(value));
return GetEntry(key).SetRaw(value);
}
bool NetworkTable::SetDefaultRaw(StringRef key, StringRef defaultValue) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::SetDefaultEntryValue(path, nt::Value::MakeRaw(defaultValue));
return GetEntry(key).SetDefaultRaw(defaultValue);
}
std::string NetworkTable::GetRaw(llvm::StringRef key,
llvm::StringRef defaultValue) const {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
auto value = nt::GetEntryValue(path);
if (!value || value->type() != NT_RAW) return defaultValue;
return value->GetRaw();
return GetEntry(key).GetRaw(defaultValue);
}
bool NetworkTable::PutValue(StringRef key, std::shared_ptr<nt::Value> value) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::SetEntryValue(path, value);
bool NetworkTable::PutValue(StringRef key, std::shared_ptr<Value> value) {
return GetEntry(key).SetValue(value);
}
bool NetworkTable::SetDefaultValue(StringRef key,
std::shared_ptr<nt::Value> defaultValue) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::SetDefaultEntryValue(path, defaultValue);
std::shared_ptr<Value> defaultValue) {
return GetEntry(key).SetDefaultValue(defaultValue);
}
std::shared_ptr<nt::Value> NetworkTable::GetValue(StringRef key) const {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
path += key;
return nt::GetEntryValue(path);
std::shared_ptr<Value> NetworkTable::GetValue(StringRef key) const {
return GetEntry(key).GetValue();
}
StringRef NetworkTable::GetPath() const {
return m_path;
}
StringRef NetworkTable::GetPath() const { return m_path; }

View File

@@ -0,0 +1,16 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#include "networktables/NetworkTableEntry.h"
#include "networktables/NetworkTableInstance.h"
using namespace nt;
NetworkTableInstance NetworkTableEntry::GetInstance() const {
return NetworkTableInstance{GetInstanceFromHandle(m_handle)};
}

View File

@@ -0,0 +1,54 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#include "networktables/NetworkTableInstance.h"
#include "llvm/SmallString.h"
using namespace nt;
std::shared_ptr<NetworkTable> NetworkTableInstance::GetTable(
StringRef key) const {
if (key.empty() || key == "/") {
return std::make_shared<NetworkTable>(m_handle, "");
} else if (key[0] == NetworkTable::PATH_SEPARATOR_CHAR) {
return std::make_shared<NetworkTable>(m_handle, key);
} else {
llvm::SmallString<128> path;
path += NetworkTable::PATH_SEPARATOR_CHAR;
path += key;
return std::make_shared<NetworkTable>(m_handle, path);
}
}
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(
StringRef 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);
}

View File

@@ -0,0 +1,16 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#include "networktables/RpcCall.h"
#include "networktables/NetworkTableEntry.h"
using namespace nt;
NetworkTableEntry RpcCall::GetEntry() const {
return NetworkTableEntry{m_entry};
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -226,16 +226,16 @@ struct NT_RpcDefinition* NT_GetRpcDefinitionForTesting(
}
// No need for free as one already exists in the main library
struct NT_RpcCallInfo* NT_GetRpcCallInfoForTesting(
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_RpcCallInfo* info =
static_cast<NT_RpcCallInfo*>(std::calloc(1, sizeof(NT_RpcCallInfo)));
info->rpc_id = rpc_id;
info->call_uid = call_uid;
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_RpcCallInfo);
*struct_size = sizeof(NT_RpcAnswer);
return info;
}
// No need for free as one already exists in the main library