diff --git a/include/networktables/NetworkTable.h b/include/networktables/NetworkTable.h index fa751c9e19..9be56baf03 100644 --- a/include/networktables/NetworkTable.h +++ b/include/networktables/NetworkTable.h @@ -29,6 +29,7 @@ class NetworkTable : public ITable { static std::vector s_ip_addresses; static std::string s_persistent_filename; static bool s_client; + static bool s_enable_ds; static bool s_running; static unsigned int s_port; @@ -86,6 +87,12 @@ class NetworkTable : public ITable { */ static void SetPort(unsigned int port); + /** + * @param enabled whether to enable the connection to the local DS to get + * the robot IP address (defaults to enabled) + */ + static void SetDSClientEnabled(bool enabled); + /** * Sets the persistent filename. * @param filename the filename that the network tables server uses for diff --git a/include/ntcore_c.h b/include/ntcore_c.h index d51c31d575..0744e4a609 100644 --- a/include/ntcore_c.h +++ b/include/ntcore_c.h @@ -362,6 +362,11 @@ void NT_StartServer(const char* persist_filename, const char* listen_address, */ void NT_StopServer(void); +/** Starts Client + * Starts a client. Use NT_SetServer to set the server name and port. + */ +void NT_StartClientNone(void); + /** Starts Client * Starts a client using the specified server and port * @@ -389,6 +394,37 @@ void NT_StartClientMulti(size_t count, const char** server_names, */ void NT_StopClient(void); +/** Sets server address for client (without restarting client). + * + * @param server_name server name (UTF-8 string, null terminated) + * @param port port to communicate over + * + */ +void NT_SetServer(const char* server_name, unsigned int port); + +/** Sets server addresses for client (without restarting client). + * The client will attempt to connect to each server in round robin fashion. + * + * @param count length of the server_names and ports arrays + * @param server_names array of server names (each a UTF-8 string, null + * terminated) + * @param ports array of ports to communicate over (one for each server) + * + */ +void NT_SetServerMulti(size_t count, const char** server_names, + const unsigned int* ports); + +/** Starts requesting server address from Driver Station. + * This connects to the Driver Station running on localhost to obtain the + * server IP address. + * + * @param port server port to use in combination with IP from DS + */ +void NT_StartDSClient(unsigned int port); + +/** Stops requesting server address from Driver Station. */ +void NT_StopDSClient(void); + /** Stop Rpc Server * Stops the Rpc server if it is running. */ diff --git a/include/ntcore_cpp.h b/include/ntcore_cpp.h index 7d7a69a565..67928f7eb8 100644 --- a/include/ntcore_cpp.h +++ b/include/ntcore_cpp.h @@ -255,9 +255,14 @@ void SetNetworkIdentity(StringRef name); void StartServer(StringRef persist_filename, const char* listen_address, unsigned int port); void StopServer(); +void StartClient(); void StartClient(const char* server_name, unsigned int port); void StartClient(ArrayRef> servers); void StopClient(); +void SetServer(const char* server_name, unsigned int port); +void SetServer(ArrayRef> servers); +void StartDSClient(unsigned int port); +void StopDSClient(); void StopRpcServer(); void StopNotifier(); void SetUpdateRate(double interval); diff --git a/java/lib/NetworkTablesJNI.cpp b/java/lib/NetworkTablesJNI.cpp index 1dae51d831..03c77ebdb4 100644 --- a/java/lib/NetworkTablesJNI.cpp +++ b/java/lib/NetworkTablesJNI.cpp @@ -1147,6 +1147,17 @@ JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI nt::StopServer(); } +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: startClient + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_startClient__ + (JNIEnv *env, jclass) +{ + nt::StartClient(); +} + /* * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI * Method: startClient @@ -1205,6 +1216,75 @@ JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI nt::StopClient(); } +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: setServer + * Signature: (Ljava/lang/String;I)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setServer__Ljava_lang_String_2I + (JNIEnv *env, jclass, jstring serverName, jint port) +{ + nt::SetServer(JStringRef{env, serverName}.c_str(), port); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: setServer + * Signature: ([Ljava/lang/String;[I)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setServer___3Ljava_lang_String_2_3I + (JNIEnv *env, jclass, jobjectArray serverNames, jintArray ports) +{ + int len = env->GetArrayLength(serverNames); + if (len != env->GetArrayLength(ports)) { + env->ThrowNew(illegalArgEx, + "serverNames and ports arrays must be the same size"); + return; + } + jint* portInts = env->GetIntArrayElements(ports, nullptr); + if (!portInts) return; + + std::vector names; + std::vector> servers; + names.reserve(len); + servers.reserve(len); + for (int i = 0; i < len; ++i) { + JLocal elem{ + env, static_cast(env->GetObjectArrayElement(serverNames, i))}; + if (!elem) { + env->ThrowNew(illegalArgEx, "null string in serverNames"); + return; + } + names.emplace_back(JStringRef{env, elem}.str()); + servers.emplace_back(std::make_pair(nt::StringRef(names.back()), + portInts[i])); + } + env->ReleaseIntArrayElements(ports, portInts, JNI_ABORT); + nt::SetServer(servers); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: startDSClient + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_startDSClient + (JNIEnv *env, jclass, jint port) +{ + nt::StartDSClient(port); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: stopDSClient + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_stopDSClient + (JNIEnv *env, jclass) +{ + nt::StopDSClient(); +} + /* * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI * Method: setUpdateRate diff --git a/java/src/edu/wpi/first/wpilibj/networktables/NetworkTable.java b/java/src/edu/wpi/first/wpilibj/networktables/NetworkTable.java index 0c4d2169f7..c9194db5fa 100644 --- a/java/src/edu/wpi/first/wpilibj/networktables/NetworkTable.java +++ b/java/src/edu/wpi/first/wpilibj/networktables/NetworkTable.java @@ -21,9 +21,9 @@ public class NetworkTable implements ITable, IRemote { public static final int DEFAULT_PORT = 1735; private static boolean client = false; + private static boolean enableDS = true; private static boolean running = false; private static int port = DEFAULT_PORT; - private static String[] ipAddresses = new String[0]; private static String persistentFilename = "networktables.ini"; private synchronized static void checkInit() { @@ -39,10 +39,9 @@ public class NetworkTable implements ITable, IRemote { if (running) shutdown(); if (client) { - int[] ports = new int[ipAddresses.length]; - for (int i=0; i 0 && + (addresses[0].equals("localhost") || addresses[0].equals("127.0.0.1"))) + NetworkTablesJNI.stopDSClient(); + else if (enableDS) + NetworkTablesJNI.startDSClient(port); } /** @@ -136,6 +137,18 @@ public class NetworkTable implements ITable, IRemote { port = aport; } + /** + * @param enabled whether to enable the connection to the local DS to get + * the robot IP address (defaults to enabled) + */ + public synchronized static void setDSClientEnabled(boolean enabled) { + enableDS = enabled; + if (enableDS) + NetworkTablesJNI.startDSClient(port); + else + NetworkTablesJNI.stopDSClient(); + } + /** * Sets the persistent filename. * @param filename the filename that the network tables server uses for diff --git a/java/src/edu/wpi/first/wpilibj/networktables/NetworkTablesJNI.java b/java/src/edu/wpi/first/wpilibj/networktables/NetworkTablesJNI.java index 7d3df88a79..236e7c0c71 100644 --- a/java/src/edu/wpi/first/wpilibj/networktables/NetworkTablesJNI.java +++ b/java/src/edu/wpi/first/wpilibj/networktables/NetworkTablesJNI.java @@ -148,9 +148,14 @@ public class NetworkTablesJNI { public static native void setNetworkIdentity(String name); public static native void startServer(String persistFilename, String listenAddress, int port); public static native void stopServer(); + public static native void startClient(); public static native void startClient(String serverName, int port); public static native void startClient(String[] serverNames, int[] ports); public static native void stopClient(); + public static native void setServer(String serverName, int port); + public static native void setServer(String[] serverNames, int[] ports); + public static native void startDSClient(int port); + public static native void stopDSClient(); public static native void setUpdateRate(double interval); public static native ConnectionInfo[] getConnections(); diff --git a/ntcore-jni.def b/ntcore-jni.def index 2e9d82229b..9970a95f57 100644 --- a/ntcore-jni.def +++ b/ntcore-jni.def @@ -113,6 +113,11 @@ NT_FreeRpcResultsDefForTesting @114 NT_GetRpcDefinitionForTesting @115 NT_GetRpcCallInfoForTesting @116 +NT_SetServer @117 +NT_SetServerMulti @118 +NT_StartDSClient @119 +NT_StopDSClient @120 + ; JNI functions JNI_OnLoad JNI_OnUnload @@ -167,9 +172,14 @@ Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_callRpc__Ljava_lang_St Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setNetworkIdentity Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_startServer Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_stopServer +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_startClient__ Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_startClient__Ljava_lang_String_2I Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_startClient___3Ljava_lang_String_2_3I Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_stopClient +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setServer__Ljava_lang_String_2I +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setServer___3Ljava_lang_String_2_3I +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_startDSClient +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_stopDSClient Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setUpdateRate Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getConnections Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_savePersistent diff --git a/ntcore.def b/ntcore.def index 9b7320df31..f47d22f366 100644 --- a/ntcore.def +++ b/ntcore.def @@ -112,3 +112,8 @@ NT_GetRpcResultsDefForTesting @113 NT_FreeRpcResultsDefForTesting @114 NT_GetRpcDefinitionForTesting @115 NT_GetRpcCallInfoForTesting @116 + +NT_SetServer @117 +NT_SetServerMulti @118 +NT_StartDSClient @119 +NT_StopDSClient @120 diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index 45642f52a3..c65514be53 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -26,16 +26,16 @@ void Dispatcher::StartServer(llvm::StringRef persist_filename, static_cast(port), listen_address, Logger::GetInstance()))); } -void Dispatcher::StartClient(const char* server_name, unsigned int port) { +void Dispatcher::SetServer(const char* server_name, unsigned int port) { std::string server_name_copy(server_name); - DispatcherBase::StartClient([=]() -> std::unique_ptr { + SetConnector([=]() -> std::unique_ptr { return wpi::TCPConnector::connect(server_name_copy.c_str(), static_cast(port), Logger::GetInstance(), 1); }); } -void Dispatcher::StartClient( +void Dispatcher::SetServer( ArrayRef> servers) { std::vector connectors; for (const auto& server : servers) { @@ -47,9 +47,20 @@ void Dispatcher::StartClient( Logger::GetInstance(), 1); }); } - DispatcherBase::StartClient(std::move(connectors)); + SetConnector(std::move(connectors)); } +void Dispatcher::SetServerOverride(const char* server_name, unsigned int port) { + std::string server_name_copy(server_name); + SetConnectorOverride([=]() -> std::unique_ptr { + return wpi::TCPConnector::connect(server_name_copy.c_str(), + static_cast(port), + Logger::GetInstance(), 1); + }); +} + +void Dispatcher::ClearServerOverride() { ClearConnectorOverride(); } + Dispatcher::Dispatcher() : Dispatcher(Storage::GetInstance(), Notifier::GetInstance()) {} @@ -98,18 +109,11 @@ void DispatcherBase::StartServer( m_clientserver_thread = std::thread(&Dispatcher::ServerThreadMain, this); } -void DispatcherBase::StartClient(Connector connector) { - std::vector connectors; - connectors.push_back(connector); - StartClient(std::move(connectors)); -} - -void DispatcherBase::StartClient(std::vector&& connectors) { +void DispatcherBase::StartClient() { { std::lock_guard lock(m_user_mutex); if (m_active) return; m_active = true; - m_client_connectors = std::move(connectors); } m_server = false; using namespace std::placeholders; @@ -195,6 +199,27 @@ void DispatcherBase::NotifyConnections( for (const auto& conn : m_connections) conn->NotifyIfActive(callback); } +void DispatcherBase::SetConnector(Connector connector) { + std::vector connectors; + connectors.push_back(connector); + SetConnector(std::move(connectors)); +} + +void DispatcherBase::SetConnector(std::vector&& connectors) { + std::lock_guard lock(m_user_mutex); + m_client_connectors = std::move(connectors); +} + +void DispatcherBase::SetConnectorOverride(Connector connector) { + std::lock_guard lock(m_user_mutex); + m_client_connector_override = std::move(connector); +} + +void DispatcherBase::ClearConnectorOverride() { + std::lock_guard lock(m_user_mutex); + m_client_connector_override = nullptr; +} + void DispatcherBase::DispatchThreadMain() { auto timeout_time = std::chrono::steady_clock::now(); @@ -320,9 +345,13 @@ void DispatcherBase::ClientThreadMain() { // get next server to connect to { std::lock_guard lock(m_user_mutex); - if (m_client_connectors.empty()) continue; - if (i >= m_client_connectors.size()) i = 0; - connect = m_client_connectors[i++]; + if (m_client_connector_override) { + connect = m_client_connector_override; + } else { + if (m_client_connectors.empty()) continue; + if (i >= m_client_connectors.size()) i = 0; + connect = m_client_connectors[i++]; + } } // try to connect (with timeout) diff --git a/src/Dispatcher.h b/src/Dispatcher.h index 87b97e29f2..2bfa228bb6 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -41,8 +41,7 @@ class DispatcherBase { void StartServer(llvm::StringRef persist_filename, std::unique_ptr acceptor); - void StartClient(Connector connector); - void StartClient(std::vector&& connectors); + void StartClient(); void Stop(); void SetUpdateRate(double interval); void SetIdentity(llvm::StringRef name); @@ -50,6 +49,12 @@ class DispatcherBase { std::vector GetConnections() const; void NotifyConnections(ConnectionListenerCallback callback) const; + void SetConnector(Connector connector); + void SetConnector(std::vector&& connectors); + + void SetConnectorOverride(Connector connector); + void ClearConnectorOverride(); + bool active() const { return m_active; } DispatcherBase(const DispatcherBase&) = delete; @@ -85,6 +90,7 @@ class DispatcherBase { std::thread m_clientserver_thread; std::unique_ptr m_server_acceptor; + Connector m_client_connector_override; std::vector m_client_connectors; // Mutex for user-accessible items @@ -118,8 +124,12 @@ class Dispatcher : public DispatcherBase { void StartServer(StringRef persist_filename, const char* listen_address, unsigned int port); - void StartClient(const char* server_name, unsigned int port); - void StartClient(ArrayRef> servers); + + void SetServer(const char* server_name, unsigned int port); + void SetServer(ArrayRef> servers); + + void SetServerOverride(const char* server_name, unsigned int port); + void ClearServerOverride(); private: Dispatcher(); diff --git a/src/DsClient.cpp b/src/DsClient.cpp new file mode 100644 index 0000000000..1e2c5c2e07 --- /dev/null +++ b/src/DsClient.cpp @@ -0,0 +1,150 @@ +/*----------------------------------------------------------------------------*/ +/* 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 "DsClient.h" + +#include "llvm/raw_ostream.h" +#include "llvm/SmallString.h" +#include "support/raw_socket_istream.h" +#include "tcpsockets/TCPConnector.h" + +#include "Dispatcher.h" +#include "Log.h" + +using namespace nt; + +ATOMIC_STATIC_INIT(DsClient) + +class DsClient::Thread : public wpi::SafeThread { + public: + Thread(unsigned int port) : m_port(port) {} + + void Main(); + + unsigned int m_port; + std::unique_ptr m_stream; +}; + +void DsClient::Start(unsigned int port) { + auto thr = m_owner.GetThread(); + if (!thr) + m_owner.Start(new Thread(port)); + else + thr->m_port = port; +} + +void DsClient::Stop() { + { + // Close the stream so the read (if any) terminates. + auto thr = m_owner.GetThread(); + if (thr) { + thr->m_active = false; + if (thr->m_stream) thr->m_stream->close(); + } + } + m_owner.Stop(); +} + +void DsClient::Thread::Main() { + unsigned int oldip = 0; + wpi::Logger nolog; // to silence log messages from TCPConnector + + while (m_active) { + // wait for periodic reconnect or termination + auto timeout_time = + std::chrono::steady_clock::now() + std::chrono::milliseconds(500); + unsigned int port; + { + std::unique_lock lock(m_mutex); + m_cond.wait_until(lock, timeout_time, [&] { return !m_active; }); + port = m_port; + } + if (!m_active) goto done; + + // Try to connect to DS on the local machine + m_stream = + wpi::TCPConnector::connect("127.0.0.1", 1742, nolog, 1); + if (!m_active) goto done; + if (!m_stream) continue; + + DEBUG3("connected to DS"); + wpi::raw_socket_istream is(*m_stream); + + while (m_active && !is.has_error()) { + // Read JSON "{...}". This is very limited, does not handle quoted "}" or + // nested {}, but is sufficient for this purpose. + llvm::SmallString<128> json; + char ch; + + // Throw away characters until { + do { + is.read(ch); + if (is.has_error()) break; + if (!m_active) goto done; + } while (ch != '{'); + json += '{'; + + if (is.has_error()) { + m_stream = nullptr; + break; + } + + // Read characters until } + do { + is.read(ch); + if (is.has_error()) break; + if (!m_active) goto done; + json += ch; + } while (ch != '}'); + + if (is.has_error()) { + m_stream = nullptr; + break; + } + DEBUG3("json=" << json); + + // Look for "robotIP":12345, and get 12345 portion + size_t pos = json.find("\"robotIP\""); + if (pos == llvm::StringRef::npos) continue; // could not find? + pos += 9; + pos = json.find(':', pos); + if (pos == llvm::StringRef::npos) continue; // could not find? + size_t endpos = json.find_first_not_of("0123456789", pos + 1); + DEBUG3("found robotIP=" << json.slice(pos + 1, endpos)); + + // Parse into number + unsigned int ip; + if (json.slice(pos + 1, endpos).getAsInteger(10, ip)) continue; // error + + // If zero, clear the server override + if (ip == 0) { + Dispatcher::GetInstance().ClearServerOverride(); + oldip = 0; + continue; + } + + // If unchanged, don't reconnect + if (ip == oldip) continue; + oldip = ip; + + // Convert number into dotted quad + json.clear(); + llvm::raw_svector_ostream os{json}; + os << ((ip >> 24) & 0xff) << "." << ((ip >> 16) & 0xff) << "." + << ((ip >> 8) & 0xff) << "." << (ip & 0xff); + INFO("client: DS overriding server IP to " << os.str()); + Dispatcher::GetInstance().SetServerOverride(json.c_str(), port); + } + + // We disconnected from the DS, clear the server override + Dispatcher::GetInstance().ClearServerOverride(); + oldip = 0; + } + +done: + Dispatcher::GetInstance().ClearServerOverride(); +} diff --git a/src/DsClient.h b/src/DsClient.h new file mode 100644 index 0000000000..9dd905c911 --- /dev/null +++ b/src/DsClient.h @@ -0,0 +1,38 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. 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_DSCLIENT_H_ +#define NT_DSCLIENT_H_ + +#include "support/atomic_static.h" +#include "support/SafeThread.h" + +namespace nt { + +class DsClient { + public: + static DsClient& GetInstance() { + ATOMIC_STATIC(DsClient, instance); + return instance; + } + ~DsClient() = default; + + void Start(unsigned int port); + void Stop(); + + private: + DsClient() = default; + + class Thread; + wpi::SafeThreadOwner m_owner; + + ATOMIC_STATIC_DECL(DsClient) +}; + +} // namespace nt + +#endif // NT_DSCLIENT_H_ diff --git a/src/networktables/NetworkTable.cpp b/src/networktables/NetworkTable.cpp index c45cac8a46..5a689e9311 100644 --- a/src/networktables/NetworkTable.cpp +++ b/src/networktables/NetworkTable.cpp @@ -2,6 +2,7 @@ #include +#include "llvm/raw_ostream.h" #include "llvm/SmallString.h" #include "llvm/StringMap.h" #include "tables/ITableListener.h" @@ -11,20 +12,17 @@ using llvm::StringRef; const char NetworkTable::PATH_SEPARATOR_CHAR = '/'; -std::vector NetworkTable::s_ip_addresses; std::string NetworkTable::s_persistent_filename = "networktables.ini"; bool NetworkTable::s_client = false; +bool NetworkTable::s_enable_ds = true; bool NetworkTable::s_running = false; unsigned int NetworkTable::s_port = NT_DEFAULT_PORT; void NetworkTable::Initialize() { if (s_running) Shutdown(); if (s_client) { - std::vector> servers; - servers.reserve(s_ip_addresses.size()); - for (const auto& ip_address : s_ip_addresses) - servers.emplace_back(std::make_pair(ip_address, s_port)); - nt::StartClient(servers); + nt::StartClient(); + if (s_enable_ds) nt::StartDSClient(s_port); } else nt::StartServer(s_persistent_filename, "", s_port); s_running = true; @@ -32,9 +30,10 @@ void NetworkTable::Initialize() { void NetworkTable::Shutdown() { if (!s_running) return; - if (s_client) + if (s_client) { + nt::StopDSClient(); nt::StopClient(); - else + } else nt::StopServer(); s_running = false; } @@ -44,27 +43,74 @@ void NetworkTable::SetClientMode() { s_client = true; } void NetworkTable::SetServerMode() { s_client = false; } void NetworkTable::SetTeam(int team) { - char tmp[30]; -#ifdef _MSC_VER - sprintf_s(tmp, "roboRIO-%d-FRC.local", team); -#else - using namespace std; - snprintf(tmp, 30, "roboRIO-%d-FRC.local", team); -#endif - SetIPAddress(tmp); + std::pair servers[4]; + + // 10.te.am.2 + llvm::SmallString<32> fixed; + { + llvm::raw_svector_ostream oss{fixed}; + oss << "10." << static_cast(team / 100) << '.' + << static_cast(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--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--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); + } + + nt::SetServer(servers); } void NetworkTable::SetIPAddress(StringRef address) { - s_ip_addresses.clear(); - s_ip_addresses.emplace_back(address); + llvm::SmallString<32> addr_copy{address}; + nt::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(); + else if (s_enable_ds) + nt::StartDSClient(s_port); } void NetworkTable::SetIPAddress(llvm::ArrayRef addresses) { - s_ip_addresses = addresses; + llvm::SmallVector, 8> servers; + for (const auto& ip_address : addresses) + servers.emplace_back(std::make_pair(ip_address, s_port)); + nt::SetServer(servers); + + // 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(); + else if (s_enable_ds) + nt::StartDSClient(s_port); } void NetworkTable::SetPort(unsigned int port) { s_port = port; } +void NetworkTable::SetDSClientEnabled(bool enabled) { + s_enable_ds = enabled; + if (s_enable_ds) + nt::StartDSClient(s_port); + else + nt::StopDSClient(); +} + void NetworkTable::SetPersistentFilename(StringRef filename) { s_persistent_filename = filename; } diff --git a/src/ntcore_c.cpp b/src/ntcore_c.cpp index e8a40f1ba8..34ffb301bd 100644 --- a/src/ntcore_c.cpp +++ b/src/ntcore_c.cpp @@ -372,6 +372,8 @@ void NT_StartServer(const char* persist_filename, const char* listen_address, void NT_StopServer(void) { nt::StopServer(); } +void NT_StartClientNone(void) { nt::StartClient(); } + void NT_StartClient(const char* server_name, unsigned int port) { nt::StartClient(server_name, port); } @@ -387,6 +389,23 @@ void NT_StartClientMulti(size_t count, const char** server_names, void NT_StopClient(void) { nt::StopClient(); } +void NT_SetServer(const char* server_name, unsigned int port) { + nt::SetServer(server_name, port); +} + +void NT_SetServerMulti(size_t count, const char** server_names, + const unsigned int* ports) { + std::vector> servers; + servers.reserve(count); + for (size_t i = 0; i < count; ++i) + servers.emplace_back(std::make_pair(server_names[i], ports[i])); + nt::SetServer(servers); +} + +void NT_StartDSClient(unsigned int port) { nt::StartDSClient(port); } + +void NT_StopDSClient(void) { nt::StopDSClient(); } + void NT_StopRpcServer(void) { nt::StopRpcServer(); } void NT_StopNotifier(void) { nt::StopNotifier(); } diff --git a/src/ntcore_cpp.cpp b/src/ntcore_cpp.cpp index f80fcc871a..5ae962b4be 100644 --- a/src/ntcore_cpp.cpp +++ b/src/ntcore_cpp.cpp @@ -14,6 +14,7 @@ #include "support/timestamp.h" #include "Log.h" #include "Dispatcher.h" +#include "DsClient.h" #include "Notifier.h" #include "RpcServer.h" #include "Storage.h" @@ -246,16 +247,34 @@ void StartServer(StringRef persist_filename, const char* listen_address, void StopServer() { Dispatcher::GetInstance().Stop(); } +void StartClient() { Dispatcher::GetInstance().StartClient(); } + void StartClient(const char* server_name, unsigned int port) { - Dispatcher::GetInstance().StartClient(server_name, port); + auto& d = Dispatcher::GetInstance(); + d.SetServer(server_name, port); + d.StartClient(); } void StartClient(ArrayRef> servers) { - Dispatcher::GetInstance().StartClient(servers); + auto& d = Dispatcher::GetInstance(); + d.SetServer(servers); + d.StartClient(); } void StopClient() { Dispatcher::GetInstance().Stop(); } +void SetServer(const char* server_name, unsigned int port) { + Dispatcher::GetInstance().SetServer(server_name, port); +} + +void SetServer(ArrayRef> servers) { + Dispatcher::GetInstance().SetServer(servers); +} + +void StartDSClient(unsigned int port) { DsClient::GetInstance().Start(port); } + +void StopDSClient() { DsClient::GetInstance().Stop(); } + void StopRpcServer() { RpcServer::GetInstance().Stop(); } void StopNotifier() { Notifier::GetInstance().Stop(); }