mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-01 02:41:48 +00:00
Add FRC Driver Station connection support.
The 2017 FRC Driver Station supports getting the robot IP via a TCP connection that returns JSON. Use this to support overriding the server IP address used for client connections. Default to using this approach for client connections in the NetworkTable interfaces. Add support for setting the server address without stopping and restarting the client. SetTeam now also round-robins by default.
This commit is contained in:
@@ -29,6 +29,7 @@ class NetworkTable : public ITable {
|
||||
static std::vector<std::string> s_ip_addresses;
|
||||
static std::string s_persistent_filename;
|
||||
static bool s_client;
|
||||
static bool s_enable_ds;
|
||||
static bool s_running;
|
||||
static unsigned int s_port;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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<std::pair<StringRef, unsigned int>> servers);
|
||||
void StopClient();
|
||||
void SetServer(const char* server_name, unsigned int port);
|
||||
void SetServer(ArrayRef<std::pair<StringRef, unsigned int>> servers);
|
||||
void StartDSClient(unsigned int port);
|
||||
void StopDSClient();
|
||||
void StopRpcServer();
|
||||
void StopNotifier();
|
||||
void SetUpdateRate(double interval);
|
||||
|
||||
@@ -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<std::string> names;
|
||||
std::vector<std::pair<nt::StringRef, unsigned int>> servers;
|
||||
names.reserve(len);
|
||||
servers.reserve(len);
|
||||
for (int i = 0; i < len; ++i) {
|
||||
JLocal<jstring> elem{
|
||||
env, static_cast<jstring>(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
|
||||
|
||||
@@ -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<ipAddresses.length; i++)
|
||||
ports[i] = port;
|
||||
NetworkTablesJNI.startClient(ipAddresses, ports);
|
||||
NetworkTablesJNI.startClient();
|
||||
if (enableDS)
|
||||
NetworkTablesJNI.startDSClient(port);
|
||||
} else
|
||||
NetworkTablesJNI.startServer(persistentFilename, "", port);
|
||||
running = true;
|
||||
@@ -54,9 +53,10 @@ public class NetworkTable implements ITable, IRemote {
|
||||
public synchronized static void shutdown() {
|
||||
if (!running)
|
||||
return;
|
||||
if (client)
|
||||
if (client) {
|
||||
NetworkTablesJNI.stopDSClient();
|
||||
NetworkTablesJNI.stopClient();
|
||||
else
|
||||
} else
|
||||
NetworkTablesJNI.stopServer();
|
||||
running = false;
|
||||
}
|
||||
@@ -90,7 +90,12 @@ public class NetworkTable implements ITable, IRemote {
|
||||
* @param team the team number
|
||||
*/
|
||||
public synchronized static void setTeam(int team) {
|
||||
setIPAddress("roboRIO-" + team + "-FRC.local");
|
||||
String[] addresses = new String[4];
|
||||
addresses[0] = "10." + (int)(team / 100) + "." + (int)(team % 100) + ".2";
|
||||
addresses[1] = "172.22.11.2";
|
||||
addresses[2] = "roboRIO-" + team + "-FRC.local";
|
||||
addresses[3] = "roboRIO-" + team + "-FRC.lan";
|
||||
setIPAddress(addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,11 +103,9 @@ public class NetworkTable implements ITable, IRemote {
|
||||
* mode
|
||||
*/
|
||||
public synchronized static void setIPAddress(final String address) {
|
||||
if (ipAddresses.length == 1 && ipAddresses[0].equals(address))
|
||||
return;
|
||||
checkInit();
|
||||
ipAddresses = new String[1];
|
||||
ipAddresses[0] = address;
|
||||
String[] addresses = new String[1];
|
||||
addresses[0] = address;
|
||||
setIPAddress(addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,19 +113,17 @@ public class NetworkTable implements ITable, IRemote {
|
||||
* client mode (in round robin order)
|
||||
*/
|
||||
public synchronized static void setIPAddress(final String[] addresses) {
|
||||
if (ipAddresses.length == addresses.length) {
|
||||
boolean match = true;
|
||||
for (int i=0; i<addresses.length; i++) {
|
||||
if (!ipAddresses[i].equals(addresses[i])) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match)
|
||||
return;
|
||||
}
|
||||
checkInit();
|
||||
ipAddresses = addresses;
|
||||
int[] ports = new int[addresses.length];
|
||||
for (int i=0; i<addresses.length; i++)
|
||||
ports[i] = port;
|
||||
NetworkTablesJNI.setServer(addresses, ports);
|
||||
|
||||
// Stop the DS client if we're explicitly connecting to localhost
|
||||
if (addresses.length > 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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,16 +26,16 @@ void Dispatcher::StartServer(llvm::StringRef persist_filename,
|
||||
static_cast<int>(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<wpi::NetworkStream> {
|
||||
SetConnector([=]() -> std::unique_ptr<wpi::NetworkStream> {
|
||||
return wpi::TCPConnector::connect(server_name_copy.c_str(),
|
||||
static_cast<int>(port),
|
||||
Logger::GetInstance(), 1);
|
||||
});
|
||||
}
|
||||
|
||||
void Dispatcher::StartClient(
|
||||
void Dispatcher::SetServer(
|
||||
ArrayRef<std::pair<StringRef, unsigned int>> servers) {
|
||||
std::vector<Connector> 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<wpi::NetworkStream> {
|
||||
return wpi::TCPConnector::connect(server_name_copy.c_str(),
|
||||
static_cast<int>(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<Connector> connectors;
|
||||
connectors.push_back(connector);
|
||||
StartClient(std::move(connectors));
|
||||
}
|
||||
|
||||
void DispatcherBase::StartClient(std::vector<Connector>&& connectors) {
|
||||
void DispatcherBase::StartClient() {
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<Connector> connectors;
|
||||
connectors.push_back(connector);
|
||||
SetConnector(std::move(connectors));
|
||||
}
|
||||
|
||||
void DispatcherBase::SetConnector(std::vector<Connector>&& connectors) {
|
||||
std::lock_guard<std::mutex> lock(m_user_mutex);
|
||||
m_client_connectors = std::move(connectors);
|
||||
}
|
||||
|
||||
void DispatcherBase::SetConnectorOverride(Connector connector) {
|
||||
std::lock_guard<std::mutex> lock(m_user_mutex);
|
||||
m_client_connector_override = std::move(connector);
|
||||
}
|
||||
|
||||
void DispatcherBase::ClearConnectorOverride() {
|
||||
std::lock_guard<std::mutex> 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<std::mutex> 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)
|
||||
|
||||
@@ -41,8 +41,7 @@ class DispatcherBase {
|
||||
|
||||
void StartServer(llvm::StringRef persist_filename,
|
||||
std::unique_ptr<wpi::NetworkAcceptor> acceptor);
|
||||
void StartClient(Connector connector);
|
||||
void StartClient(std::vector<Connector>&& connectors);
|
||||
void StartClient();
|
||||
void Stop();
|
||||
void SetUpdateRate(double interval);
|
||||
void SetIdentity(llvm::StringRef name);
|
||||
@@ -50,6 +49,12 @@ class DispatcherBase {
|
||||
std::vector<ConnectionInfo> GetConnections() const;
|
||||
void NotifyConnections(ConnectionListenerCallback callback) const;
|
||||
|
||||
void SetConnector(Connector connector);
|
||||
void SetConnector(std::vector<Connector>&& 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<wpi::NetworkAcceptor> m_server_acceptor;
|
||||
Connector m_client_connector_override;
|
||||
std::vector<Connector> 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<std::pair<StringRef, unsigned int>> servers);
|
||||
|
||||
void SetServer(const char* server_name, unsigned int port);
|
||||
void SetServer(ArrayRef<std::pair<StringRef, unsigned int>> servers);
|
||||
|
||||
void SetServerOverride(const char* server_name, unsigned int port);
|
||||
void ClearServerOverride();
|
||||
|
||||
private:
|
||||
Dispatcher();
|
||||
|
||||
150
src/DsClient.cpp
Normal file
150
src/DsClient.cpp
Normal file
@@ -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<wpi::NetworkStream> 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<std::mutex> lock(m_mutex);
|
||||
m_cond.wait_until(lock, timeout_time, [&] { return !m_active; });
|
||||
port = m_port;
|
||||
}
|
||||
if (!m_active) goto done;
|
||||
|
||||
// Try to connect to DS on the local machine
|
||||
m_stream =
|
||||
wpi::TCPConnector::connect("127.0.0.1", 1742, nolog, 1);
|
||||
if (!m_active) goto done;
|
||||
if (!m_stream) continue;
|
||||
|
||||
DEBUG3("connected to DS");
|
||||
wpi::raw_socket_istream is(*m_stream);
|
||||
|
||||
while (m_active && !is.has_error()) {
|
||||
// Read JSON "{...}". This is very limited, does not handle quoted "}" or
|
||||
// nested {}, but is sufficient for this purpose.
|
||||
llvm::SmallString<128> json;
|
||||
char ch;
|
||||
|
||||
// Throw away characters until {
|
||||
do {
|
||||
is.read(ch);
|
||||
if (is.has_error()) break;
|
||||
if (!m_active) goto done;
|
||||
} while (ch != '{');
|
||||
json += '{';
|
||||
|
||||
if (is.has_error()) {
|
||||
m_stream = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
// Read characters until }
|
||||
do {
|
||||
is.read(ch);
|
||||
if (is.has_error()) break;
|
||||
if (!m_active) goto done;
|
||||
json += ch;
|
||||
} while (ch != '}');
|
||||
|
||||
if (is.has_error()) {
|
||||
m_stream = nullptr;
|
||||
break;
|
||||
}
|
||||
DEBUG3("json=" << json);
|
||||
|
||||
// Look for "robotIP":12345, and get 12345 portion
|
||||
size_t pos = json.find("\"robotIP\"");
|
||||
if (pos == llvm::StringRef::npos) continue; // could not find?
|
||||
pos += 9;
|
||||
pos = json.find(':', pos);
|
||||
if (pos == llvm::StringRef::npos) continue; // could not find?
|
||||
size_t endpos = json.find_first_not_of("0123456789", pos + 1);
|
||||
DEBUG3("found robotIP=" << json.slice(pos + 1, endpos));
|
||||
|
||||
// Parse into number
|
||||
unsigned int ip;
|
||||
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();
|
||||
}
|
||||
38
src/DsClient.h
Normal file
38
src/DsClient.h
Normal file
@@ -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<Thread> m_owner;
|
||||
|
||||
ATOMIC_STATIC_DECL(DsClient)
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NT_DSCLIENT_H_
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#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<std::string> 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<std::pair<StringRef, unsigned int>> 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<StringRef, unsigned int> servers[4];
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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<std::string> addresses) {
|
||||
s_ip_addresses = 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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -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<std::pair<StringRef, unsigned int>> 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(); }
|
||||
|
||||
@@ -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<std::pair<StringRef, unsigned int>> 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<std::pair<StringRef, unsigned int>> 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(); }
|
||||
|
||||
Reference in New Issue
Block a user