mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-22 01:11:42 +00:00
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:
@@ -90,12 +90,12 @@ dependencies {
|
||||
model {
|
||||
jniConfigs {
|
||||
ntcore(JNIConfig) {
|
||||
jniDefinitionClasses = [ "edu.wpi.first.wpilibj.networktables.NetworkTablesJNI" ]
|
||||
jniDefinitionClasses = [ "edu.wpi.first.networktables.NetworkTablesJNI" ]
|
||||
jniArmHeaderLocations = [ all: file("${rootDir}/src/arm-linux-jni") ]
|
||||
sourceSets = [ project.sourceSets.main ]
|
||||
}
|
||||
ntcoreJNI(JNIConfig) {
|
||||
jniDefinitionClasses = [ "edu.wpi.first.wpilibj.networktables.NetworkTablesJNI" ]
|
||||
jniDefinitionClasses = [ "edu.wpi.first.networktables.NetworkTablesJNI" ]
|
||||
jniArmHeaderLocations = [ all: file("${rootDir}/src/arm-linux-jni") ]
|
||||
sourceSets = [ project.sourceSets.main ]
|
||||
}
|
||||
|
||||
@@ -1,25 +1,34 @@
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <thread>
|
||||
|
||||
#include "ntcore.h"
|
||||
|
||||
int main() {
|
||||
nt::SetLogger(
|
||||
[](unsigned int level, const char* file, unsigned int line,
|
||||
const char* msg) {
|
||||
std::fputs(msg, stderr);
|
||||
auto inst = nt::GetDefaultInstance();
|
||||
nt::AddLogger(
|
||||
inst,
|
||||
[](const nt::LogMessage& msg) {
|
||||
std::fputs(msg.message.c_str(), stderr);
|
||||
std::fputc('\n', stderr);
|
||||
},
|
||||
0);
|
||||
nt::StartClient("127.0.0.1", 10000);
|
||||
0, UINT_MAX);
|
||||
nt::StartClient(inst, "127.0.0.1", 10000);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
auto foo = nt::GetEntryValue("/foo");
|
||||
if (foo && foo->IsDouble()) printf("Got foo: %g\n", foo->GetDouble());
|
||||
nt::SetEntryValue("/bar", nt::Value::MakeBoolean(false));
|
||||
nt::SetEntryFlags("/bar", NT_PERSISTENT);
|
||||
nt::SetEntryValue("/bar2", nt::Value::MakeBoolean(true));
|
||||
nt::SetEntryValue("/bar2", nt::Value::MakeBoolean(false));
|
||||
nt::SetEntryValue("/bar2", nt::Value::MakeBoolean(true));
|
||||
|
||||
auto foo = nt::GetEntry(inst, "/foo");
|
||||
auto foo_val = nt::GetEntryValue(foo);
|
||||
if (foo_val && foo_val->IsDouble())
|
||||
printf("Got foo: %g\n", foo_val->GetDouble());
|
||||
|
||||
auto bar = nt::GetEntry(inst, "/bar");
|
||||
nt::SetEntryValue(bar, nt::Value::MakeBoolean(false));
|
||||
nt::SetEntryFlags(bar, NT_PERSISTENT);
|
||||
|
||||
auto bar2 = nt::GetEntry(inst, "/bar2");
|
||||
nt::SetEntryValue(bar2, nt::Value::MakeBoolean(true));
|
||||
nt::SetEntryValue(bar2, nt::Value::MakeBoolean(false));
|
||||
nt::SetEntryValue(bar2, nt::Value::MakeBoolean(true));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
}
|
||||
|
||||
@@ -1,48 +1,60 @@
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <thread>
|
||||
|
||||
#include "support/json.h"
|
||||
|
||||
#include "ntcore.h"
|
||||
|
||||
std::string callback1(nt::StringRef name, nt::StringRef params_str,
|
||||
const nt::ConnectionInfo& conn_info) {
|
||||
auto params = nt::UnpackRpcValues(params_str, NT_DOUBLE);
|
||||
if (params.empty()) {
|
||||
std::fputs("empty params?\n", stderr);
|
||||
return "";
|
||||
void callback1(const nt::RpcAnswer& answer) {
|
||||
wpi::json params;
|
||||
try {
|
||||
params = wpi::json::from_cbor(answer.params);
|
||||
} catch (wpi::json::parse_error err) {
|
||||
std::fputs("could not decode params?\n", stderr);
|
||||
return;
|
||||
}
|
||||
std::fprintf(stderr, "called with %g\n", params[0]->GetDouble());
|
||||
if (!params.is_number()) {
|
||||
std::fputs("did not get number\n", stderr);
|
||||
return;
|
||||
}
|
||||
double val = params.get<double>();
|
||||
std::fprintf(stderr, "called with %g\n", val);
|
||||
|
||||
return nt::PackRpcValues(nt::Value::MakeDouble(params[0]->GetDouble() + 1.2));
|
||||
answer.PostResponse(wpi::json::to_cbor(val + 1.2));
|
||||
}
|
||||
|
||||
int main() {
|
||||
nt::SetLogger(
|
||||
[](unsigned int level, const char* file, unsigned int line,
|
||||
const char* msg) {
|
||||
std::fputs(msg, stderr);
|
||||
auto inst = nt::GetDefaultInstance();
|
||||
nt::AddLogger(
|
||||
inst,
|
||||
[](const nt::LogMessage& msg) {
|
||||
std::fputs(msg.message.c_str(), stderr);
|
||||
std::fputc('\n', stderr);
|
||||
},
|
||||
0);
|
||||
0, UINT_MAX);
|
||||
|
||||
nt::RpcDefinition def;
|
||||
def.version = 1;
|
||||
def.name = "myfunc1";
|
||||
def.params.emplace_back("param1", nt::Value::MakeDouble(0.0));
|
||||
def.results.emplace_back("result1", NT_DOUBLE);
|
||||
nt::CreateRpc("func1", nt::PackRpcDefinition(def), callback1);
|
||||
nt::StartServer(inst, "rpc_local.ini", "", 10000);
|
||||
auto entry = nt::GetEntry(inst, "func1");
|
||||
nt::CreateRpc(entry, nt::StringRef("", 1), callback1);
|
||||
std::fputs("calling rpc\n", stderr);
|
||||
unsigned int call1_uid =
|
||||
nt::CallRpc("func1", nt::PackRpcValues(nt::Value::MakeDouble(2.0)));
|
||||
unsigned int call1_uid = nt::CallRpc(entry, wpi::json::to_cbor(2.0));
|
||||
std::string call1_result_str;
|
||||
std::fputs("waiting for rpc result\n", stderr);
|
||||
nt::GetRpcResult(true, call1_uid, &call1_result_str);
|
||||
auto call1_result = nt::UnpackRpcValues(call1_result_str, NT_DOUBLE);
|
||||
if (call1_result.empty()) {
|
||||
std::fputs("empty result?\n", stderr);
|
||||
nt::GetRpcResult(entry, call1_uid, &call1_result_str);
|
||||
wpi::json call1_result;
|
||||
try {
|
||||
call1_result = wpi::json::from_cbor(call1_result_str);
|
||||
} catch (wpi::json::parse_error err) {
|
||||
std::fputs("could not decode result?\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
std::fprintf(stderr, "got %g\n", call1_result[0]->GetDouble());
|
||||
if (!call1_result.is_number()) {
|
||||
std::fputs("result is not number?\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
std::fprintf(stderr, "got %g\n", call1_result.get<double>());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,38 +1,50 @@
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
|
||||
#include "support/json.h"
|
||||
|
||||
#include "ntcore.h"
|
||||
|
||||
std::string callback1(nt::StringRef name, nt::StringRef params_str,
|
||||
const nt::ConnectionInfo& conn_info) {
|
||||
auto params = nt::UnpackRpcValues(params_str, NT_DOUBLE);
|
||||
if (params.empty()) {
|
||||
std::fputs("empty params?\n", stderr);
|
||||
return "";
|
||||
void callback1(const nt::RpcAnswer& answer) {
|
||||
wpi::json params;
|
||||
try {
|
||||
params = wpi::json::from_cbor(answer.params);
|
||||
} catch (wpi::json::parse_error err) {
|
||||
std::fputs("could not decode params?\n", stderr);
|
||||
return;
|
||||
}
|
||||
return nt::PackRpcValues(nt::Value::MakeDouble(params[0]->GetDouble() + 1.2));
|
||||
if (!params.is_number()) {
|
||||
std::fputs("did not get number\n", stderr);
|
||||
return;
|
||||
}
|
||||
double val = params.get<double>();
|
||||
answer.PostResponse(wpi::json::to_cbor(val + 1.2));
|
||||
}
|
||||
|
||||
int main() {
|
||||
nt::RpcDefinition def;
|
||||
def.version = 1;
|
||||
def.name = "myfunc1";
|
||||
def.params.emplace_back("param1", nt::Value::MakeDouble(0.0));
|
||||
def.results.emplace_back("result1", NT_DOUBLE);
|
||||
nt::CreateRpc("func1", nt::PackRpcDefinition(def), callback1);
|
||||
auto inst = nt::GetDefaultInstance();
|
||||
nt::StartServer(inst, "rpc_speed.ini", "", 10000);
|
||||
auto entry = nt::GetEntry(inst, "func1");
|
||||
nt::CreateRpc(entry, nt::StringRef("", 1), callback1);
|
||||
std::string call1_result_str;
|
||||
|
||||
auto start2 = std::chrono::high_resolution_clock::now();
|
||||
auto start = nt::Now();
|
||||
for (int i=0; i<10000; ++i) {
|
||||
unsigned int call1_uid =
|
||||
nt::CallRpc("func1", nt::PackRpcValues(nt::Value::MakeDouble(i)));
|
||||
nt::GetRpcResult(true, call1_uid, &call1_result_str);
|
||||
auto call1_result = nt::UnpackRpcValues(call1_result_str, NT_DOUBLE);
|
||||
if (call1_result.empty()) {
|
||||
std::fputs("empty result?\n", stderr);
|
||||
unsigned int call1_uid = nt::CallRpc(entry, wpi::json::to_cbor(i));
|
||||
nt::GetRpcResult(entry, call1_uid, &call1_result_str);
|
||||
wpi::json call1_result;
|
||||
try {
|
||||
call1_result = wpi::json::from_cbor(call1_result_str);
|
||||
} catch (wpi::json::parse_error err) {
|
||||
std::fputs("could not decode result?\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
if (!call1_result.is_number()) {
|
||||
std::fputs("result is not number?\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,31 @@
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <thread>
|
||||
|
||||
#include "ntcore.h"
|
||||
|
||||
int main() {
|
||||
nt::SetLogger(
|
||||
[](unsigned int level, const char* file, unsigned int line,
|
||||
const char* msg) {
|
||||
std::fputs(msg, stderr);
|
||||
auto inst = nt::GetDefaultInstance();
|
||||
nt::AddLogger(
|
||||
inst,
|
||||
[](const nt::LogMessage& msg) {
|
||||
std::fputs(msg.message.c_str(), stderr);
|
||||
std::fputc('\n', stderr);
|
||||
},
|
||||
0);
|
||||
nt::StartServer("persistent.ini", "", 10000);
|
||||
0, UINT_MAX);
|
||||
nt::StartServer(inst, "persistent.ini", "", 10000);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
nt::SetEntryValue("/foo", nt::Value::MakeDouble(0.5));
|
||||
nt::SetEntryFlags("/foo", NT_PERSISTENT);
|
||||
nt::SetEntryValue("/foo2", nt::Value::MakeDouble(0.5));
|
||||
nt::SetEntryValue("/foo2", nt::Value::MakeDouble(0.7));
|
||||
nt::SetEntryValue("/foo2", nt::Value::MakeDouble(0.6));
|
||||
nt::SetEntryValue("/foo2", nt::Value::MakeDouble(0.5));
|
||||
|
||||
auto foo = nt::GetEntry(inst, "/foo");
|
||||
nt::SetEntryValue(foo, nt::Value::MakeDouble(0.5));
|
||||
nt::SetEntryFlags(foo, NT_PERSISTENT);
|
||||
|
||||
auto foo2 = nt::GetEntry(inst, "/foo2");
|
||||
nt::SetEntryValue(foo2, nt::Value::MakeDouble(0.5));
|
||||
nt::SetEntryValue(foo2, nt::Value::MakeDouble(0.7));
|
||||
nt::SetEntryValue(foo2, nt::Value::MakeDouble(0.6));
|
||||
nt::SetEntryValue(foo2, nt::Value::MakeDouble(0.5));
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package edu.wpi.first.ntcore;
|
||||
|
||||
import edu.wpi.first.wpilibj.networktables.NetworkTablesJNI;
|
||||
import edu.wpi.first.networktables.NetworkTablesJNI;
|
||||
import edu.wpi.first.wpiutil.RuntimeDetector;
|
||||
|
||||
public class DevMain {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello World!");
|
||||
System.out.println(RuntimeDetector.getPlatformPath());
|
||||
NetworkTablesJNI.flush();
|
||||
NetworkTablesJNI.flush(NetworkTablesJNI.getDefaultInstance());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#include "ntcore.h"
|
||||
#include "nt_Value.h"
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
nt::SetEntryValue("MyValue", nt::Value::MakeString("Hello World"));
|
||||
auto myValue = nt::GetEntry(nt::GetDefaultInstance(), "MyValue");
|
||||
|
||||
std::cout << nt::GetEntryValue("MyValue")->GetString() << std::endl;
|
||||
nt::SetEntryValue(myValue, nt::Value::MakeString("Hello World"));
|
||||
|
||||
std::cout << nt::GetEntryValue(myValue)->GetString() << std::endl;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* NetworkTables Connection information.
|
||||
*/
|
||||
public final class ConnectionInfo {
|
||||
/**
|
||||
* The remote identifier (as set on the remote node by
|
||||
* {@link NetworkTableInstance#setNetworkIdentity(String)}).
|
||||
*/
|
||||
public final String remote_id;
|
||||
|
||||
/**
|
||||
* The IP address of the remote node.
|
||||
*/
|
||||
public final String remote_ip;
|
||||
|
||||
/**
|
||||
* The port number of the remote node.
|
||||
*/
|
||||
public final int remote_port;
|
||||
|
||||
/**
|
||||
* The last time any update was received from the remote node (same scale as
|
||||
* returned by {@link NetworkTablesJNI#now()}).
|
||||
*/
|
||||
public final long last_update;
|
||||
|
||||
/**
|
||||
* The protocol version being used for this connection. This is in protocol
|
||||
* layer format, so 0x0200 = 2.0, 0x0300 = 3.0).
|
||||
*/
|
||||
public final int protocol_version;
|
||||
|
||||
/** Constructor.
|
||||
* This should generally only be used internally to NetworkTables.
|
||||
* @param remote_id Remote identifier
|
||||
* @param remote_ip Remote IP address
|
||||
* @param remote_port Remote port number
|
||||
* @param last_update Last time an update was received
|
||||
* @param protocol_version The protocol version used for the connection
|
||||
*/
|
||||
public ConnectionInfo(String remote_id, String remote_ip, int remote_port, long last_update, int protocol_version) {
|
||||
this.remote_id = remote_id;
|
||||
this.remote_ip = remote_ip;
|
||||
this.remote_port = remote_port;
|
||||
this.last_update = last_update;
|
||||
this.protocol_version = protocol_version;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* NetworkTables Connection notification.
|
||||
*/
|
||||
public final class ConnectionNotification {
|
||||
/**
|
||||
* Listener that was triggered.
|
||||
*/
|
||||
public final int listener;
|
||||
|
||||
/**
|
||||
* True if event is due to connection being established.
|
||||
*/
|
||||
public final boolean connected;
|
||||
|
||||
/**
|
||||
* Connection information.
|
||||
*/
|
||||
public final ConnectionInfo conn;
|
||||
|
||||
/** Constructor.
|
||||
* This should generally only be used internally to NetworkTables.
|
||||
* @param inst Instance
|
||||
* @param listener Listener that was triggered
|
||||
* @param connected Connected if true
|
||||
* @param conn Connection information
|
||||
*/
|
||||
public ConnectionNotification(NetworkTableInstance inst, int listener, boolean connected, ConnectionInfo conn) {
|
||||
this.inst = inst;
|
||||
this.listener = listener;
|
||||
this.connected = connected;
|
||||
this.conn = conn;
|
||||
}
|
||||
|
||||
private final NetworkTableInstance inst;
|
||||
|
||||
public NetworkTableInstance getInstance() {
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
63
src/main/java/edu/wpi/first/networktables/EntryInfo.java
Normal file
63
src/main/java/edu/wpi/first/networktables/EntryInfo.java
Normal file
@@ -0,0 +1,63 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* NetworkTables Entry information.
|
||||
*/
|
||||
public final class EntryInfo {
|
||||
/** Entry handle. */
|
||||
public final int entry;
|
||||
|
||||
/** Entry name. */
|
||||
public final String name;
|
||||
|
||||
/** Entry type. */
|
||||
public final NetworkTableType type;
|
||||
|
||||
/** Entry flags. */
|
||||
public final int flags;
|
||||
|
||||
/** Timestamp of last change to entry (type or value). */
|
||||
public final long last_change;
|
||||
|
||||
/** Constructor.
|
||||
* This should generally only be used internally to NetworkTables.
|
||||
* @param inst Instance
|
||||
* @param entry Entry handle
|
||||
* @param name Name
|
||||
* @param type Type (integer version of {@link NetworkTableType})
|
||||
* @param flags Flags
|
||||
* @param last_change Timestamp of last change
|
||||
*/
|
||||
public EntryInfo(NetworkTableInstance inst, int entry, String name, int type, int flags, long last_change) {
|
||||
this.inst = inst;
|
||||
this.entry = entry;
|
||||
this.name = name;
|
||||
this.type = NetworkTableType.getFromInt(type);
|
||||
this.flags = flags;
|
||||
this.last_change = last_change;
|
||||
}
|
||||
|
||||
/* Network table instance. */
|
||||
private final NetworkTableInstance inst;
|
||||
|
||||
/* Cached entry object. */
|
||||
private NetworkTableEntry entryObject;
|
||||
|
||||
/**
|
||||
* Get the entry as an object.
|
||||
* @return NetworkTableEntry for this entry.
|
||||
*/
|
||||
NetworkTableEntry getEntry() {
|
||||
if (entryObject == null) {
|
||||
entryObject = new NetworkTableEntry(inst, entry);
|
||||
}
|
||||
return entryObject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* Flag values for use with entry listeners.
|
||||
*
|
||||
* The flags are a bitmask and must be OR'ed together to indicate the
|
||||
* combination of events desired to be received.
|
||||
*
|
||||
* The constants kNew, kDelete, kUpdate, and kFlags represent different events
|
||||
* that can occur to entries.
|
||||
*
|
||||
* By default, notifications are only generated for remote changes occurring
|
||||
* after the listener is created. The constants kImmediate and kLocal are
|
||||
* modifiers that cause notifications to be generated at other times.
|
||||
*/
|
||||
public interface EntryListenerFlags {
|
||||
/** Initial listener addition.
|
||||
* Set this flag to receive immediate notification of entries matching the
|
||||
* flag criteria (generally only useful when combined with kNew).
|
||||
*/
|
||||
public static final int kImmediate = 0x01;
|
||||
|
||||
/** Changed locally.
|
||||
* Set this flag to receive notification of both local changes and changes
|
||||
* coming from remote nodes. By default, notifications are only generated
|
||||
* for remote changes. Must be combined with some combination of kNew,
|
||||
* kDelete, kUpdate, and kFlags to receive notifications of those respective
|
||||
* events.
|
||||
*/
|
||||
public static final int kLocal = 0x02;
|
||||
|
||||
/** Newly created entry.
|
||||
* Set this flag to receive a notification when an entry is created.
|
||||
*/
|
||||
public static final int kNew = 0x04;
|
||||
|
||||
/** Entry was deleted.
|
||||
* Set this flag to receive a notification when an entry is deleted.
|
||||
*/
|
||||
public static final int kDelete = 0x08;
|
||||
|
||||
/** Entry's value changed.
|
||||
* Set this flag to receive a notification when an entry's value (or type)
|
||||
* changes.
|
||||
*/
|
||||
public static final int kUpdate = 0x10;
|
||||
|
||||
/** Entry's flags changed.
|
||||
* Set this flag to receive a notification when an entry's flags value
|
||||
* changes.
|
||||
*/
|
||||
public static final int kFlags = 0x20;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* NetworkTables Entry notification.
|
||||
*/
|
||||
public final class EntryNotification {
|
||||
/**
|
||||
* Listener that was triggered.
|
||||
*/
|
||||
public final int listener;
|
||||
|
||||
/**
|
||||
* Entry handle.
|
||||
*/
|
||||
public final int entry;
|
||||
|
||||
/**
|
||||
* Entry name.
|
||||
*/
|
||||
public final String name;
|
||||
|
||||
/**
|
||||
* The new value.
|
||||
*/
|
||||
public final NetworkTableValue value;
|
||||
|
||||
/**
|
||||
* Update flags. For example, {@link EntryListenerFlags#kNew} if the key did
|
||||
* not previously exist.
|
||||
*/
|
||||
public final int flags;
|
||||
|
||||
/** Constructor.
|
||||
* This should generally only be used internally to NetworkTables.
|
||||
* @param inst Instance
|
||||
* @param listener Listener that was triggered
|
||||
* @param entry Entry handle
|
||||
* @param name Entry name
|
||||
* @param value The new value
|
||||
* @param flags Update flags
|
||||
*/
|
||||
public EntryNotification(NetworkTableInstance inst, int listener, int entry, String name, NetworkTableValue value, int flags) {
|
||||
this.inst = inst;
|
||||
this.listener = listener;
|
||||
this.entry = entry;
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
/* Network table instance. */
|
||||
private final NetworkTableInstance inst;
|
||||
|
||||
/* Cached entry object. */
|
||||
NetworkTableEntry entryObject;
|
||||
|
||||
/**
|
||||
* Get the entry as an object.
|
||||
* @return NetworkTableEntry for this entry.
|
||||
*/
|
||||
public NetworkTableEntry getEntry() {
|
||||
if (entryObject == null) {
|
||||
entryObject = new NetworkTableEntry(inst, entry);
|
||||
}
|
||||
return entryObject;
|
||||
}
|
||||
}
|
||||
75
src/main/java/edu/wpi/first/networktables/LogMessage.java
Normal file
75
src/main/java/edu/wpi/first/networktables/LogMessage.java
Normal file
@@ -0,0 +1,75 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* NetworkTables log message.
|
||||
*/
|
||||
public final class LogMessage {
|
||||
/**
|
||||
* Logging levels.
|
||||
*/
|
||||
public static final int kCritical = 50;
|
||||
public static final int kError = 40;
|
||||
public static final int kWarning = 30;
|
||||
public static final int kInfo = 20;
|
||||
public static final int kDebug = 10;
|
||||
public static final int kDebug1 = 9;
|
||||
public static final int kDebug2 = 8;
|
||||
public static final int kDebug3 = 7;
|
||||
public static final int kDebug4 = 6;
|
||||
|
||||
/**
|
||||
* The logger that generated the message.
|
||||
*/
|
||||
public final int logger;
|
||||
|
||||
/**
|
||||
* Log level of the message.
|
||||
*/
|
||||
public final int level;
|
||||
|
||||
/**
|
||||
* The filename of the source file that generated the message.
|
||||
*/
|
||||
public final String filename;
|
||||
|
||||
/**
|
||||
* The line number in the source file that generated the message.
|
||||
*/
|
||||
public final int line;
|
||||
|
||||
/**
|
||||
* The message.
|
||||
*/
|
||||
public final String message;
|
||||
|
||||
/** Constructor.
|
||||
* This should generally only be used internally to NetworkTables.
|
||||
* @param inst Instance
|
||||
* @param logger Logger
|
||||
* @param level Log level
|
||||
* @param filename Filename
|
||||
* @param line Line number
|
||||
* @param message Message
|
||||
*/
|
||||
public LogMessage(NetworkTableInstance inst, int logger, int level, String filename, int line, String message) {
|
||||
this.inst = inst;
|
||||
this.logger = logger;
|
||||
this.level = level;
|
||||
this.filename = filename;
|
||||
this.line = line;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
private final NetworkTableInstance inst;
|
||||
|
||||
NetworkTableInstance getInstance() {
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
283
src/main/java/edu/wpi/first/networktables/NetworkTable.java
Normal file
283
src/main/java/edu/wpi/first/networktables/NetworkTable.java
Normal file
@@ -0,0 +1,283 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A network table that knows its subtable path.
|
||||
*/
|
||||
public final class NetworkTable {
|
||||
/**
|
||||
* The path separator for sub-tables and keys
|
||||
*
|
||||
*/
|
||||
public static final char PATH_SEPARATOR = '/';
|
||||
|
||||
private final String path;
|
||||
private final String pathWithSep;
|
||||
private final NetworkTableInstance inst;
|
||||
|
||||
public NetworkTable(NetworkTableInstance inst, String path) {
|
||||
this.path = path;
|
||||
this.pathWithSep = path + PATH_SEPARATOR;
|
||||
this.inst = inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instance for the table.
|
||||
* @return Instance
|
||||
*/
|
||||
public NetworkTableInstance getInstance() { return inst; }
|
||||
|
||||
public String toString() { return "NetworkTable: " + path; }
|
||||
|
||||
private final ConcurrentMap<String, NetworkTableEntry> entries = new ConcurrentHashMap<String, NetworkTableEntry>();
|
||||
|
||||
/**
|
||||
* Gets the entry for a subkey.
|
||||
* @param key the key name
|
||||
* @return Network table entry.
|
||||
*/
|
||||
public NetworkTableEntry getEntry(String key) {
|
||||
NetworkTableEntry entry = entries.get(key);
|
||||
if (entry == null) {
|
||||
entry = inst.getEntry(pathWithSep + key);
|
||||
entries.putIfAbsent(key, entry);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to keys only within this table.
|
||||
* @param listener listener to add
|
||||
* @param flags {@link EntryListenerFlags} bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addEntryListener(TableEntryListener listener, int flags) {
|
||||
final int prefixLen = path.length() + 1;
|
||||
return inst.addEntryListener(pathWithSep, (event) -> {
|
||||
String relativeKey = event.name.substring(prefixLen);
|
||||
if (relativeKey.indexOf(PATH_SEPARATOR) != -1) // part of a subtable
|
||||
return;
|
||||
listener.valueChanged(this, relativeKey, event.getEntry(), event.value, event.flags);
|
||||
}, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to a single key.
|
||||
* @param key the key name
|
||||
* @param listener listener to add
|
||||
* @param flags {@link EntryListenerFlags} bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addEntryListener(String key, TableEntryListener listener, int flags) {
|
||||
final NetworkTableEntry entry = getEntry(key);
|
||||
return inst.addEntryListener(entry, (event) -> {
|
||||
listener.valueChanged(this, key, entry, event.value, event.flags);
|
||||
}, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an entry listener.
|
||||
* @param listener listener handle
|
||||
*/
|
||||
public void removeEntryListener(int listener) {
|
||||
inst.removeEntryListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for sub-table creation.
|
||||
* This calls the listener once for each newly created sub-table.
|
||||
* It immediately calls the listener for any existing sub-tables.
|
||||
* @param listener listener to add
|
||||
* @param localNotify notify local changes as well as remote
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addSubTableListener(TableListener listener, boolean localNotify) {
|
||||
int flags = EntryListenerFlags.kNew | EntryListenerFlags.kImmediate;
|
||||
if (localNotify)
|
||||
flags |= EntryListenerFlags.kLocal;
|
||||
|
||||
final int prefixLen = path.length() + 1;
|
||||
final NetworkTable parent = this;
|
||||
|
||||
return inst.addEntryListener(pathWithSep, new Consumer<EntryNotification>() {
|
||||
final Set<String> notifiedTables = new HashSet<String>();
|
||||
|
||||
@Override
|
||||
public void accept(EntryNotification event) {
|
||||
String relativeKey = event.name.substring(prefixLen);
|
||||
int endSubTable = relativeKey.indexOf(PATH_SEPARATOR);
|
||||
if (endSubTable == -1)
|
||||
return;
|
||||
String subTableKey = relativeKey.substring(0, endSubTable);
|
||||
if (notifiedTables.contains(subTableKey))
|
||||
return;
|
||||
notifiedTables.add(subTableKey);
|
||||
listener.tableCreated(parent, subTableKey, parent.getSubTable(subTableKey));
|
||||
}
|
||||
}, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a sub-table listener.
|
||||
* @param listener listener handle
|
||||
*/
|
||||
public void removeTableListener(int listener) {
|
||||
inst.removeEntryListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the table at the specified key. If there is no table at the
|
||||
* specified key, it will create a new table
|
||||
*
|
||||
* @param key the name of the table relative to this one
|
||||
* @return a sub table relative to this one
|
||||
*/
|
||||
public NetworkTable getSubTable(String key) {
|
||||
return new NetworkTable(inst, pathWithSep + key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the table and tells if it contains the specified key
|
||||
*
|
||||
* @param key the key to search for
|
||||
* @return true if the table as a value assigned to the given key
|
||||
*/
|
||||
public boolean containsKey(String key) {
|
||||
return getEntry(key).exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key the key to search for
|
||||
* @return true if there is a subtable with the key which contains at least
|
||||
* one key/subtable of its own
|
||||
*/
|
||||
public boolean containsSubTable(String key) {
|
||||
int[] handles = NetworkTablesJNI.getEntries(inst.getHandle(), pathWithSep + key + PATH_SEPARATOR, 0);
|
||||
return handles.length != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all keys in the table (not including sub-tables).
|
||||
* @param types bitmask of types; 0 is treated as a "don't care".
|
||||
* @return keys currently in the table
|
||||
*/
|
||||
public Set<String> getKeys(int types) {
|
||||
Set<String> keys = new HashSet<String>();
|
||||
int prefixLen = path.length() + 1;
|
||||
for (EntryInfo info : inst.getEntryInfo(pathWithSep, types)) {
|
||||
String relativeKey = info.name.substring(prefixLen);
|
||||
if (relativeKey.indexOf(PATH_SEPARATOR) != -1)
|
||||
continue;
|
||||
keys.add(relativeKey);
|
||||
// populate entries as we go
|
||||
if (entries.get(relativeKey) == null) {
|
||||
entries.putIfAbsent(relativeKey, new NetworkTableEntry(inst, info.entry));
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all keys in the table (not including sub-tables).
|
||||
* @return keys currently in the table
|
||||
*/
|
||||
public Set<String> getKeys() {
|
||||
return getKeys(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the names of all subtables in the table.
|
||||
* @return subtables currently in the table
|
||||
*/
|
||||
public Set<String> getSubTables() {
|
||||
Set<String> keys = new HashSet<String>();
|
||||
int prefixLen = path.length() + 1;
|
||||
for (EntryInfo info : inst.getEntryInfo(pathWithSep, 0)) {
|
||||
String relativeKey = info.name.substring(prefixLen);
|
||||
int endSubTable = relativeKey.indexOf(PATH_SEPARATOR);
|
||||
if (endSubTable == -1)
|
||||
continue;
|
||||
keys.add(relativeKey.substring(0, endSubTable));
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the specified key in this table. The key can
|
||||
* not be null.
|
||||
*
|
||||
* @param key the key name
|
||||
*/
|
||||
public void delete(String key) {
|
||||
getEntry(key).delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value in the table
|
||||
*
|
||||
* @param key the key to be assigned to
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
boolean putValue(String key, NetworkTableValue value) {
|
||||
return getEntry(key).setValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
boolean setDefaultValue(String key, NetworkTableValue defaultValue) {
|
||||
return getEntry(key).setDefaultValue(defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value associated with a key as an object
|
||||
*
|
||||
* @param key the key of the value to look up
|
||||
* @return the value associated with the given key, or nullptr if the key
|
||||
* does not exist
|
||||
*/
|
||||
NetworkTableValue getValue(String key) {
|
||||
return getEntry(key).getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof NetworkTable)) {
|
||||
return false;
|
||||
}
|
||||
NetworkTable other = (NetworkTable) o;
|
||||
return inst.equals(other.inst) && path.equals(other.path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(inst, path);
|
||||
}
|
||||
}
|
||||
709
src/main/java/edu/wpi/first/networktables/NetworkTableEntry.java
Normal file
709
src/main/java/edu/wpi/first/networktables/NetworkTableEntry.java
Normal file
@@ -0,0 +1,709 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* NetworkTables Entry
|
||||
*/
|
||||
public final class NetworkTableEntry {
|
||||
/**
|
||||
* Flag values (as returned by {@link #getFlags()}).
|
||||
*/
|
||||
public static final int kPersistent = 0x01;
|
||||
|
||||
/**
|
||||
* Construct from native handle.
|
||||
* @param inst Instance
|
||||
* @param handle Native handle
|
||||
*/
|
||||
public NetworkTableEntry(NetworkTableInstance inst, int handle) {
|
||||
m_inst = inst;
|
||||
m_handle = handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native handle for the entry.
|
||||
* @return Native handle
|
||||
*/
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instance for the entry.
|
||||
* @return Instance
|
||||
*/
|
||||
public NetworkTableInstance getInstance() {
|
||||
return m_inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the entry currently exists.
|
||||
* @return True if the entry exists, false otherwise.
|
||||
*/
|
||||
public boolean exists() {
|
||||
return NetworkTablesJNI.getType(m_handle) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the entry (the key).
|
||||
* @return the entry's name
|
||||
*/
|
||||
public String getName() {
|
||||
return NetworkTablesJNI.getEntryName(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the entry.
|
||||
* @return the entry's type
|
||||
*/
|
||||
public NetworkTableType getType() {
|
||||
return NetworkTableType.getFromInt(NetworkTablesJNI.getType(m_handle));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the flags.
|
||||
* @return the flags (bitmask)
|
||||
*/
|
||||
public int getFlags() {
|
||||
return NetworkTablesJNI.getEntryFlags(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last time the entry's value was changed.
|
||||
* @return Entry last change time
|
||||
*/
|
||||
public long getLastChange() {
|
||||
return NetworkTablesJNI.getEntryLastChange(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets combined information about the entry.
|
||||
* @return Entry information
|
||||
*/
|
||||
public EntryInfo getInfo() {
|
||||
return NetworkTablesJNI.getEntryInfoHandle(m_inst, m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entry's value.
|
||||
* Returns a value with type NetworkTableType.kUnassigned if the value
|
||||
* does not exist.
|
||||
* @return the entry's value
|
||||
*/
|
||||
public NetworkTableValue getValue() {
|
||||
return NetworkTablesJNI.getValue(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a boolean. If the entry does not exist or is of
|
||||
* different type, it will return the default value.
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
public boolean getBoolean(boolean defaultValue) {
|
||||
return NetworkTablesJNI.getBoolean(m_handle, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a double. If the entry does not exist or is of
|
||||
* different type, it will return the default value.
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
public double getDouble(double defaultValue) {
|
||||
return NetworkTablesJNI.getDouble(m_handle, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a double. If the entry does not exist or is of
|
||||
* different type, it will return the default value.
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
public Number getNumber(Number defaultValue) {
|
||||
return NetworkTablesJNI.getDouble(m_handle, defaultValue.doubleValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a string. If the entry does not exist or is of
|
||||
* different type, it will return the default value.
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
public String getString(String defaultValue) {
|
||||
return NetworkTablesJNI.getString(m_handle, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a raw value (byte array). If the entry does not
|
||||
* exist or is of different type, it will return the default value.
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
public byte[] getRaw(byte[] defaultValue) {
|
||||
return NetworkTablesJNI.getRaw(m_handle, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a boolean array. If the entry does not exist
|
||||
* or is of different type, it will return the default value.
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
public boolean[] getBooleanArray(boolean[] defaultValue) {
|
||||
return NetworkTablesJNI.getBooleanArray(m_handle, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a boolean array. If the entry does not exist
|
||||
* or is of different type, it will return the default value.
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
public Boolean[] getBooleanArray(Boolean[] defaultValue) {
|
||||
return NetworkTableValue.fromNative(NetworkTablesJNI.getBooleanArray(m_handle, NetworkTableValue.toNative(defaultValue)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a double array. If the entry does not exist
|
||||
* or is of different type, it will return the default value.
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
public double[] getDoubleArray(double[] defaultValue) {
|
||||
return NetworkTablesJNI.getDoubleArray(m_handle, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a double array. If the entry does not exist
|
||||
* or is of different type, it will return the default value.
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
public Double[] getDoubleArray(Double[] defaultValue) {
|
||||
return NetworkTableValue.fromNative(NetworkTablesJNI.getDoubleArray(m_handle, NetworkTableValue.toNative(defaultValue)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a double array. If the entry does not exist
|
||||
* or is of different type, it will return the default value.
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
public Number[] getNumberArray(Number[] defaultValue) {
|
||||
return NetworkTableValue.fromNative(NetworkTablesJNI.getDoubleArray(m_handle, NetworkTableValue.toNative(defaultValue)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a string array. If the entry does not exist
|
||||
* or is of different type, it will return the default value.
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
public String[] getStringArray(String[] defaultValue) {
|
||||
return NetworkTablesJNI.getStringArray(m_handle, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setDefaultValue(NetworkTableValue defaultValue) {
|
||||
long time = defaultValue.getTime();
|
||||
Object o = defaultValue.getValue();
|
||||
switch (defaultValue.getType()) {
|
||||
case kBoolean:
|
||||
return NetworkTablesJNI.setDefaultBoolean(m_handle, time, ((Boolean)o).booleanValue());
|
||||
case kDouble:
|
||||
return NetworkTablesJNI.setDefaultDouble(m_handle, time, ((Number)o).doubleValue());
|
||||
case kString:
|
||||
return NetworkTablesJNI.setDefaultString(m_handle, time, (String)o);
|
||||
case kRaw:
|
||||
return NetworkTablesJNI.setDefaultRaw(m_handle, time, (byte[])o);
|
||||
case kBooleanArray:
|
||||
return NetworkTablesJNI.setDefaultBooleanArray(m_handle, time, (boolean[])o);
|
||||
case kDoubleArray:
|
||||
return NetworkTablesJNI.setDefaultDoubleArray(m_handle, time, (double[])o);
|
||||
case kStringArray:
|
||||
return NetworkTablesJNI.setDefaultStringArray(m_handle, time, (String[])o);
|
||||
case kRpc:
|
||||
// TODO
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setDefaultBoolean(boolean defaultValue) {
|
||||
return NetworkTablesJNI.setDefaultBoolean(m_handle, 0, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setDefaultDouble(double defaultValue) {
|
||||
return NetworkTablesJNI.setDefaultDouble(m_handle, 0, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setDefaultNumber(Number defaultValue) {
|
||||
return NetworkTablesJNI.setDefaultDouble(m_handle, 0, defaultValue.doubleValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setDefaultString(String defaultValue) {
|
||||
return NetworkTablesJNI.setDefaultString(m_handle, 0, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setDefaultRaw(byte[] defaultValue) {
|
||||
return NetworkTablesJNI.setDefaultRaw(m_handle, 0, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setDefaultBooleanArray(boolean[] defaultValue) {
|
||||
return NetworkTablesJNI.setDefaultBooleanArray(m_handle, 0, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setDefaultBooleanArray(Boolean[] defaultValue) {
|
||||
return NetworkTablesJNI.setDefaultBooleanArray(m_handle, 0, NetworkTableValue.toNative(defaultValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setDefaultDoubleArray(double[] defaultValue) {
|
||||
return NetworkTablesJNI.setDefaultDoubleArray(m_handle, 0, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setDefaultNumberArray(Number[] defaultValue) {
|
||||
return NetworkTablesJNI.setDefaultDoubleArray(m_handle, 0, NetworkTableValue.toNative(defaultValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setDefaultStringArray(String[] defaultValue) {
|
||||
return NetworkTablesJNI.setDefaultStringArray(m_handle, 0, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
public boolean setValue(NetworkTableValue value) {
|
||||
long time = value.getTime();
|
||||
Object o = value.getValue();
|
||||
switch (value.getType()) {
|
||||
case kBoolean:
|
||||
return NetworkTablesJNI.setBoolean(m_handle, time, ((Boolean)o).booleanValue(), false);
|
||||
case kDouble:
|
||||
return NetworkTablesJNI.setDouble(m_handle, time, ((Number)o).doubleValue(), false);
|
||||
case kString:
|
||||
return NetworkTablesJNI.setString(m_handle, time, (String)o, false);
|
||||
case kRaw:
|
||||
return NetworkTablesJNI.setRaw(m_handle, time, (byte[])o, false);
|
||||
case kBooleanArray:
|
||||
return NetworkTablesJNI.setBooleanArray(m_handle, time, (boolean[])o, false);
|
||||
case kDoubleArray:
|
||||
return NetworkTablesJNI.setDoubleArray(m_handle, time, (double[])o, false);
|
||||
case kStringArray:
|
||||
return NetworkTablesJNI.setStringArray(m_handle, time, (String[])o, false);
|
||||
case kRpc:
|
||||
// TODO
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setBoolean(boolean value) {
|
||||
return NetworkTablesJNI.setBoolean(m_handle, 0, value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setDouble(double value) {
|
||||
return NetworkTablesJNI.setDouble(m_handle, 0, value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setNumber(Number value) {
|
||||
return NetworkTablesJNI.setDouble(m_handle, 0, value.doubleValue(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setString(String value) {
|
||||
return NetworkTablesJNI.setString(m_handle, 0, value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setRaw(byte[] value) {
|
||||
return NetworkTablesJNI.setRaw(m_handle, 0, value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @param len the length of the value
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setRaw(ByteBuffer value, int len) {
|
||||
if (!value.isDirect())
|
||||
throw new IllegalArgumentException("must be a direct buffer");
|
||||
if (value.capacity() < len)
|
||||
throw new IllegalArgumentException("buffer is too small, must be at least " + len);
|
||||
return NetworkTablesJNI.setRaw(m_handle, 0, value, len, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setBooleanArray(boolean[] value) {
|
||||
return NetworkTablesJNI.setBooleanArray(m_handle, 0, value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setBooleanArray(Boolean[] value) {
|
||||
return NetworkTablesJNI.setBooleanArray(m_handle, 0, NetworkTableValue.toNative(value), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setDoubleArray(double[] value) {
|
||||
return NetworkTablesJNI.setDoubleArray(m_handle, 0, value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setNumberArray(Number[] value) {
|
||||
return NetworkTablesJNI.setDoubleArray(m_handle, 0, NetworkTableValue.toNative(value), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
public boolean setStringArray(String[] value) {
|
||||
return NetworkTablesJNI.setStringArray(m_handle, 0, value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
public void forceSetValue(NetworkTableValue value) {
|
||||
long time = value.getTime();
|
||||
Object o = value.getValue();
|
||||
switch (value.getType()) {
|
||||
case kBoolean:
|
||||
NetworkTablesJNI.setBoolean(m_handle, time, ((Boolean)o).booleanValue(), true);
|
||||
return;
|
||||
case kDouble:
|
||||
NetworkTablesJNI.setDouble(m_handle, time, ((Number)o).doubleValue(), true);
|
||||
return;
|
||||
case kString:
|
||||
NetworkTablesJNI.setString(m_handle, time, (String)o, true);
|
||||
return;
|
||||
case kRaw:
|
||||
NetworkTablesJNI.setRaw(m_handle, time, (byte[])o, true);
|
||||
return;
|
||||
case kBooleanArray:
|
||||
NetworkTablesJNI.setBooleanArray(m_handle, time, (boolean[])o, true);
|
||||
return;
|
||||
case kDoubleArray:
|
||||
NetworkTablesJNI.setDoubleArray(m_handle, time, (double[])o, true);
|
||||
return;
|
||||
case kStringArray:
|
||||
NetworkTablesJNI.setStringArray(m_handle, time, (String[])o, true);
|
||||
return;
|
||||
case kRpc:
|
||||
// TODO
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
public void forceSetBoolean(boolean value) {
|
||||
NetworkTablesJNI.setBoolean(m_handle, 0, value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
public void forceSetDouble(double value) {
|
||||
NetworkTablesJNI.setDouble(m_handle, 0, value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
public void forceSetNumber(Number value) {
|
||||
NetworkTablesJNI.setDouble(m_handle, 0, value.doubleValue(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
public void forceSetString(String value) {
|
||||
NetworkTablesJNI.setString(m_handle, 0, value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
public void forceSetRaw(byte[] value) {
|
||||
NetworkTablesJNI.setRaw(m_handle, 0, value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
public void forceSetBooleanArray(boolean[] value) {
|
||||
NetworkTablesJNI.setBooleanArray(m_handle, 0, value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
public void forceSetBooleanArray(Boolean[] value) {
|
||||
NetworkTablesJNI.setBooleanArray(m_handle, 0, NetworkTableValue.toNative(value), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
public void forceSetDoubleArray(double[] value) {
|
||||
NetworkTablesJNI.setDoubleArray(m_handle, 0, value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
public void forceSetNumberArray(Number[] value) {
|
||||
NetworkTablesJNI.setDoubleArray(m_handle, 0, NetworkTableValue.toNative(value), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
public void forceSetStringArray(String[] value) {
|
||||
NetworkTablesJNI.setStringArray(m_handle, 0, value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets flags.
|
||||
* @param flags the flags to set (bitmask)
|
||||
*/
|
||||
public void setFlags(int flags) {
|
||||
NetworkTablesJNI.setEntryFlags(m_handle, getFlags() | flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears flags.
|
||||
* @param flags the flags to clear (bitmask)
|
||||
*/
|
||||
public void clearFlags(int flags) {
|
||||
NetworkTablesJNI.setEntryFlags(m_handle, getFlags() & ~flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make value persistent through program restarts.
|
||||
*/
|
||||
public void setPersistent() {
|
||||
setFlags(kPersistent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop making value persistent through program restarts.
|
||||
*/
|
||||
public void clearPersistent() {
|
||||
clearFlags(kPersistent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the value is persistent through program restarts.
|
||||
* @return True if the value is persistent.
|
||||
*/
|
||||
public boolean isPersistent() {
|
||||
return (getFlags() & kPersistent) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the entry.
|
||||
*/
|
||||
public void delete() {
|
||||
NetworkTablesJNI.deleteEntry(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a callback-based RPC entry point. Only valid to use on the server.
|
||||
* The callback function will be called when the RPC is called.
|
||||
* This function creates RPC version 0 definitions (raw data in and out).
|
||||
* @param callback callback function
|
||||
*/
|
||||
void createRpc(Consumer<RpcAnswer> callback) {
|
||||
m_inst.createRpc(this, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a RPC function. May be used on either the client or server.
|
||||
* This function is non-blocking. Either {@link RpcCall#GetResult()} or
|
||||
* {@link RpcCall#CancelResult()} must be called on the return value to either
|
||||
* get or ignore the result of the call.
|
||||
* @param params parameter
|
||||
* @return RPC call object.
|
||||
*/
|
||||
RpcCall callRpc(byte[] params) {
|
||||
return new RpcCall(this, NetworkTablesJNI.callRpc(m_handle, params));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener for changes to the entry
|
||||
* @param listener the listener to add
|
||||
* @param flags bitmask specifying desired notifications
|
||||
* @return listener handle
|
||||
*/
|
||||
public int addListener(Consumer<EntryNotification> listener, int flags) {
|
||||
return m_inst.addEntryListener(this, listener, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a listener from receiving entry events
|
||||
* @param listener the listener to be removed
|
||||
*/
|
||||
public void removeListener(int listener) {
|
||||
m_inst.removeEntryListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof NetworkTableEntry)) {
|
||||
return false;
|
||||
}
|
||||
NetworkTableEntry other = (NetworkTableEntry) o;
|
||||
return m_handle == other.m_handle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
private NetworkTableInstance m_inst;
|
||||
private int m_handle;
|
||||
}
|
||||
1082
src/main/java/edu/wpi/first/networktables/NetworkTableInstance.java
Normal file
1082
src/main/java/edu/wpi/first/networktables/NetworkTableInstance.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* Network table data types.
|
||||
*/
|
||||
public enum NetworkTableType {
|
||||
kUnassigned(0),
|
||||
kBoolean(0x01),
|
||||
kDouble(0x02),
|
||||
kString(0x04),
|
||||
kRaw(0x08),
|
||||
kBooleanArray(0x10),
|
||||
kDoubleArray(0x20),
|
||||
kStringArray(0x40),
|
||||
kRpc(0x80);
|
||||
|
||||
private final int value;
|
||||
|
||||
private NetworkTableType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static NetworkTableType getFromInt(int value) {
|
||||
switch (value) {
|
||||
case 0x01: return kBoolean;
|
||||
case 0x02: return kDouble;
|
||||
case 0x04: return kString;
|
||||
case 0x08: return kRaw;
|
||||
case 0x10: return kBooleanArray;
|
||||
case 0x20: return kDoubleArray;
|
||||
case 0x40: return kStringArray;
|
||||
case 0x80: return kRpc;
|
||||
default: return kUnassigned;
|
||||
}
|
||||
}
|
||||
}
|
||||
472
src/main/java/edu/wpi/first/networktables/NetworkTableValue.java
Normal file
472
src/main/java/edu/wpi/first/networktables/NetworkTableValue.java
Normal file
@@ -0,0 +1,472 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A network table entry value.
|
||||
*/
|
||||
public final class NetworkTableValue {
|
||||
NetworkTableValue(NetworkTableType type, Object value, long time) {
|
||||
m_type = type;
|
||||
m_value = value;
|
||||
m_time = time;
|
||||
}
|
||||
|
||||
NetworkTableValue(NetworkTableType type, Object value) {
|
||||
this(type, value, NetworkTablesJNI.now());
|
||||
}
|
||||
|
||||
NetworkTableValue(int type, Object value, long time) {
|
||||
this(NetworkTableType.getFromInt(type), value, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data type.
|
||||
* @return The type.
|
||||
*/
|
||||
public NetworkTableType getType() {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data value stored.
|
||||
* @return The type.
|
||||
*/
|
||||
public Object getValue() {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the creation time of the value.
|
||||
* @return The time, in the units returned by NetworkTablesJNI.now().
|
||||
*/
|
||||
public long getTime() {
|
||||
return m_time;
|
||||
}
|
||||
|
||||
/*
|
||||
* Type Checkers
|
||||
*/
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a value or is unassigned.
|
||||
* @return True if the entry value contains a value.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_type != NetworkTableType.kUnassigned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a boolean.
|
||||
* @return True if the entry value is of boolean type.
|
||||
*/
|
||||
public boolean isBoolean() {
|
||||
return m_type == NetworkTableType.kBoolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a double.
|
||||
* @return True if the entry value is of double type.
|
||||
*/
|
||||
public boolean isDouble() {
|
||||
return m_type == NetworkTableType.kDouble;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a string.
|
||||
* @return True if the entry value is of string type.
|
||||
*/
|
||||
public boolean isString() {
|
||||
return m_type == NetworkTableType.kString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a raw.
|
||||
* @return True if the entry value is of raw type.
|
||||
*/
|
||||
public boolean isRaw() {
|
||||
return m_type == NetworkTableType.kRaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a rpc definition.
|
||||
* @return True if the entry value is of rpc definition type.
|
||||
*/
|
||||
public boolean isRpc() {
|
||||
return m_type == NetworkTableType.kRpc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a boolean array.
|
||||
* @return True if the entry value is of boolean array type.
|
||||
*/
|
||||
public boolean isBooleanArray() {
|
||||
return m_type == NetworkTableType.kBooleanArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a double array.
|
||||
* @return True if the entry value is of double array type.
|
||||
*/
|
||||
public boolean isDoubleArray() {
|
||||
return m_type == NetworkTableType.kDoubleArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a string array.
|
||||
* @return True if the entry value is of string array type.
|
||||
*/
|
||||
public boolean isStringArray() {
|
||||
return m_type == NetworkTableType.kStringArray;
|
||||
}
|
||||
|
||||
/*
|
||||
* Type-Safe Getters
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the entry's boolean value.
|
||||
* @throws ClassCastException if the entry value is not of boolean type.
|
||||
* @return The boolean value.
|
||||
*/
|
||||
public boolean getBoolean() {
|
||||
if (m_type != NetworkTableType.kBoolean) {
|
||||
throw new ClassCastException("cannot convert " + m_type + " to boolean");
|
||||
}
|
||||
return ((Boolean)m_value).booleanValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's double value.
|
||||
* @throws ClassCastException if the entry value is not of double type.
|
||||
* @return The double value.
|
||||
*/
|
||||
public double getDouble() {
|
||||
if (m_type != NetworkTableType.kDouble) {
|
||||
throw new ClassCastException("cannot convert " + m_type + " to double");
|
||||
}
|
||||
return ((Number)m_value).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's string value.
|
||||
* @throws ClassCastException if the entry value is not of string type.
|
||||
* @return The string value.
|
||||
*/
|
||||
public String getString() {
|
||||
if (m_type != NetworkTableType.kString) {
|
||||
throw new ClassCastException("cannot convert " + m_type + " to string");
|
||||
}
|
||||
return (String)m_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's raw value.
|
||||
* @throws ClassCastException if the entry value is not of raw type.
|
||||
* @return The raw value.
|
||||
*/
|
||||
public byte[] getRaw() {
|
||||
if (m_type != NetworkTableType.kRaw) {
|
||||
throw new ClassCastException("cannot convert " + m_type + " to raw");
|
||||
}
|
||||
return (byte[])m_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's rpc definition value.
|
||||
* @throws ClassCastException if the entry value is not of rpc definition type.
|
||||
* @return The rpc definition value.
|
||||
*/
|
||||
public byte[] getRpc() {
|
||||
if (m_type != NetworkTableType.kRpc) {
|
||||
throw new ClassCastException("cannot convert " + m_type + " to rpc");
|
||||
}
|
||||
return (byte[])m_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's boolean array value.
|
||||
* @throws ClassCastException if the entry value is not of boolean array type.
|
||||
* @return The boolean array value.
|
||||
*/
|
||||
public boolean[] getBooleanArray() {
|
||||
if (m_type != NetworkTableType.kBooleanArray) {
|
||||
throw new ClassCastException("cannot convert " + m_type + " to boolean array");
|
||||
}
|
||||
return (boolean[])m_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's double array value.
|
||||
* @throws ClassCastException if the entry value is not of double array type.
|
||||
* @return The double array value.
|
||||
*/
|
||||
public double[] getDoubleArray() {
|
||||
if (m_type != NetworkTableType.kDoubleArray) {
|
||||
throw new ClassCastException("cannot convert " + m_type + " to double array");
|
||||
}
|
||||
return (double[])m_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's string array value.
|
||||
* @throws ClassCastException if the entry value is not of string array type.
|
||||
* @return The string array value.
|
||||
*/
|
||||
public String[] getStringArray() {
|
||||
if (m_type != NetworkTableType.kStringArray) {
|
||||
throw new ClassCastException("cannot convert " + m_type + " to string array");
|
||||
}
|
||||
return (String[])m_value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Factory functions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a boolean entry value.
|
||||
* @param value the value
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeBoolean(boolean value) {
|
||||
return new NetworkTableValue(NetworkTableType.kBoolean, new Boolean(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boolean entry value.
|
||||
* @param value the value
|
||||
* @param time the creation time to use (instead of the current time)
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeBoolean(boolean value, long time) {
|
||||
return new NetworkTableValue(NetworkTableType.kBoolean, new Boolean(value), time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a double entry value.
|
||||
* @param value the value
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeDouble(double value) {
|
||||
return new NetworkTableValue(NetworkTableType.kDouble, new Double(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a double entry value.
|
||||
* @param value the value
|
||||
* @param time the creation time to use (instead of the current time)
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeDouble(double value, long time) {
|
||||
return new NetworkTableValue(NetworkTableType.kDouble, new Double(value), time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string entry value.
|
||||
* @param value the value
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeString(String value) {
|
||||
return new NetworkTableValue(NetworkTableType.kString, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string entry value.
|
||||
* @param value the value
|
||||
* @param time the creation time to use (instead of the current time)
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeString(String value, long time) {
|
||||
return new NetworkTableValue(NetworkTableType.kString, value, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a raw entry value.
|
||||
* @param value the value
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeRaw(byte[] value) {
|
||||
return new NetworkTableValue(NetworkTableType.kRaw, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a raw entry value.
|
||||
* @param value the value
|
||||
* @param time the creation time to use (instead of the current time)
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeRaw(byte[] value, long time) {
|
||||
return new NetworkTableValue(NetworkTableType.kRaw, value, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rpc entry value.
|
||||
* @param value the value
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeRpc(byte[] value) {
|
||||
return new NetworkTableValue(NetworkTableType.kRpc, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rpc entry value.
|
||||
* @param value the value
|
||||
* @param time the creation time to use (instead of the current time)
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeRpc(byte[] value, long time) {
|
||||
return new NetworkTableValue(NetworkTableType.kRpc, value, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boolean array entry value.
|
||||
* @param value the value
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeBooleanArray(boolean[] value) {
|
||||
return new NetworkTableValue(NetworkTableType.kBooleanArray, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boolean array entry value.
|
||||
* @param value the value
|
||||
* @param time the creation time to use (instead of the current time)
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeBooleanArray(boolean[] value, long time) {
|
||||
return new NetworkTableValue(NetworkTableType.kBooleanArray, value, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boolean array entry value.
|
||||
* @param value the value
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeBooleanArray(Boolean[] value) {
|
||||
return new NetworkTableValue(NetworkTableType.kBooleanArray, toNative(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boolean array entry value.
|
||||
* @param value the value
|
||||
* @param time the creation time to use (instead of the current time)
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeBooleanArray(Boolean[] value, long time) {
|
||||
return new NetworkTableValue(NetworkTableType.kBooleanArray, toNative(value), time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a double array entry value.
|
||||
* @param value the value
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeDoubleArray(double[] value) {
|
||||
return new NetworkTableValue(NetworkTableType.kDoubleArray, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a double array entry value.
|
||||
* @param value the value
|
||||
* @param time the creation time to use (instead of the current time)
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeDoubleArray(double[] value, long time) {
|
||||
return new NetworkTableValue(NetworkTableType.kDoubleArray, value, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a double array entry value.
|
||||
* @param value the value
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeDoubleArray(Number[] value) {
|
||||
return new NetworkTableValue(NetworkTableType.kDoubleArray, toNative(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a double array entry value.
|
||||
* @param value the value
|
||||
* @param time the creation time to use (instead of the current time)
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeDoubleArray(Number[] value, long time) {
|
||||
return new NetworkTableValue(NetworkTableType.kDoubleArray, toNative(value), time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string array entry value.
|
||||
* @param value the value
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeStringArray(String[] value) {
|
||||
return new NetworkTableValue(NetworkTableType.kStringArray, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string array entry value.
|
||||
* @param value the value
|
||||
* @param time the creation time to use (instead of the current time)
|
||||
* @return The entry value
|
||||
*/
|
||||
public static NetworkTableValue makeStringArray(String[] value, long time) {
|
||||
return new NetworkTableValue(NetworkTableType.kStringArray, value, time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof NetworkTableValue)) {
|
||||
return false;
|
||||
}
|
||||
NetworkTableValue other = (NetworkTableValue) o;
|
||||
return m_type == other.m_type && m_value.equals(other.m_value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(m_type, m_value);
|
||||
}
|
||||
|
||||
static boolean[] toNative(Boolean[] arr) {
|
||||
boolean[] out = new boolean[arr.length];
|
||||
for (int i = 0; i < arr.length; i++)
|
||||
out[i] = arr[i];
|
||||
return out;
|
||||
}
|
||||
|
||||
static double[] toNative(Number[] arr) {
|
||||
double[] out = new double[arr.length];
|
||||
for (int i = 0; i < arr.length; i++)
|
||||
out[i] = arr[i].doubleValue();
|
||||
return out;
|
||||
}
|
||||
|
||||
static Boolean[] fromNative(boolean[] arr) {
|
||||
Boolean[] out = new Boolean[arr.length];
|
||||
for (int i = 0; i < arr.length; i++)
|
||||
out[i] = arr[i];
|
||||
return out;
|
||||
}
|
||||
|
||||
static Double[] fromNative(double[] arr) {
|
||||
Double[] out = new Double[arr.length];
|
||||
for (int i = 0; i < arr.length; i++)
|
||||
out[i] = arr[i];
|
||||
return out;
|
||||
}
|
||||
|
||||
private NetworkTableType m_type;
|
||||
private Object m_value;
|
||||
private long m_time;
|
||||
}
|
||||
182
src/main/java/edu/wpi/first/networktables/NetworkTablesJNI.java
Normal file
182
src/main/java/edu/wpi/first/networktables/NetworkTablesJNI.java
Normal file
@@ -0,0 +1,182 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import edu.wpi.first.wpiutil.RuntimeDetector;
|
||||
|
||||
public final class NetworkTablesJNI {
|
||||
static boolean libraryLoaded = false;
|
||||
static File jniLibrary = null;
|
||||
static {
|
||||
if (!libraryLoaded) {
|
||||
try {
|
||||
System.loadLibrary("ntcore");
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
try {
|
||||
String resname = RuntimeDetector.getLibraryResource("ntcore");
|
||||
InputStream is = NetworkTablesJNI.class.getResourceAsStream(resname);
|
||||
if (is != null) {
|
||||
// create temporary file
|
||||
if (System.getProperty("os.name").startsWith("Windows"))
|
||||
jniLibrary = File.createTempFile("NetworkTablesJNI", ".dll");
|
||||
else if (System.getProperty("os.name").startsWith("Mac"))
|
||||
jniLibrary = File.createTempFile("libNetworkTablesJNI", ".dylib");
|
||||
else
|
||||
jniLibrary = File.createTempFile("libNetworkTablesJNI", ".so");
|
||||
// flag for delete on exit
|
||||
jniLibrary.deleteOnExit();
|
||||
OutputStream os = new FileOutputStream(jniLibrary);
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int readBytes;
|
||||
try {
|
||||
while ((readBytes = is.read(buffer)) != -1) {
|
||||
os.write(buffer, 0, readBytes);
|
||||
}
|
||||
} finally {
|
||||
os.close();
|
||||
is.close();
|
||||
}
|
||||
System.load(jniLibrary.getAbsolutePath());
|
||||
} else {
|
||||
System.loadLibrary("ntcore");
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
libraryLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static native int getDefaultInstance();
|
||||
public static native int createInstance();
|
||||
public static native void destroyInstance(int inst);
|
||||
public static native int getInstanceFromHandle(int handle);
|
||||
|
||||
public static native int getEntry(int inst, String key);
|
||||
public static native int[] getEntries(int inst, String prefix, int types);
|
||||
public static native String getEntryName(int entry);
|
||||
public static native long getEntryLastChange(int entry);
|
||||
|
||||
public static native int getType(int entry);
|
||||
|
||||
public static native boolean setBoolean(int entry, long time, boolean value, boolean force);
|
||||
public static native boolean setDouble(int entry, long time, double value, boolean force);
|
||||
public static native boolean setString(int entry, long time, String value, boolean force);
|
||||
public static native boolean setRaw(int entry, long time, byte[] value, boolean force);
|
||||
public static native boolean setRaw(int entry, long time, ByteBuffer value, int len, boolean force);
|
||||
public static native boolean setBooleanArray(int entry, long time, boolean[] value, boolean force);
|
||||
public static native boolean setDoubleArray(int entry, long time, double[] value, boolean force);
|
||||
public static native boolean setStringArray(int entry, long time, String[] value, boolean force);
|
||||
|
||||
public static native NetworkTableValue getValue(int entry);
|
||||
|
||||
public static native boolean getBoolean(int entry, boolean defaultValue);
|
||||
public static native double getDouble(int entry, double defaultValue);
|
||||
public static native String getString(int entry, String defaultValue);
|
||||
public static native byte[] getRaw(int entry, byte[] defaultValue);
|
||||
public static native boolean[] getBooleanArray(int entry, boolean[] defaultValue);
|
||||
public static native double[] getDoubleArray(int entry, double[] defaultValue);
|
||||
public static native String[] getStringArray(int entry, String[] defaultValue);
|
||||
public static native boolean setDefaultBoolean(int entry, long time, boolean defaultValue);
|
||||
|
||||
public static native boolean setDefaultDouble(int entry, long time, double defaultValue);
|
||||
public static native boolean setDefaultString(int entry, long time, String defaultValue);
|
||||
public static native boolean setDefaultRaw(int entry, long time, byte[] defaultValue);
|
||||
public static native boolean setDefaultBooleanArray(int entry, long time, boolean[] defaultValue);
|
||||
public static native boolean setDefaultDoubleArray(int entry, long time, double[] defaultValue);
|
||||
public static native boolean setDefaultStringArray(int entry, long time, String[] defaultValue);
|
||||
|
||||
public static native void setEntryFlags(int entry, int flags);
|
||||
public static native int getEntryFlags(int entry);
|
||||
|
||||
public static native void deleteEntry(int entry);
|
||||
|
||||
public static native void deleteAllEntries(int inst);
|
||||
|
||||
public static native EntryInfo getEntryInfoHandle(NetworkTableInstance inst, int entry);
|
||||
public static native EntryInfo[] getEntryInfo(NetworkTableInstance instObject, int inst, String prefix, int types);
|
||||
|
||||
public static native int createEntryListenerPoller(int inst);
|
||||
public static native void destroyEntryListenerPoller(int poller);
|
||||
public static native int addPolledEntryListener(int poller, String prefix, int flags);
|
||||
public static native int addPolledEntryListener(int poller, int entry, int flags);
|
||||
public static native EntryNotification[] pollEntryListener(NetworkTableInstance inst, int poller) throws InterruptedException;
|
||||
public static native EntryNotification[] pollEntryListenerTimeout(NetworkTableInstance inst, int poller, double timeout) throws InterruptedException;
|
||||
public static native void cancelPollEntryListener(int poller);
|
||||
public static native void removeEntryListener(int entryListener);
|
||||
public static native boolean waitForEntryListenerQueue(int inst, double timeout);
|
||||
|
||||
public static native int createConnectionListenerPoller(int inst);
|
||||
public static native void destroyConnectionListenerPoller(int poller);
|
||||
public static native int addPolledConnectionListener(int poller, boolean immediateNotify);
|
||||
public static native ConnectionNotification[] pollConnectionListener(NetworkTableInstance inst, int poller) throws InterruptedException;
|
||||
public static native ConnectionNotification[] pollConnectionListenerTimeout(NetworkTableInstance inst, int poller, double timeout) throws InterruptedException;
|
||||
public static native void cancelPollConnectionListener(int poller);
|
||||
public static native void removeConnectionListener(int connListener);
|
||||
public static native boolean waitForConnectionListenerQueue(int inst, double timeout);
|
||||
|
||||
public static native int createRpcCallPoller(int inst);
|
||||
public static native void destroyRpcCallPoller(int poller);
|
||||
public static native void createPolledRpc(int entry, byte[] def, int poller);
|
||||
public static native RpcAnswer[] pollRpc(NetworkTableInstance inst, int poller) throws InterruptedException;
|
||||
public static native RpcAnswer[] pollRpcTimeout(NetworkTableInstance inst, int poller, double timeout) throws InterruptedException;
|
||||
public static native void cancelPollRpc(int poller);
|
||||
public static native boolean waitForRpcCallQueue(int inst, double timeout);
|
||||
public static native void postRpcResponse(int entry, int call, byte[] result);
|
||||
public static native int callRpc(int entry, byte[] params);
|
||||
public static native byte[] getRpcResult(int entry, int call);
|
||||
public static native byte[] getRpcResult(int entry, int call, double timeout);
|
||||
public static native void cancelRpcResult(int entry, int call);
|
||||
|
||||
public static native byte[] getRpc(int entry, byte[] defaultValue);
|
||||
|
||||
public static native void setNetworkIdentity(int inst, String name);
|
||||
public static native int getNetworkMode(int inst);
|
||||
public static native void startServer(int inst, String persistFilename, String listenAddress, int port);
|
||||
public static native void stopServer(int inst);
|
||||
public static native void startClient(int inst);
|
||||
public static native void startClient(int inst, String serverName, int port);
|
||||
public static native void startClient(int inst, String[] serverNames, int[] ports);
|
||||
public static native void startClientTeam(int inst, int team, int port);
|
||||
public static native void stopClient(int inst);
|
||||
public static native void setServer(int inst, String serverName, int port);
|
||||
public static native void setServer(int inst, String[] serverNames, int[] ports);
|
||||
public static native void setServerTeam(int inst, int team, int port);
|
||||
public static native void startDSClient(int inst, int port);
|
||||
public static native void stopDSClient(int inst);
|
||||
public static native void setUpdateRate(int inst, double interval);
|
||||
|
||||
public static native void flush(int inst);
|
||||
|
||||
public static native ConnectionInfo[] getConnections(int inst);
|
||||
|
||||
public static native boolean isConnected(int inst);
|
||||
|
||||
public static native void savePersistent(int inst, String filename) throws PersistentException;
|
||||
public static native String[] loadPersistent(int inst, String filename) throws PersistentException; // returns warnings
|
||||
|
||||
public static native long now();
|
||||
|
||||
public static native int createLoggerPoller(int inst);
|
||||
public static native void destroyLoggerPoller(int poller);
|
||||
public static native int addPolledLogger(int poller, int minLevel, int maxLevel);
|
||||
public static native LogMessage[] pollLogger(NetworkTableInstance inst, int poller) throws InterruptedException;
|
||||
public static native LogMessage[] pollLoggerTimeout(NetworkTableInstance inst, int poller, double timeout) throws InterruptedException;
|
||||
public static native void cancelPollLogger(int poller);
|
||||
public static native void removeLogger(int logger);
|
||||
public static native boolean waitForLoggerQueue(int inst, double timeout);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An exception thrown when persistent load/save fails in a {@link NetworkTable}
|
||||
*
|
||||
*/
|
||||
public final class PersistentException extends IOException {
|
||||
|
||||
public static final long serialVersionUID = 0;
|
||||
|
||||
/**
|
||||
* @param message The error message
|
||||
*/
|
||||
public PersistentException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
91
src/main/java/edu/wpi/first/networktables/RpcAnswer.java
Normal file
91
src/main/java/edu/wpi/first/networktables/RpcAnswer.java
Normal file
@@ -0,0 +1,91 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* NetworkTables Remote Procedure Call (Server Side).
|
||||
*/
|
||||
public final class RpcAnswer {
|
||||
/** Entry handle. */
|
||||
public final int entry;
|
||||
|
||||
/** Call handle. */
|
||||
public int call;
|
||||
|
||||
/** Entry name. */
|
||||
public final String name;
|
||||
|
||||
/** Call raw parameters. */
|
||||
public final String params;
|
||||
|
||||
/** Connection that called the RPC. */
|
||||
public final ConnectionInfo conn;
|
||||
|
||||
/** Constructor.
|
||||
* This should generally only be used internally to NetworkTables.
|
||||
* @param inst Instance
|
||||
* @param entry Entry handle
|
||||
* @param call Call handle
|
||||
* @param name Entry name
|
||||
* @param params Call raw parameters
|
||||
* @param conn Connection info
|
||||
*/
|
||||
public RpcAnswer(NetworkTableInstance inst, int entry, int call, String name, String params, ConnectionInfo conn) {
|
||||
this.inst = inst;
|
||||
this.entry = entry;
|
||||
this.call = call;
|
||||
this.name = name;
|
||||
this.params = params;
|
||||
this.conn = conn;
|
||||
}
|
||||
|
||||
static final byte[] emptyResponse = new byte[] {};
|
||||
|
||||
/**
|
||||
* Posts an empty response if one was not previously sent.
|
||||
*/
|
||||
public synchronized void free() {
|
||||
if (call != 0) {
|
||||
postResponse(emptyResponse);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return call != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post RPC response (return value) for a polled RPC.
|
||||
* @param result result raw data that will be provided to remote caller
|
||||
*/
|
||||
public void postResponse(byte[] result) {
|
||||
NetworkTablesJNI.postRpcResponse(entry, call, result);
|
||||
call = 0;
|
||||
}
|
||||
|
||||
/* Network table instance. */
|
||||
private final NetworkTableInstance inst;
|
||||
|
||||
/* Cached entry object. */
|
||||
NetworkTableEntry entryObject;
|
||||
|
||||
/**
|
||||
* Get the entry as an object.
|
||||
* @return NetworkTableEntry for the RPC.
|
||||
*/
|
||||
NetworkTableEntry getEntry() {
|
||||
if (entryObject == null) {
|
||||
entryObject = new NetworkTableEntry(inst, entry);
|
||||
}
|
||||
return entryObject;
|
||||
}
|
||||
}
|
||||
93
src/main/java/edu/wpi/first/networktables/RpcCall.java
Normal file
93
src/main/java/edu/wpi/first/networktables/RpcCall.java
Normal file
@@ -0,0 +1,93 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* NetworkTables Remote Procedure Call.
|
||||
*/
|
||||
public final class RpcCall {
|
||||
/** Constructor.
|
||||
* This should generally only be used internally to NetworkTables.
|
||||
* @param entry Entry
|
||||
* @param call Call handle
|
||||
*/
|
||||
public RpcCall(NetworkTableEntry entry, int call) {
|
||||
m_entry = entry;
|
||||
m_call = call;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the result if no other action taken.
|
||||
*/
|
||||
public synchronized void free() {
|
||||
if (m_call != 0) {
|
||||
cancelResult();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_call != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the RPC entry.
|
||||
* @return NetworkTableEntry for the RPC.
|
||||
*/
|
||||
public NetworkTableEntry getEntry() {
|
||||
return m_entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the call native handle.
|
||||
* @return Native handle.
|
||||
*/
|
||||
public int getCall() {
|
||||
return m_call;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result (return value). This function blocks until
|
||||
* the result is received.
|
||||
* @return Received result (output)
|
||||
*/
|
||||
public byte[] getResult() {
|
||||
byte[] result = NetworkTablesJNI.getRpcResult(m_entry.getHandle(), m_call);
|
||||
if (result.length != 0) {
|
||||
m_call = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result (return value). This function blocks until
|
||||
* the result is received or it times out.
|
||||
* @param timeout timeout, in seconds
|
||||
* @return Received result (output)
|
||||
*/
|
||||
public byte[] getResult(double timeout) {
|
||||
byte[] result = NetworkTablesJNI.getRpcResult(m_entry.getHandle(), m_call, timeout);
|
||||
if (result.length != 0) {
|
||||
m_call = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore the result. This function is non-blocking.
|
||||
*/
|
||||
public void cancelResult() {
|
||||
NetworkTablesJNI.cancelRpcResult(m_entry.getHandle(), m_call);
|
||||
}
|
||||
|
||||
private final NetworkTableEntry m_entry;
|
||||
private int m_call;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* A listener that listens to changes in values in a {@link NetworkTable}
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface TableEntryListener extends EntryListenerFlags {
|
||||
/**
|
||||
* Called when a key-value pair is changed in a {@link NetworkTable}.
|
||||
*
|
||||
* @param table the table the key-value pair exists in
|
||||
* @param key the key associated with the value that changed
|
||||
* @param entry the entry associated with the value that changed
|
||||
* @param value the new value
|
||||
* @param flags update flags; for example, EntryListenerFlags.kNew if the key
|
||||
* did not previously exist in the table
|
||||
*/
|
||||
void valueChanged(NetworkTable table, String key, NetworkTableEntry entry, NetworkTableValue value, int flags);
|
||||
}
|
||||
23
src/main/java/edu/wpi/first/networktables/TableListener.java
Normal file
23
src/main/java/edu/wpi/first/networktables/TableListener.java
Normal file
@@ -0,0 +1,23 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* A listener that listens to new tables in a {@link NetworkTable}
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface TableListener {
|
||||
/**
|
||||
* Called when a new table is created within a {@link NetworkTable}.
|
||||
*
|
||||
* @param parent the parent of the table
|
||||
* @param name the name of the new table
|
||||
* @param table the new table
|
||||
*/
|
||||
void tableCreated(NetworkTable parent, String name, NetworkTable table);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package edu.wpi.first.wpilibj.networktables;
|
||||
|
||||
public class ConnectionInfo {
|
||||
public final String remote_id;
|
||||
public final String remote_ip;
|
||||
public final int remote_port;
|
||||
public final long last_update;
|
||||
public final int protocol_version;
|
||||
|
||||
public ConnectionInfo(String remote_id, String remote_ip, int remote_port, long last_update, int protocol_version) {
|
||||
this.remote_id = remote_id;
|
||||
this.remote_ip = remote_ip;
|
||||
this.remote_port = remote_port;
|
||||
this.last_update = last_update;
|
||||
this.protocol_version = protocol_version;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package edu.wpi.first.wpilibj.networktables;
|
||||
|
||||
public class EntryInfo {
|
||||
public final String name;
|
||||
public final int type;
|
||||
public final int flags;
|
||||
public final long last_change;
|
||||
|
||||
public EntryInfo(String name, int type, int flags, long last_change) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.flags = flags;
|
||||
this.last_change = last_change;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,159 +0,0 @@
|
||||
package edu.wpi.first.wpilibj.networktables;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import edu.wpi.first.wpiutil.RuntimeDetector;
|
||||
|
||||
public class NetworkTablesJNI {
|
||||
static boolean libraryLoaded = false;
|
||||
static File jniLibrary = null;
|
||||
static {
|
||||
if (!libraryLoaded) {
|
||||
try {
|
||||
System.loadLibrary("ntcore");
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
try {
|
||||
String resname = RuntimeDetector.getLibraryResource("ntcore");
|
||||
InputStream is = NetworkTablesJNI.class.getResourceAsStream(resname);
|
||||
if (is != null) {
|
||||
// create temporary file
|
||||
if (System.getProperty("os.name").startsWith("Windows"))
|
||||
jniLibrary = File.createTempFile("NetworkTablesJNI", ".dll");
|
||||
else if (System.getProperty("os.name").startsWith("Mac"))
|
||||
jniLibrary = File.createTempFile("libNetworkTablesJNI", ".dylib");
|
||||
else
|
||||
jniLibrary = File.createTempFile("libNetworkTablesJNI", ".so");
|
||||
// flag for delete on exit
|
||||
jniLibrary.deleteOnExit();
|
||||
OutputStream os = new FileOutputStream(jniLibrary);
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int readBytes;
|
||||
try {
|
||||
while ((readBytes = is.read(buffer)) != -1) {
|
||||
os.write(buffer, 0, readBytes);
|
||||
}
|
||||
} finally {
|
||||
os.close();
|
||||
is.close();
|
||||
}
|
||||
System.load(jniLibrary.getAbsolutePath());
|
||||
} else {
|
||||
System.loadLibrary("ntcore");
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
libraryLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static final int NT_NET_MODE_NONE = 0x00;
|
||||
public static final int NT_NET_MODE_SERVER = 0x01;
|
||||
public static final int NT_NET_MODE_CLIENT = 0x02;
|
||||
public static final int NT_NET_MODE_STARTING = 0x04;
|
||||
public static final int NT_NET_MODE_FAILURE = 0x08;
|
||||
|
||||
public static native boolean containsKey(String key);
|
||||
public static native int getType(String key);
|
||||
|
||||
public static native boolean putBoolean(String key, boolean value);
|
||||
public static native boolean putDouble(String key, double value);
|
||||
public static native boolean putString(String key, String value);
|
||||
public static native boolean putRaw(String key, byte[] value);
|
||||
public static native boolean putRaw(String key, ByteBuffer value, int len);
|
||||
public static native boolean putBooleanArray(String key, boolean[] value);
|
||||
public static native boolean putDoubleArray(String key, double[] value);
|
||||
public static native boolean putStringArray(String key, String[] value);
|
||||
|
||||
public static native void forcePutBoolean(String key, boolean value);
|
||||
public static native void forcePutDouble(String key, double value);
|
||||
public static native void forcePutString(String key, String value);
|
||||
public static native void forcePutRaw(String key, byte[] value);
|
||||
public static native void forcePutRaw(String key, ByteBuffer value, int len);
|
||||
public static native void forcePutBooleanArray(String key, boolean[] value);
|
||||
public static native void forcePutDoubleArray(String key, double[] value);
|
||||
public static native void forcePutStringArray(String key, String[] value);
|
||||
|
||||
public static native Object getValue(String key, Object defaultValue);
|
||||
public static native boolean getBoolean(String key, boolean defaultValue);
|
||||
public static native double getDouble(String key, double defaultValue);
|
||||
public static native String getString(String key, String defaultValue);
|
||||
public static native byte[] getRaw(String key, byte[] defaultValue);
|
||||
public static native boolean[] getBooleanArray(String key, boolean[] defaultValue);
|
||||
public static native double[] getDoubleArray(String key, double[] defaultValue);
|
||||
public static native String[] getStringArray(String key, String[] defaultValue);
|
||||
|
||||
public static native boolean setDefaultBoolean(String key, boolean defaultValue);
|
||||
public static native boolean setDefaultDouble(String key, double defaultValue);
|
||||
public static native boolean setDefaultString(String key, String defaultValue);
|
||||
public static native boolean setDefaultRaw(String key, byte[] defaultValue);
|
||||
public static native boolean setDefaultBooleanArray(String key, boolean[] defaultValue);
|
||||
public static native boolean setDefaultDoubleArray(String key, double[] defaultValue);
|
||||
public static native boolean setDefaultStringArray(String key, String[] defaultValue);
|
||||
|
||||
public static native void setEntryFlags(String key, int flags);
|
||||
public static native int getEntryFlags(String key);
|
||||
|
||||
public static native void deleteEntry(String key);
|
||||
public static native void deleteAllEntries();
|
||||
|
||||
public static native EntryInfo[] getEntries(String prefix, int types);
|
||||
|
||||
public static native void flush();
|
||||
|
||||
@FunctionalInterface
|
||||
public interface EntryListenerFunction {
|
||||
void apply(int uid, String key, Object value, int flags);
|
||||
}
|
||||
public static native int addEntryListener(String prefix, EntryListenerFunction listener, int flags);
|
||||
public static native void removeEntryListener(int entryListenerUid);
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ConnectionListenerFunction {
|
||||
void apply(int uid, boolean connected, ConnectionInfo conn);
|
||||
}
|
||||
public static native int addConnectionListener(ConnectionListenerFunction listener, boolean immediateNotify);
|
||||
public static native void removeConnectionListener(int connListenerUid);
|
||||
|
||||
// public static native void createRpc(String key, byte[] def, IRpc rpc);
|
||||
// public static native void createRpc(String key, ByteBuffer def, int def_len, IRpc rpc);
|
||||
public static native byte[] getRpc(String key, byte[] defaultValue);
|
||||
public static native int callRpc(String key, byte[] params);
|
||||
public static native int callRpc(String key, ByteBuffer params, int params_len);
|
||||
// public static native byte[] getRpcResultBlocking(int callUid);
|
||||
// public static native byte[] getRpcResultNonblocking(int callUid) throws RpcNoResponseException;
|
||||
|
||||
public static native void setNetworkIdentity(String name);
|
||||
public static native int getNetworkMode();
|
||||
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();
|
||||
|
||||
public static native void savePersistent(String filename) throws PersistentException;
|
||||
public static native String[] loadPersistent(String filename) throws PersistentException; // returns warnings
|
||||
|
||||
public static native long now();
|
||||
|
||||
@FunctionalInterface
|
||||
public interface LoggerFunction {
|
||||
void apply(int level, String file, int line, String msg);
|
||||
}
|
||||
public static native void setLogger(LoggerFunction func, int minLevel);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package edu.wpi.first.wpilibj.networktables;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An exception thrown when persistent load/save fails in a {@link NetworkTable}
|
||||
*
|
||||
*/
|
||||
public class PersistentException extends IOException {
|
||||
|
||||
/**
|
||||
* @param message The error message
|
||||
*/
|
||||
public PersistentException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,14 +3,13 @@ package edu.wpi.first.wpilibj.tables;
|
||||
|
||||
/**
|
||||
* Represents an object that has a remote connection
|
||||
*
|
||||
* @author Mitchell
|
||||
*
|
||||
* @deprecated Use {@link edu.wpi.first.networktables.NetworkTableInstance}.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface IRemote {
|
||||
/**
|
||||
* Register an object to listen for connection and disconnection events
|
||||
*
|
||||
*
|
||||
* @param listener the listener to be register
|
||||
* @param immediateNotify if the listener object should be notified of the current connection state
|
||||
*/
|
||||
@@ -18,17 +17,17 @@ public interface IRemote {
|
||||
|
||||
/**
|
||||
* Unregister a listener from connection events
|
||||
*
|
||||
*
|
||||
* @param listener the listener to be unregistered
|
||||
*/
|
||||
public void removeConnectionListener(IRemoteConnectionListener listener);
|
||||
|
||||
|
||||
/**
|
||||
* Get the current state of the objects connection
|
||||
* @return the current connection state
|
||||
*/
|
||||
public boolean isConnected();
|
||||
|
||||
|
||||
/**
|
||||
* If the object is acting as a server
|
||||
* @return if the object is a server
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package edu.wpi.first.wpilibj.tables;
|
||||
|
||||
import edu.wpi.first.wpilibj.networktables.ConnectionInfo;
|
||||
import edu.wpi.first.networktables.ConnectionInfo;
|
||||
|
||||
/**
|
||||
* A listener that listens for connection changes in a {@link IRemote} object
|
||||
*
|
||||
* @author Mitchell
|
||||
*
|
||||
* @deprecated Use Consumer<{@link edu.wpi.first.networktables.ConnectionNotification}>.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface IRemoteConnectionListener {
|
||||
/**
|
||||
* Called when an IRemote is connected
|
||||
|
||||
@@ -6,7 +6,9 @@ import java.util.Set;
|
||||
|
||||
/**
|
||||
* A table whose values can be read and written to
|
||||
* @deprecated Use {@link edu.wpi.first.networktables.NetworkTable}.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface ITable {
|
||||
|
||||
/**
|
||||
@@ -34,17 +36,20 @@ public interface ITable {
|
||||
public ITable getSubTable(String key);
|
||||
|
||||
/**
|
||||
* Gets all keys in the table (not including sub-tables).
|
||||
* @param types bitmask of types; 0 is treated as a "don't care".
|
||||
* @return keys currently in the table
|
||||
*/
|
||||
public Set<String> getKeys(int types);
|
||||
|
||||
/**
|
||||
* Gets all keys in the table (not including sub-tables).
|
||||
* @return keys currently in the table
|
||||
*/
|
||||
public Set<String> getKeys();
|
||||
|
||||
/**
|
||||
* Gets the names of all subtables in the table.
|
||||
* @return subtables currently in the table
|
||||
*/
|
||||
public Set<String> getSubTables();
|
||||
@@ -137,15 +142,15 @@ public interface ITable {
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
public boolean putNumber(String key, double value);
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doens't exist.
|
||||
* @return False if the table key exists with a different type
|
||||
*/
|
||||
public boolean setDefaultNumber(String key, double defaultValue);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number the key maps to. If the key does not exist or is of
|
||||
* different type, it will return the default value.
|
||||
@@ -163,15 +168,15 @@ public interface ITable {
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
public boolean putString(String key, String value);
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doens't exist.
|
||||
* @return False if the table key exists with a different type
|
||||
*/
|
||||
public boolean setDefaultString(String key, String defaultValue);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the string the key maps to. If the key does not exist or is of
|
||||
* different type, it will return the default value.
|
||||
@@ -189,15 +194,15 @@ public interface ITable {
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
public boolean putBoolean(String key, boolean value);
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doens't exist.
|
||||
* @return False if the table key exists with a different type
|
||||
*/
|
||||
public boolean setDefaultBoolean(String key, boolean defaultValue);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the boolean the key maps to. If the key does not exist or is of
|
||||
* different type, it will return the default value.
|
||||
@@ -215,15 +220,15 @@ public interface ITable {
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
public boolean putBooleanArray(String key, boolean[] value);
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doens't exist.
|
||||
* @return False if the table key exists with a different type
|
||||
*/
|
||||
public boolean setDefaultBooleanArray(String key, boolean[] defaultValue);
|
||||
|
||||
|
||||
/**
|
||||
* Put a boolean array in the table
|
||||
* @param key the key to be assigned to
|
||||
@@ -231,15 +236,15 @@ public interface ITable {
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
public boolean putBooleanArray(String key, Boolean[] value);
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doens't exist.
|
||||
* @return False if the table key exists with a different type
|
||||
*/
|
||||
public boolean setDefaultBooleanArray(String key, Boolean[] defaultValue);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the boolean array the key maps to. If the key does not exist or is
|
||||
* of different type, it will return the default value.
|
||||
@@ -266,15 +271,15 @@ public interface ITable {
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
public boolean putNumberArray(String key, double[] value);
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doens't exist.
|
||||
* @return False if the table key exists with a different type
|
||||
*/
|
||||
public boolean setDefaultNumberArray(String key, double[] defaultValue);
|
||||
|
||||
|
||||
/**
|
||||
* Put a number array in the table
|
||||
* @param key the key to be assigned to
|
||||
@@ -282,15 +287,15 @@ public interface ITable {
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
public boolean putNumberArray(String key, Double[] value);
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doens't exist.
|
||||
* @return False if the table key exists with a different type
|
||||
*/
|
||||
public boolean setDefaultNumberArray(String key, Double[] defaultValue);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number array the key maps to. If the key does not exist or is
|
||||
* of different type, it will return the default value.
|
||||
@@ -317,15 +322,15 @@ public interface ITable {
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
public boolean putStringArray(String key, String[] value);
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doens't exist.
|
||||
* @return False if the table key exists with a different type
|
||||
*/
|
||||
public boolean setDefaultStringArray(String key, String[] defaultValue);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the string array the key maps to. If the key does not exist or is
|
||||
* of different type, it will return the default value.
|
||||
@@ -343,15 +348,15 @@ public interface ITable {
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
public boolean putRaw(String key, byte[] value);
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
* @param key the key
|
||||
* @param defaultValue the default value to set if key doens't exist.
|
||||
* @return False if the table key exists with a different type
|
||||
*/
|
||||
public boolean setDefaultRaw(String key, byte[] defaultValue);
|
||||
|
||||
|
||||
/**
|
||||
* Put a raw value (bytes from a byte buffer) in the table
|
||||
* @param key the key to be assigned to
|
||||
@@ -467,7 +472,8 @@ public interface ITable {
|
||||
public double getDouble(String key, double defaultValue);
|
||||
|
||||
/**
|
||||
* Gets the full path of this table.
|
||||
* Gets the full path of this table. Does not include the trailing "/".
|
||||
* @return The path to this table (e.g. "", "/foo").
|
||||
*/
|
||||
public String getPath();
|
||||
|
||||
|
||||
@@ -2,11 +2,12 @@ package edu.wpi.first.wpilibj.tables;
|
||||
|
||||
/**
|
||||
* A listener that listens to changes in values in a {@link ITable}
|
||||
*
|
||||
* @author Mitchell
|
||||
*
|
||||
* @deprecated Use Consumer<{@link edu.wpi.first.networktables.EntryNotification}>,
|
||||
* {@link edu.wpi.first.networktables.TableEntryListener}, or
|
||||
* {@link edu.wpi.first.networktables.TableListener} as appropriate.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@Deprecated
|
||||
public interface ITableListener {
|
||||
/**
|
||||
* Called when a key-value pair is changed in a {@link ITable}
|
||||
|
||||
338
src/main/native/cpp/CallbackManager.h
Normal file
338
src/main/native/cpp/CallbackManager.h
Normal 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_
|
||||
29
src/main/native/cpp/ConnectionNotifier.cpp
Normal file
29
src/main/native/cpp/ConnectionNotifier.cpp
Normal 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);
|
||||
}
|
||||
73
src/main/native/cpp/ConnectionNotifier.h
Normal file
73
src/main/native/cpp/ConnectionNotifier.h
Normal 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_
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
89
src/main/native/cpp/EntryNotifier.cpp
Normal file
89
src/main/native/cpp/EntryNotifier.cpp
Normal 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);
|
||||
}
|
||||
108
src/main/native/cpp/EntryNotifier.h
Normal file
108
src/main/native/cpp/EntryNotifier.h
Normal 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_
|
||||
65
src/main/native/cpp/Handle.h
Normal file
65
src/main/native/cpp/Handle.h
Normal 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_
|
||||
29
src/main/native/cpp/IConnectionNotifier.h
Normal file
29
src/main/native/cpp/IConnectionNotifier.h
Normal 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_
|
||||
34
src/main/native/cpp/IDispatcher.h
Normal file
34
src/main/native/cpp/IDispatcher.h
Normal 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_
|
||||
31
src/main/native/cpp/IEntryNotifier.h
Normal file
31
src/main/native/cpp/IEntryNotifier.h
Normal 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_
|
||||
38
src/main/native/cpp/IRpcServer.h
Normal file
38
src/main/native/cpp/IRpcServer.h
Normal 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_
|
||||
64
src/main/native/cpp/IStorage.h
Normal file
64
src/main/native/cpp/IStorage.h
Normal 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_
|
||||
106
src/main/native/cpp/InstanceImpl.cpp
Normal file
106
src/main/native/cpp/InstanceImpl.cpp
Normal 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);
|
||||
}
|
||||
60
src/main/native/cpp/InstanceImpl.h
Normal file
60
src/main/native/cpp/InstanceImpl.h
Normal 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_
|
||||
@@ -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() {}
|
||||
@@ -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_
|
||||
|
||||
77
src/main/native/cpp/LoggerImpl.cpp
Normal file
77
src/main/native/cpp/LoggerImpl.cpp
Normal 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);
|
||||
}
|
||||
84
src/main/native/cpp/LoggerImpl.h
Normal file
84
src/main/native/cpp/LoggerImpl.h
Normal 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_
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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; }
|
||||
|
||||
16
src/main/native/cpp/networktables/NetworkTableEntry.cpp
Normal file
16
src/main/native/cpp/networktables/NetworkTableEntry.cpp
Normal 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)};
|
||||
}
|
||||
54
src/main/native/cpp/networktables/NetworkTableInstance.cpp
Normal file
54
src/main/native/cpp/networktables/NetworkTableInstance.cpp
Normal 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);
|
||||
}
|
||||
16
src/main/native/cpp/networktables/RpcCall.cpp
Normal file
16
src/main/native/cpp/networktables/RpcCall.cpp
Normal 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
@@ -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
|
||||
|
||||
73
src/main/native/include/networktables/EntryListenerFlags.h
Normal file
73
src/main/native/include/networktables/EntryListenerFlags.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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_ENTRYLISTENERFLAGS_H_
|
||||
#define NT_ENTRYLISTENERFLAGS_H_
|
||||
|
||||
#include "ntcore_c.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
namespace EntryListenerFlags {
|
||||
|
||||
/**
|
||||
* Flag values for use with entry listeners.
|
||||
*
|
||||
* The flags are a bitmask and must be OR'ed together to indicate the
|
||||
* combination of events desired to be received.
|
||||
*
|
||||
* The constants kNew, kDelete, kUpdate, and kFlags represent different events
|
||||
* that can occur to entries.
|
||||
*
|
||||
* By default, notifications are only generated for remote changes occurring
|
||||
* after the listener is created. The constants kImmediate and kLocal are
|
||||
* modifiers that cause notifications to be generated at other times.
|
||||
*/
|
||||
enum {
|
||||
/** Initial listener addition.
|
||||
* Set this flag to receive immediate notification of entries matching the
|
||||
* flag criteria (generally only useful when combined with kNew).
|
||||
*/
|
||||
kImmediate = NT_NOTIFY_IMMEDIATE,
|
||||
|
||||
/** Changed locally.
|
||||
* Set this flag to receive notification of both local changes and changes
|
||||
* coming from remote nodes. By default, notifications are only generated
|
||||
* for remote changes. Must be combined with some combination of kNew,
|
||||
* kDelete, kUpdate, and kFlags to receive notifications of those respective
|
||||
* events.
|
||||
*/
|
||||
kLocal = NT_NOTIFY_LOCAL,
|
||||
|
||||
/** Newly created entry.
|
||||
* Set this flag to receive a notification when an entry is created.
|
||||
*/
|
||||
kNew = NT_NOTIFY_NEW,
|
||||
|
||||
/** Entry was deleted.
|
||||
* Set this flag to receive a notification when an entry is deleted.
|
||||
*/
|
||||
kDelete = NT_NOTIFY_DELETE,
|
||||
|
||||
/** Entry's value changed.
|
||||
* Set this flag to receive a notification when an entry's value (or type)
|
||||
* changes.
|
||||
*/
|
||||
kUpdate = NT_NOTIFY_UPDATE,
|
||||
|
||||
/** Entry's flags changed.
|
||||
* Set this flag to receive a notification when an entry's flags value
|
||||
* changes.
|
||||
*/
|
||||
kFlags = NT_NOTIFY_FLAGS
|
||||
};
|
||||
|
||||
} // namespace EntryListenerFlags
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NT_ENTRYLISTENERFLAGS_H_
|
||||
@@ -12,20 +12,35 @@
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "llvm/StringMap.h"
|
||||
#include "networktables/NetworkTableEntry.h"
|
||||
#include "networktables/TableEntryListener.h"
|
||||
#include "networktables/TableListener.h"
|
||||
#include "ntcore_c.h"
|
||||
#include "tables/ITable.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
using llvm::ArrayRef;
|
||||
using llvm::StringRef;
|
||||
|
||||
class NetworkTableInstance;
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A network table that knows its subtable path.
|
||||
*/
|
||||
class NetworkTable : public ITable {
|
||||
class NetworkTable final : public ITable {
|
||||
private:
|
||||
struct private_init {};
|
||||
|
||||
NT_Inst m_inst;
|
||||
std::string m_path;
|
||||
std::mutex m_mutex;
|
||||
typedef std::pair<ITableListener*, unsigned int> Listener;
|
||||
mutable std::mutex m_mutex;
|
||||
mutable llvm::StringMap<NT_Entry> m_entries;
|
||||
typedef std::pair<ITableListener*, NT_EntryListener> Listener;
|
||||
std::vector<Listener> m_listeners;
|
||||
|
||||
static std::vector<std::string> s_ip_addresses;
|
||||
@@ -36,31 +51,48 @@ class NetworkTable : public ITable {
|
||||
static unsigned int s_port;
|
||||
|
||||
public:
|
||||
NetworkTable(llvm::StringRef path, const private_init&);
|
||||
NetworkTable(NT_Inst inst, StringRef path);
|
||||
virtual ~NetworkTable();
|
||||
|
||||
/**
|
||||
* Gets the instance for the table.
|
||||
* @return Instance
|
||||
*/
|
||||
NetworkTableInstance GetInstance() const;
|
||||
|
||||
/**
|
||||
* The path separator for sub-tables and keys
|
||||
*
|
||||
*/
|
||||
static const char PATH_SEPARATOR_CHAR;
|
||||
|
||||
/**
|
||||
* @throws IOException
|
||||
* Initializes network tables
|
||||
*/
|
||||
WPI_DEPRECATED(
|
||||
"use NetworkTableInstance::StartServer() or "
|
||||
"NetworkTableInstance::StartClient() instead")
|
||||
static void Initialize();
|
||||
|
||||
/**
|
||||
* Shuts down network tables
|
||||
*/
|
||||
WPI_DEPRECATED(
|
||||
"use NetworkTableInstance::StopServer() or "
|
||||
"NetworkTableInstance::StopClient() instead")
|
||||
static void Shutdown();
|
||||
|
||||
/**
|
||||
* set that network tables should be a client
|
||||
* This must be called before initialize or GetTable
|
||||
*/
|
||||
WPI_DEPRECATED("use NetworkTableInstance::StartClient() instead")
|
||||
static void SetClientMode();
|
||||
|
||||
/**
|
||||
* set that network tables should be a server
|
||||
* This must be called before initialize or GetTable
|
||||
*/
|
||||
WPI_DEPRECATED("use NetworkTableInstance::StartServer() instead")
|
||||
static void SetServerMode();
|
||||
|
||||
/**
|
||||
@@ -69,30 +101,48 @@ class NetworkTable : public ITable {
|
||||
* This must be called before initialize or GetTable
|
||||
* @param team the team number
|
||||
*/
|
||||
WPI_DEPRECATED(
|
||||
"use NetworkTableInstance::SetServerTeam() or "
|
||||
"NetworkTableInstance::StartClientTeam() instead")
|
||||
static void SetTeam(int team);
|
||||
|
||||
/**
|
||||
* @param address the adress that network tables will connect to in client
|
||||
* mode
|
||||
*/
|
||||
static void SetIPAddress(llvm::StringRef address);
|
||||
WPI_DEPRECATED(
|
||||
"use NetworkTableInstance::SetServer() or "
|
||||
"NetworkTableInstance::StartClient() instead")
|
||||
static void SetIPAddress(StringRef address);
|
||||
|
||||
/**
|
||||
* @param addresses the addresses that network tables will connect to in
|
||||
* client mode (in round robin order)
|
||||
*/
|
||||
static void SetIPAddress(llvm::ArrayRef<std::string> addresses);
|
||||
WPI_DEPRECATED(
|
||||
"use NetworkTableInstance::SetServer() or "
|
||||
"NetworkTableInstance::StartClient() instead")
|
||||
static void SetIPAddress(ArrayRef<std::string> addresses);
|
||||
|
||||
/**
|
||||
* @param port the port number that network tables will connect to in client
|
||||
* mode or listen to in server mode
|
||||
* Set the port number that network tables will connect to in client
|
||||
* mode or listen to in server mode.
|
||||
* @param port the port number
|
||||
*/
|
||||
WPI_DEPRECATED(
|
||||
"use the appropriate parameters to NetworkTableInstance::SetServer(), "
|
||||
"NetworkTableInstance::StartClient(), "
|
||||
"NetworkTableInstance::StartServer(), and "
|
||||
"NetworkTableInstance::StartDSClient() instead")
|
||||
static void SetPort(unsigned int port);
|
||||
|
||||
/**
|
||||
* @param enabled whether to enable the connection to the local DS to get
|
||||
* the robot IP address (defaults to enabled)
|
||||
* Enable requesting the server address from the Driver Station.
|
||||
* @param enabled whether to enable the connection to the local DS
|
||||
*/
|
||||
WPI_DEPRECATED(
|
||||
"use NetworkTableInstance::StartDSClient() and "
|
||||
"NetworkTableInstance::StopDSClient() instead")
|
||||
static void SetDSClientEnabled(bool enabled);
|
||||
|
||||
/**
|
||||
@@ -100,18 +150,23 @@ class NetworkTable : public ITable {
|
||||
* @param filename the filename that the network tables server uses for
|
||||
* automatic loading and saving of persistent values
|
||||
*/
|
||||
static void SetPersistentFilename(llvm::StringRef filename);
|
||||
WPI_DEPRECATED(
|
||||
"use the appropriate parameter to NetworkTableInstance::StartServer() "
|
||||
"instead")
|
||||
static void SetPersistentFilename(StringRef filename);
|
||||
|
||||
/**
|
||||
* Sets the network identity.
|
||||
* This is provided in the connection info on the remote end.
|
||||
* @param name identity
|
||||
*/
|
||||
static void SetNetworkIdentity(llvm::StringRef name);
|
||||
WPI_DEPRECATED("use NetworkTableInstance::SetNetworkIdentity() instead")
|
||||
static void SetNetworkIdentity(StringRef name);
|
||||
|
||||
/**
|
||||
* Deletes ALL keys in ALL subtables. Use with caution!
|
||||
*/
|
||||
WPI_DEPRECATED("use NetworkTableInstance::DeleteAllEntries() instead")
|
||||
static void GlobalDeleteAll();
|
||||
|
||||
/**
|
||||
@@ -120,13 +175,16 @@ class NetworkTable : public ITable {
|
||||
* This is primarily useful for synchronizing network updates with
|
||||
* user code.
|
||||
*/
|
||||
WPI_DEPRECATED("use NetworkTableInstance::Flush() instead")
|
||||
static void Flush();
|
||||
|
||||
/**
|
||||
* Set the periodic update rate.
|
||||
* Sets how frequently updates are sent to other nodes over the network.
|
||||
*
|
||||
* @param interval update interval in seconds (range 0.01 to 1.0)
|
||||
*/
|
||||
WPI_DEPRECATED("use NetworkTableInstance::SetUpdateRate() instead")
|
||||
static void SetUpdateRate(double interval);
|
||||
|
||||
/**
|
||||
@@ -135,7 +193,8 @@ class NetworkTable : public ITable {
|
||||
* @param filename file name
|
||||
* @return Error (or nullptr).
|
||||
*/
|
||||
static const char* SavePersistent(llvm::StringRef filename);
|
||||
WPI_DEPRECATED("use NetworkTableInstance::SavePersistent() instead")
|
||||
static const char* SavePersistent(StringRef filename);
|
||||
|
||||
/**
|
||||
* Loads persistent keys from a file. The server does this automatically.
|
||||
@@ -144,8 +203,9 @@ class NetworkTable : public ITable {
|
||||
* @param warn callback function called for warnings
|
||||
* @return Error (or nullptr).
|
||||
*/
|
||||
WPI_DEPRECATED("use NetworkTableInstance::LoadPersistent() instead")
|
||||
static const char* LoadPersistent(
|
||||
llvm::StringRef filename,
|
||||
StringRef filename,
|
||||
std::function<void(size_t line, const char* msg)> warn);
|
||||
|
||||
/**
|
||||
@@ -154,34 +214,103 @@ class NetworkTable : public ITable {
|
||||
* This will automatically initialize network tables if it has not been
|
||||
* already.
|
||||
*
|
||||
* @param key
|
||||
* the key name
|
||||
* @param key the key name
|
||||
* @return the network table requested
|
||||
*/
|
||||
static std::shared_ptr<NetworkTable> GetTable(llvm::StringRef key);
|
||||
WPI_DEPRECATED(
|
||||
"use NetworkTableInstance::GetTable() or "
|
||||
"NetworkTableInstance::GetEntry() instead")
|
||||
static std::shared_ptr<NetworkTable> GetTable(StringRef key);
|
||||
|
||||
/**
|
||||
* Gets the entry for a subkey.
|
||||
* @param key the key name
|
||||
* @return Network table entry.
|
||||
*/
|
||||
NetworkTableEntry GetEntry(StringRef key) const;
|
||||
|
||||
/**
|
||||
* Listen to keys only within this table.
|
||||
* @param listener listener to add
|
||||
* @param flags EntryListenerFlags bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_EntryListener AddEntryListener(TableEntryListener listener,
|
||||
unsigned int flags) const;
|
||||
|
||||
/**
|
||||
* Listen to a single key.
|
||||
* @param key the key name
|
||||
* @param listener listener to add
|
||||
* @param flags EntryListenerFlags bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_EntryListener AddEntryListener(StringRef key, TableEntryListener listener,
|
||||
unsigned int flags) const;
|
||||
|
||||
/**
|
||||
* Remove an entry listener.
|
||||
* @param listener listener handle
|
||||
*/
|
||||
void RemoveEntryListener(NT_EntryListener listener) const;
|
||||
|
||||
/**
|
||||
* Listen for sub-table creation.
|
||||
* This calls the listener once for each newly created sub-table.
|
||||
* It immediately calls the listener for any existing sub-tables.
|
||||
* @param listener listener to add
|
||||
* @param localNotify notify local changes as well as remote
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_EntryListener AddSubTableListener(TableListener listener,
|
||||
bool localNotify = false) const;
|
||||
|
||||
/**
|
||||
* Remove a sub-table listener.
|
||||
* @param listener listener handle
|
||||
*/
|
||||
void RemoveTableListener(NT_EntryListener listener) const;
|
||||
|
||||
WPI_DEPRECATED(
|
||||
"use AddEntryListener() instead with flags value of NT_NOTIFY_NEW | "
|
||||
"NT_NOTIFY_UPDATE")
|
||||
void AddTableListener(ITableListener* listener) override;
|
||||
|
||||
WPI_DEPRECATED(
|
||||
"use AddEntryListener() instead with flags value of NT_NOTIFY_NEW | "
|
||||
"NT_NOTIFY_UPDATE | NT_NOTIFY_IMMEDIATE")
|
||||
void AddTableListener(ITableListener* listener,
|
||||
bool immediateNotify) override;
|
||||
|
||||
WPI_DEPRECATED("use AddEntryListener() instead")
|
||||
void AddTableListenerEx(ITableListener* listener,
|
||||
unsigned int flags) override;
|
||||
void AddTableListener(llvm::StringRef key, ITableListener* listener,
|
||||
|
||||
WPI_DEPRECATED("use AddEntryListener() instead")
|
||||
void AddTableListener(StringRef key, ITableListener* listener,
|
||||
bool immediateNotify) override;
|
||||
void AddTableListenerEx(llvm::StringRef key, ITableListener* listener,
|
||||
|
||||
WPI_DEPRECATED("use AddEntryListener() instead")
|
||||
void AddTableListenerEx(StringRef key, ITableListener* listener,
|
||||
unsigned int flags) override;
|
||||
|
||||
WPI_DEPRECATED("use AddSubTableListener(TableListener, bool) instead")
|
||||
void AddSubTableListener(ITableListener* listener) override;
|
||||
|
||||
WPI_DEPRECATED("use AddSubTableListener(TableListener, bool) instead")
|
||||
void AddSubTableListener(ITableListener* listener, bool localNotify) override;
|
||||
|
||||
WPI_DEPRECATED("use RemoveTableListener(NT_EntryListener) instead")
|
||||
void RemoveTableListener(ITableListener* listener) override;
|
||||
|
||||
/**
|
||||
* Returns the table at the specified key. If there is no table at the
|
||||
* specified key, it will create a new table
|
||||
*
|
||||
* @param key
|
||||
* the key name
|
||||
* @param key the key name
|
||||
* @return the networktable to be returned
|
||||
*/
|
||||
std::shared_ptr<ITable> GetSubTable(llvm::StringRef key) const override;
|
||||
std::shared_ptr<NetworkTable> GetSubTable(StringRef key) const override;
|
||||
|
||||
/**
|
||||
* Determines whether the given key is in this table.
|
||||
@@ -189,7 +318,7 @@ class NetworkTable : public ITable {
|
||||
* @param key the key to search for
|
||||
* @return true if the table as a value assigned to the given key
|
||||
*/
|
||||
bool ContainsKey(llvm::StringRef key) const override;
|
||||
bool ContainsKey(StringRef key) const override;
|
||||
|
||||
/**
|
||||
* Determines whether there exists a non-empty subtable for this key
|
||||
@@ -199,15 +328,17 @@ class NetworkTable : public ITable {
|
||||
* @return true if there is a subtable with the key which contains at least
|
||||
* one key/subtable of its own
|
||||
*/
|
||||
bool ContainsSubTable(llvm::StringRef key) const override;
|
||||
bool ContainsSubTable(StringRef key) const override;
|
||||
|
||||
/**
|
||||
* Gets all keys in the table (not including sub-tables).
|
||||
* @param types bitmask of types; 0 is treated as a "don't care".
|
||||
* @return keys currently in the table
|
||||
*/
|
||||
std::vector<std::string> GetKeys(int types = 0) const override;
|
||||
|
||||
/**
|
||||
* Gets the names of all subtables in the table.
|
||||
* @return subtables currently in the table
|
||||
*/
|
||||
std::vector<std::string> GetSubTables() const override;
|
||||
@@ -217,7 +348,7 @@ class NetworkTable : public ITable {
|
||||
*
|
||||
* @param key the key to make persistent
|
||||
*/
|
||||
void SetPersistent(llvm::StringRef key) override;
|
||||
void SetPersistent(StringRef key) override;
|
||||
|
||||
/**
|
||||
* Stop making a key's value persistent through program restarts.
|
||||
@@ -225,7 +356,7 @@ class NetworkTable : public ITable {
|
||||
*
|
||||
* @param key the key name
|
||||
*/
|
||||
void ClearPersistent(llvm::StringRef key) override;
|
||||
void ClearPersistent(StringRef key) override;
|
||||
|
||||
/**
|
||||
* Returns whether the value is persistent through program restarts.
|
||||
@@ -233,7 +364,7 @@ class NetworkTable : public ITable {
|
||||
*
|
||||
* @param key the key name
|
||||
*/
|
||||
bool IsPersistent(llvm::StringRef key) const override;
|
||||
bool IsPersistent(StringRef key) const override;
|
||||
|
||||
/**
|
||||
* Sets flags on the specified key in this table. The key can
|
||||
@@ -242,7 +373,7 @@ class NetworkTable : public ITable {
|
||||
* @param key the key name
|
||||
* @param flags the flags to set (bitmask)
|
||||
*/
|
||||
void SetFlags(llvm::StringRef key, unsigned int flags) override;
|
||||
void SetFlags(StringRef key, unsigned int flags) override;
|
||||
|
||||
/**
|
||||
* Clears flags on the specified key in this table. The key can
|
||||
@@ -251,7 +382,7 @@ class NetworkTable : public ITable {
|
||||
* @param key the key name
|
||||
* @param flags the flags to clear (bitmask)
|
||||
*/
|
||||
void ClearFlags(llvm::StringRef key, unsigned int flags) override;
|
||||
void ClearFlags(StringRef key, unsigned int flags) override;
|
||||
|
||||
/**
|
||||
* Returns the flags for the specified key.
|
||||
@@ -259,14 +390,14 @@ class NetworkTable : public ITable {
|
||||
* @param key the key name
|
||||
* @return the flags, or 0 if the key is not defined
|
||||
*/
|
||||
unsigned int GetFlags(llvm::StringRef key) const override;
|
||||
unsigned int GetFlags(StringRef key) const override;
|
||||
|
||||
/**
|
||||
* Deletes the specified key in this table.
|
||||
*
|
||||
* @param key the key name
|
||||
*/
|
||||
void Delete(llvm::StringRef key) override;
|
||||
void Delete(StringRef key) override;
|
||||
|
||||
/**
|
||||
* Put a number in the table
|
||||
@@ -275,7 +406,7 @@ class NetworkTable : public ITable {
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
bool PutNumber(llvm::StringRef key, double value) override;
|
||||
bool PutNumber(StringRef key, double value) override;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
@@ -283,8 +414,7 @@ class NetworkTable : public ITable {
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
virtual bool SetDefaultNumber(llvm::StringRef key,
|
||||
double defaultValue) override;
|
||||
bool SetDefaultNumber(StringRef key, double defaultValue) override;
|
||||
|
||||
/**
|
||||
* Gets the number associated with the given name.
|
||||
@@ -294,8 +424,7 @@ class NetworkTable : public ITable {
|
||||
* @return the value associated with the given key or the given default value
|
||||
* if there is no value associated with the key
|
||||
*/
|
||||
virtual double GetNumber(llvm::StringRef key,
|
||||
double defaultValue) const override;
|
||||
double GetNumber(StringRef key, double defaultValue) const override;
|
||||
|
||||
/**
|
||||
* Put a string in the table
|
||||
@@ -304,7 +433,7 @@ class NetworkTable : public ITable {
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
virtual bool PutString(llvm::StringRef key, llvm::StringRef value) override;
|
||||
bool PutString(StringRef key, StringRef value) override;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
@@ -312,8 +441,7 @@ class NetworkTable : public ITable {
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
virtual bool SetDefaultString(llvm::StringRef key,
|
||||
llvm::StringRef defaultValue) override;
|
||||
bool SetDefaultString(StringRef key, StringRef defaultValue) override;
|
||||
|
||||
/**
|
||||
* Gets the string associated with the given name. If the key does not
|
||||
@@ -324,8 +452,7 @@ class NetworkTable : public ITable {
|
||||
* @return the value associated with the given key or the given default value
|
||||
* if there is no value associated with the key
|
||||
*/
|
||||
virtual std::string GetString(llvm::StringRef key,
|
||||
llvm::StringRef defaultValue) const override;
|
||||
std::string GetString(StringRef key, StringRef defaultValue) const override;
|
||||
|
||||
/**
|
||||
* Put a boolean in the table
|
||||
@@ -334,7 +461,7 @@ class NetworkTable : public ITable {
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
virtual bool PutBoolean(llvm::StringRef key, bool value) override;
|
||||
bool PutBoolean(StringRef key, bool value) override;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
@@ -342,8 +469,7 @@ class NetworkTable : public ITable {
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
virtual bool SetDefaultBoolean(llvm::StringRef key,
|
||||
bool defaultValue) override;
|
||||
bool SetDefaultBoolean(StringRef key, bool defaultValue) override;
|
||||
|
||||
/**
|
||||
* Gets the boolean associated with the given name. If the key does not
|
||||
@@ -354,8 +480,7 @@ class NetworkTable : public ITable {
|
||||
* @return the value associated with the given key or the given default value
|
||||
* if there is no value associated with the key
|
||||
*/
|
||||
virtual bool GetBoolean(llvm::StringRef key,
|
||||
bool defaultValue) const override;
|
||||
bool GetBoolean(StringRef key, bool defaultValue) const override;
|
||||
|
||||
/**
|
||||
* Put a boolean array in the table
|
||||
@@ -367,8 +492,7 @@ class NetworkTable : public ITable {
|
||||
* std::vector<bool> is special-cased in C++. 0 is false, any
|
||||
* non-zero value is true.
|
||||
*/
|
||||
virtual bool PutBooleanArray(llvm::StringRef key,
|
||||
llvm::ArrayRef<int> value) override;
|
||||
bool PutBooleanArray(StringRef key, ArrayRef<int> value) override;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
@@ -376,8 +500,8 @@ class NetworkTable : public ITable {
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
virtual bool SetDefaultBooleanArray(
|
||||
llvm::StringRef key, llvm::ArrayRef<int> defaultValue) override;
|
||||
bool SetDefaultBooleanArray(StringRef key,
|
||||
ArrayRef<int> defaultValue) override;
|
||||
|
||||
/**
|
||||
* Returns the boolean array the key maps to. If the key does not exist or is
|
||||
@@ -394,8 +518,8 @@ class NetworkTable : public ITable {
|
||||
* because std::vector<bool> is special-cased in C++. 0 is false, any
|
||||
* non-zero value is true.
|
||||
*/
|
||||
virtual std::vector<int> GetBooleanArray(
|
||||
llvm::StringRef key, llvm::ArrayRef<int> defaultValue) const override;
|
||||
std::vector<int> GetBooleanArray(StringRef key,
|
||||
ArrayRef<int> defaultValue) const override;
|
||||
|
||||
/**
|
||||
* Put a number array in the table
|
||||
@@ -403,8 +527,7 @@ class NetworkTable : public ITable {
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
virtual bool PutNumberArray(llvm::StringRef key,
|
||||
llvm::ArrayRef<double> value) override;
|
||||
bool PutNumberArray(StringRef key, ArrayRef<double> value) override;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
@@ -412,8 +535,8 @@ class NetworkTable : public ITable {
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
virtual bool SetDefaultNumberArray(
|
||||
llvm::StringRef key, llvm::ArrayRef<double> defaultValue) override;
|
||||
bool SetDefaultNumberArray(StringRef key,
|
||||
ArrayRef<double> defaultValue) override;
|
||||
|
||||
/**
|
||||
* Returns the number array the key maps to. If the key does not exist or is
|
||||
@@ -426,8 +549,8 @@ class NetworkTable : public ITable {
|
||||
* @note This makes a copy of the array. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*/
|
||||
virtual std::vector<double> GetNumberArray(
|
||||
llvm::StringRef key, llvm::ArrayRef<double> defaultValue) const override;
|
||||
std::vector<double> GetNumberArray(
|
||||
StringRef key, ArrayRef<double> defaultValue) const override;
|
||||
|
||||
/**
|
||||
* Put a string array in the table
|
||||
@@ -435,8 +558,7 @@ class NetworkTable : public ITable {
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
virtual bool PutStringArray(llvm::StringRef key,
|
||||
llvm::ArrayRef<std::string> value) override;
|
||||
bool PutStringArray(StringRef key, ArrayRef<std::string> value) override;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
@@ -444,8 +566,8 @@ class NetworkTable : public ITable {
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
virtual bool SetDefaultStringArray(
|
||||
llvm::StringRef key, llvm::ArrayRef<std::string> defaultValue) override;
|
||||
bool SetDefaultStringArray(StringRef key,
|
||||
ArrayRef<std::string> defaultValue) override;
|
||||
|
||||
/**
|
||||
* Returns the string array the key maps to. If the key does not exist or is
|
||||
@@ -458,9 +580,8 @@ class NetworkTable : public ITable {
|
||||
* @note This makes a copy of the array. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*/
|
||||
virtual std::vector<std::string> GetStringArray(
|
||||
llvm::StringRef key,
|
||||
llvm::ArrayRef<std::string> defaultValue) const override;
|
||||
std::vector<std::string> GetStringArray(
|
||||
StringRef key, ArrayRef<std::string> defaultValue) const override;
|
||||
|
||||
/**
|
||||
* Put a raw value (byte array) in the table
|
||||
@@ -468,7 +589,7 @@ class NetworkTable : public ITable {
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
virtual bool PutRaw(llvm::StringRef key, llvm::StringRef value) override;
|
||||
bool PutRaw(StringRef key, StringRef value) override;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
@@ -476,8 +597,7 @@ class NetworkTable : public ITable {
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
virtual bool SetDefaultRaw(llvm::StringRef key,
|
||||
llvm::StringRef defaultValue) override;
|
||||
bool SetDefaultRaw(StringRef key, StringRef defaultValue) override;
|
||||
|
||||
/**
|
||||
* Returns the raw value (byte array) the key maps to. If the key does not
|
||||
@@ -490,8 +610,7 @@ class NetworkTable : public ITable {
|
||||
* @note This makes a copy of the raw contents. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*/
|
||||
virtual std::string GetRaw(llvm::StringRef key,
|
||||
llvm::StringRef defaultValue) const override;
|
||||
std::string GetRaw(StringRef key, StringRef defaultValue) const override;
|
||||
|
||||
/**
|
||||
* Put a value in the table
|
||||
@@ -500,7 +619,7 @@ class NetworkTable : public ITable {
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
bool PutValue(llvm::StringRef key, std::shared_ptr<Value> value) override;
|
||||
bool PutValue(StringRef key, std::shared_ptr<Value> value) override;
|
||||
|
||||
/**
|
||||
* Gets the current value in the table, setting it if it does not exist.
|
||||
@@ -508,8 +627,8 @@ class NetworkTable : public ITable {
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @returns False if the table key exists with a different type
|
||||
*/
|
||||
virtual bool SetDefaultValue(
|
||||
llvm::StringRef key, std::shared_ptr<Value> defaultValue) override;
|
||||
bool SetDefaultValue(StringRef key,
|
||||
std::shared_ptr<Value> defaultValue) override;
|
||||
|
||||
/**
|
||||
* Gets the value associated with a key as an object
|
||||
@@ -518,14 +637,19 @@ class NetworkTable : public ITable {
|
||||
* @return the value associated with the given key, or nullptr if the key
|
||||
* does not exist
|
||||
*/
|
||||
std::shared_ptr<Value> GetValue(llvm::StringRef key) const override;
|
||||
std::shared_ptr<Value> GetValue(StringRef key) const override;
|
||||
|
||||
/**
|
||||
* Gets the full path of this table.
|
||||
* Gets the full path of this table. Does not include the trailing "/".
|
||||
* @return The path (e.g "", "/foo").
|
||||
*/
|
||||
llvm::StringRef GetPath() const override;
|
||||
StringRef GetPath() const override;
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
} // namespace nt
|
||||
|
||||
// For backwards compatability
|
||||
|
||||
449
src/main/native/include/networktables/NetworkTableEntry.h
Normal file
449
src/main/native/include/networktables/NetworkTableEntry.h
Normal file
@@ -0,0 +1,449 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NT_ENTRY_H_
|
||||
#define NT_ENTRY_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "llvm/StringRef.h"
|
||||
|
||||
#include "networktables/NetworkTableType.h"
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
#include "networktables/RpcCall.h"
|
||||
#include "ntcore_c.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
using llvm::ArrayRef;
|
||||
using llvm::StringRef;
|
||||
|
||||
class NetworkTableInstance;
|
||||
|
||||
/** NetworkTables Entry */
|
||||
class NetworkTableEntry final {
|
||||
public:
|
||||
/**
|
||||
* Flag values (as returned by {@link #getFlags()}).
|
||||
*/
|
||||
enum Flags { kPersistent = NT_PERSISTENT };
|
||||
|
||||
/**
|
||||
* Construct invalid instance.
|
||||
*/
|
||||
NetworkTableEntry();
|
||||
|
||||
/**
|
||||
* Construct from native handle.
|
||||
* @param handle Native handle
|
||||
*/
|
||||
explicit NetworkTableEntry(NT_Entry handle);
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle for the entry.
|
||||
* @return Native handle
|
||||
*/
|
||||
NT_Entry GetHandle() const;
|
||||
|
||||
/**
|
||||
* Gets the instance for the entry.
|
||||
* @return Instance
|
||||
*/
|
||||
NetworkTableInstance GetInstance() const;
|
||||
|
||||
/**
|
||||
* Determines if the entry currently exists.
|
||||
* @return True if the entry exists, false otherwise.
|
||||
*/
|
||||
bool Exists() const;
|
||||
|
||||
/**
|
||||
* Gets the name of the entry (the key).
|
||||
* @return the entry's name
|
||||
*/
|
||||
std::string GetName() const;
|
||||
|
||||
/**
|
||||
* Gets the type of the entry.
|
||||
* @return the entry's type
|
||||
*/
|
||||
NetworkTableType GetType() const;
|
||||
|
||||
/**
|
||||
* Returns the flags.
|
||||
* @return the flags (bitmask)
|
||||
*/
|
||||
unsigned int GetFlags() const;
|
||||
|
||||
/**
|
||||
* Gets the last time the entry's value was changed.
|
||||
* @return Entry last change time
|
||||
*/
|
||||
unsigned long long GetLastChange() const;
|
||||
|
||||
/**
|
||||
* Gets combined information about the entry.
|
||||
* @return Entry information
|
||||
*/
|
||||
EntryInfo GetInfo() const;
|
||||
|
||||
/**
|
||||
* Gets the entry's value. If the entry does not exist, returns nullptr.
|
||||
*
|
||||
* @return the entry's value or nullptr if it does not exist.
|
||||
*/
|
||||
std::shared_ptr<Value> GetValue() const;
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a boolean. If the entry does not exist or is of
|
||||
* different type, it will return the default value.
|
||||
*
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
bool GetBoolean(bool defaultValue) const;
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a double. If the entry does not exist or is of
|
||||
* different type, it will return the default value.
|
||||
*
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
double GetDouble(double defaultValue) const;
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a string. If the entry does not exist or is of
|
||||
* different type, it will return the default value.
|
||||
*
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
std::string GetString(StringRef defaultValue) const;
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a raw. If the entry does not exist or is of
|
||||
* different type, it will return the default value.
|
||||
*
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*/
|
||||
std::string GetRaw(StringRef defaultValue) const;
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a boolean array. If the entry does not exist
|
||||
* or is of different type, it will return the default value.
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*
|
||||
* @note This makes a copy of the array. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*
|
||||
* @note The returned array is std::vector<int> instead of std::vector<bool>
|
||||
* because std::vector<bool> is special-cased in C++. 0 is false, any
|
||||
* non-zero value is true.
|
||||
*/
|
||||
std::vector<int> GetBooleanArray(ArrayRef<int> defaultValue) const;
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a double array. If the entry does not exist
|
||||
* or is of different type, it will return the default value.
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*
|
||||
* @note This makes a copy of the array. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*/
|
||||
std::vector<double> GetDoubleArray(ArrayRef<double> defaultValue) const;
|
||||
|
||||
/**
|
||||
* Gets the entry's value as a string array. If the entry does not exist
|
||||
* or is of different type, it will return the default value.
|
||||
* @param defaultValue the value to be returned if no value is found
|
||||
* @return the entry's value or the given default value
|
||||
*
|
||||
* @note This makes a copy of the array. If the overhead of this is a
|
||||
* concern, use GetValue() instead.
|
||||
*/
|
||||
std::vector<std::string> GetStringArray(
|
||||
ArrayRef<std::string> defaultValue) const;
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDefaultValue(std::shared_ptr<Value> value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDefaultBoolean(bool defaultValue);
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDefaultDouble(double defaultValue);
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDefaultString(StringRef defaultValue);
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDefaultRaw(StringRef defaultValue);
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDefaultBooleanArray(ArrayRef<int> defaultValue);
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDefaultDoubleArray(ArrayRef<double> defaultValue);
|
||||
|
||||
/**
|
||||
* Sets the entry's value if it does not exist.
|
||||
* @param defaultValue the default value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDefaultStringArray(ArrayRef<std::string> defaultValue);
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetValue(std::shared_ptr<Value> value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetBoolean(bool value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDouble(double value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetString(StringRef value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetRaw(StringRef value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetBooleanArray(ArrayRef<int> value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetDoubleArray(ArrayRef<double> value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value.
|
||||
* @param value the value to set
|
||||
* @return False if the entry exists with a different type
|
||||
*/
|
||||
bool SetStringArray(ArrayRef<std::string> value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
void ForceSetValue(std::shared_ptr<Value> value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
void ForceSetBoolean(bool value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
void ForceSetDouble(double value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
void ForceSetString(StringRef value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
void ForceSetRaw(StringRef value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
void ForceSetBooleanArray(ArrayRef<int> value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
void ForceSetDoubleArray(ArrayRef<double> value);
|
||||
|
||||
/**
|
||||
* Sets the entry's value. If the value is of different type, the type is
|
||||
* changed to match the new value.
|
||||
* @param value the value to set
|
||||
*/
|
||||
void ForceSetStringArray(ArrayRef<std::string> value);
|
||||
|
||||
/**
|
||||
* Sets flags.
|
||||
* @param flags the flags to set (bitmask)
|
||||
*/
|
||||
void SetFlags(unsigned int flags);
|
||||
|
||||
/**
|
||||
* Clears flags.
|
||||
* @param flags the flags to clear (bitmask)
|
||||
*/
|
||||
void ClearFlags(unsigned int flags);
|
||||
|
||||
/**
|
||||
* Make value persistent through program restarts.
|
||||
*/
|
||||
void SetPersistent();
|
||||
|
||||
/**
|
||||
* Stop making value persistent through program restarts.
|
||||
*/
|
||||
void ClearPersistent();
|
||||
|
||||
/**
|
||||
* Returns whether the value is persistent through program restarts.
|
||||
* @return True if the value is persistent.
|
||||
*/
|
||||
bool IsPersistent() const;
|
||||
|
||||
/**
|
||||
* Deletes the entry.
|
||||
*/
|
||||
void Delete();
|
||||
|
||||
/**
|
||||
* Create a callback-based RPC entry point. Only valid to use on the server.
|
||||
* The callback function will be called when the RPC is called.
|
||||
* This function creates RPC version 0 definitions (raw data in and out).
|
||||
* @param callback callback function
|
||||
*/
|
||||
void CreateRpc(std::function<void(const RpcAnswer& answer)> callback);
|
||||
|
||||
/**
|
||||
* Create a polled RPC entry point. Only valid to use on the server.
|
||||
* The caller is responsible for calling NetworkTableInstance::PollRpc()
|
||||
* to poll for servicing incoming RPC calls.
|
||||
* This function creates RPC version 0 definitions (raw data in and out).
|
||||
*/
|
||||
void CreatePolledRpc();
|
||||
|
||||
/**
|
||||
* Call a RPC function. May be used on either the client or server.
|
||||
* This function is non-blocking. Either RpcCall::GetResult() or
|
||||
* RpcCall::CancelResult() must be called on the return value to either
|
||||
* get or ignore the result of the call.
|
||||
* @param params parameter
|
||||
* @return RPC call object.
|
||||
*/
|
||||
RpcCall CallRpc(StringRef params);
|
||||
|
||||
/**
|
||||
* Add a listener for changes to this entry.
|
||||
*
|
||||
* @param callback listener to add
|
||||
* @param flags NotifyKind bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_EntryListener AddListener(
|
||||
std::function<void(const EntryNotification& event)> callback,
|
||||
unsigned int flags) const;
|
||||
|
||||
/**
|
||||
* Remove an entry listener.
|
||||
* @param entry_listener Listener handle to remove
|
||||
*/
|
||||
void RemoveListener(NT_EntryListener entry_listener);
|
||||
|
||||
/**
|
||||
* Equality operator. Returns true if both instances refer to the same
|
||||
* native handle.
|
||||
*/
|
||||
bool operator==(const NetworkTableEntry& oth) const {
|
||||
return m_handle == oth.m_handle;
|
||||
}
|
||||
|
||||
/** Inequality operator. */
|
||||
bool operator!=(const NetworkTableEntry& oth) const {
|
||||
return !(*this == oth);
|
||||
}
|
||||
|
||||
protected:
|
||||
/* Native handle */
|
||||
NT_Entry m_handle;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#include "networktables/NetworkTableEntry.inl"
|
||||
|
||||
#endif // NT_ENTRY_H_
|
||||
232
src/main/native/include/networktables/NetworkTableEntry.inl
Normal file
232
src/main/native/include/networktables/NetworkTableEntry.inl
Normal file
@@ -0,0 +1,232 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NT_ENTRY_INL_
|
||||
#define NT_ENTRY_INL_
|
||||
|
||||
namespace nt {
|
||||
|
||||
inline NetworkTableEntry::NetworkTableEntry() : m_handle{0} {}
|
||||
|
||||
inline NetworkTableEntry::NetworkTableEntry(NT_Entry handle)
|
||||
: m_handle{handle} {}
|
||||
|
||||
inline NT_Entry NetworkTableEntry::GetHandle() const { return m_handle; }
|
||||
|
||||
inline bool NetworkTableEntry::Exists() const {
|
||||
return GetEntryType(m_handle) != NT_UNASSIGNED;
|
||||
}
|
||||
|
||||
inline std::string NetworkTableEntry::GetName() const {
|
||||
return GetEntryName(m_handle);
|
||||
}
|
||||
|
||||
inline NetworkTableType NetworkTableEntry::GetType() const {
|
||||
return static_cast<NetworkTableType>(GetEntryType(m_handle));
|
||||
}
|
||||
|
||||
inline unsigned int NetworkTableEntry::GetFlags() const {
|
||||
return GetEntryFlags(m_handle);
|
||||
}
|
||||
|
||||
inline unsigned long long NetworkTableEntry::GetLastChange() const {
|
||||
return GetEntryLastChange(m_handle);
|
||||
}
|
||||
|
||||
inline EntryInfo NetworkTableEntry::GetInfo() const {
|
||||
return GetEntryInfo(m_handle);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<Value> NetworkTableEntry::GetValue() const {
|
||||
return GetEntryValue(m_handle);
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::GetBoolean(bool defaultValue) const {
|
||||
auto value = GetEntryValue(m_handle);
|
||||
if (!value || value->type() != NT_BOOLEAN) return defaultValue;
|
||||
return value->GetBoolean();
|
||||
}
|
||||
|
||||
inline double NetworkTableEntry::GetDouble(double defaultValue) const {
|
||||
auto value = GetEntryValue(m_handle);
|
||||
if (!value || value->type() != NT_DOUBLE) return defaultValue;
|
||||
return value->GetDouble();
|
||||
}
|
||||
|
||||
inline std::string NetworkTableEntry::GetString(StringRef defaultValue) const {
|
||||
auto value = GetEntryValue(m_handle);
|
||||
if (!value || value->type() != NT_STRING) return defaultValue;
|
||||
return value->GetString();
|
||||
}
|
||||
|
||||
inline std::string NetworkTableEntry::GetRaw(StringRef defaultValue) const {
|
||||
auto value = GetEntryValue(m_handle);
|
||||
if (!value || value->type() != NT_RAW) return defaultValue;
|
||||
return value->GetString();
|
||||
}
|
||||
|
||||
inline std::vector<int> NetworkTableEntry::GetBooleanArray(
|
||||
ArrayRef<int> defaultValue) const {
|
||||
auto value = GetEntryValue(m_handle);
|
||||
if (!value || value->type() != NT_BOOLEAN_ARRAY) return defaultValue;
|
||||
return value->GetBooleanArray();
|
||||
}
|
||||
|
||||
inline std::vector<double> NetworkTableEntry::GetDoubleArray(
|
||||
ArrayRef<double> defaultValue) const {
|
||||
auto value = GetEntryValue(m_handle);
|
||||
if (!value || value->type() != NT_DOUBLE_ARRAY) return defaultValue;
|
||||
return value->GetDoubleArray();
|
||||
}
|
||||
|
||||
inline std::vector<std::string> NetworkTableEntry::GetStringArray(
|
||||
ArrayRef<std::string> defaultValue) const {
|
||||
auto value = GetEntryValue(m_handle);
|
||||
if (!value || value->type() != NT_STRING_ARRAY) return defaultValue;
|
||||
return value->GetStringArray();
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDefaultValue(std::shared_ptr<Value> value) {
|
||||
return SetDefaultEntryValue(m_handle, value);
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDefaultBoolean(bool defaultValue) {
|
||||
return SetDefaultEntryValue(m_handle, Value::MakeBoolean(defaultValue));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDefaultDouble(double defaultValue) {
|
||||
return SetDefaultEntryValue(m_handle, Value::MakeDouble(defaultValue));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDefaultString(StringRef defaultValue) {
|
||||
return SetDefaultEntryValue(m_handle, Value::MakeString(defaultValue));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDefaultRaw(StringRef defaultValue) {
|
||||
return SetDefaultEntryValue(m_handle, Value::MakeRaw(defaultValue));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDefaultBooleanArray(
|
||||
ArrayRef<int> defaultValue) {
|
||||
return SetDefaultEntryValue(m_handle, Value::MakeBooleanArray(defaultValue));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDefaultDoubleArray(
|
||||
ArrayRef<double> defaultValue) {
|
||||
return SetDefaultEntryValue(m_handle, Value::MakeDoubleArray(defaultValue));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDefaultStringArray(
|
||||
ArrayRef<std::string> defaultValue) {
|
||||
return SetDefaultEntryValue(m_handle, Value::MakeStringArray(defaultValue));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetValue(std::shared_ptr<Value> value) {
|
||||
return SetEntryValue(m_handle, value);
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetBoolean(bool value) {
|
||||
return SetEntryValue(m_handle, Value::MakeBoolean(value));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDouble(double value) {
|
||||
return SetEntryValue(m_handle, Value::MakeDouble(value));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetString(StringRef value) {
|
||||
return SetEntryValue(m_handle, Value::MakeString(value));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetRaw(StringRef value) {
|
||||
return SetEntryValue(m_handle, Value::MakeRaw(value));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetBooleanArray(ArrayRef<int> value) {
|
||||
return SetEntryValue(m_handle, Value::MakeBooleanArray(value));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetDoubleArray(ArrayRef<double> value) {
|
||||
return SetEntryValue(m_handle, Value::MakeDoubleArray(value));
|
||||
}
|
||||
|
||||
inline bool NetworkTableEntry::SetStringArray(ArrayRef<std::string> value) {
|
||||
return SetEntryValue(m_handle, Value::MakeStringArray(value));
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ForceSetValue(std::shared_ptr<Value> value) {
|
||||
SetEntryTypeValue(m_handle, value);
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ForceSetBoolean(bool value) {
|
||||
SetEntryTypeValue(m_handle, Value::MakeBoolean(value));
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ForceSetDouble(double value) {
|
||||
SetEntryTypeValue(m_handle, Value::MakeDouble(value));
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ForceSetString(StringRef value) {
|
||||
SetEntryTypeValue(m_handle, Value::MakeString(value));
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ForceSetRaw(StringRef value) {
|
||||
SetEntryTypeValue(m_handle, Value::MakeRaw(value));
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ForceSetBooleanArray(ArrayRef<int> value) {
|
||||
SetEntryTypeValue(m_handle, Value::MakeBooleanArray(value));
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ForceSetDoubleArray(ArrayRef<double> value) {
|
||||
SetEntryTypeValue(m_handle, Value::MakeDoubleArray(value));
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ForceSetStringArray(
|
||||
ArrayRef<std::string> value) {
|
||||
SetEntryTypeValue(m_handle, Value::MakeStringArray(value));
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::SetFlags(unsigned int flags) {
|
||||
SetEntryFlags(m_handle, GetFlags() | flags);
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::ClearFlags(unsigned int flags) {
|
||||
SetEntryFlags(m_handle, GetFlags() & ~flags);
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::SetPersistent() { SetFlags(kPersistent); }
|
||||
|
||||
inline void NetworkTableEntry::ClearPersistent() { ClearFlags(kPersistent); }
|
||||
|
||||
inline bool NetworkTableEntry::IsPersistent() const {
|
||||
return (GetFlags() & kPersistent) != 0;
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::Delete() { DeleteEntry(m_handle); }
|
||||
|
||||
inline void NetworkTableEntry::CreateRpc(
|
||||
std::function<void(const RpcAnswer& answer)> callback) {
|
||||
::nt::CreateRpc(m_handle, StringRef("\0", 1), callback);
|
||||
}
|
||||
|
||||
inline RpcCall NetworkTableEntry::CallRpc(StringRef params) {
|
||||
return RpcCall{m_handle, ::nt::CallRpc(m_handle, params)};
|
||||
}
|
||||
|
||||
inline NT_EntryListener NetworkTableEntry::AddListener(
|
||||
std::function<void(const EntryNotification& event)> callback,
|
||||
unsigned int flags) const {
|
||||
return AddEntryListener(m_handle, callback, flags);
|
||||
}
|
||||
|
||||
inline void NetworkTableEntry::RemoveListener(NT_EntryListener entry_listener) {
|
||||
RemoveEntryListener(entry_listener);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NT_ENTRY_INL_
|
||||
521
src/main/native/include/networktables/NetworkTableInstance.h
Normal file
521
src/main/native/include/networktables/NetworkTableInstance.h
Normal file
@@ -0,0 +1,521 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NT_INSTANCE_H_
|
||||
#define NT_INSTANCE_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "llvm/ArrayRef.h"
|
||||
#include "llvm/StringRef.h"
|
||||
|
||||
#include "networktables/NetworkTable.h"
|
||||
#include "networktables/NetworkTableEntry.h"
|
||||
#include "ntcore_c.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
#ifndef NT_NOEXCEPT
|
||||
#ifdef _MSC_VER
|
||||
#if _MSC_VER >= 1900
|
||||
#define NT_NOEXCEPT noexcept
|
||||
#else
|
||||
#define NT_NOEXCEPT throw()
|
||||
#endif
|
||||
#else
|
||||
#define NT_NOEXCEPT noexcept
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace nt {
|
||||
|
||||
using llvm::ArrayRef;
|
||||
using llvm::StringRef;
|
||||
|
||||
/** NetworkTables Instance.
|
||||
*
|
||||
* Instances are completely independent from each other. Table operations on
|
||||
* one instance will not be visible to other instances unless the instances are
|
||||
* connected via the network. The main limitation on instances is that you
|
||||
* cannot have two servers on the same network port. The main utility of
|
||||
* instances is for unit testing, but they can also enable one program to
|
||||
* connect to two different NetworkTables networks.
|
||||
*
|
||||
* The global "default" instance (as returned by GetDefault()) is
|
||||
* always available, and is intended for the common case when there is only
|
||||
* a single NetworkTables instance being used in the program. The
|
||||
* default instance cannot be destroyed.
|
||||
*
|
||||
* Additional instances can be created with the Create() function.
|
||||
* Instances are not reference counted or RAII. Instead, they must be
|
||||
* explicitly destroyed (with Destroy()).
|
||||
*/
|
||||
class NetworkTableInstance final {
|
||||
public:
|
||||
/**
|
||||
* Client/server mode flag values (as returned by GetNetworkMode()).
|
||||
* This is a bitmask.
|
||||
*/
|
||||
enum NetworkMode {
|
||||
kNetModeNone = NT_NET_MODE_NONE,
|
||||
kNetModeServer = NT_NET_MODE_SERVER,
|
||||
kNetModeClient = NT_NET_MODE_CLIENT,
|
||||
kNetModeStarting = NT_NET_MODE_STARTING,
|
||||
kNetModeFailure = NT_NET_MODE_FAILURE
|
||||
};
|
||||
|
||||
/**
|
||||
* Logging levels (as used by SetLogger()).
|
||||
*/
|
||||
enum LogLevel {
|
||||
kLogCritical = NT_LOG_CRITICAL,
|
||||
kLogError = NT_LOG_ERROR,
|
||||
kLogWarning = NT_LOG_WARNING,
|
||||
kLogInfo = NT_LOG_INFO,
|
||||
kLogDebug = NT_LOG_DEBUG,
|
||||
kLogDebug1 = NT_LOG_DEBUG1,
|
||||
kLogDebug2 = NT_LOG_DEBUG2,
|
||||
kLogDebug3 = NT_LOG_DEBUG3,
|
||||
kLogDebug4 = NT_LOG_DEBUG4
|
||||
};
|
||||
|
||||
/**
|
||||
* The default port that network tables operates on.
|
||||
*/
|
||||
enum { kDefaultPort = NT_DEFAULT_PORT };
|
||||
|
||||
/**
|
||||
* Construct invalid instance.
|
||||
*/
|
||||
NetworkTableInstance() NT_NOEXCEPT;
|
||||
|
||||
/**
|
||||
* Construct from native handle.
|
||||
* @param handle Native handle
|
||||
*/
|
||||
explicit NetworkTableInstance(NT_Inst inst) NT_NOEXCEPT;
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Get global default instance.
|
||||
* @return Global default instance
|
||||
*/
|
||||
static NetworkTableInstance GetDefault();
|
||||
|
||||
/**
|
||||
* Create an instance.
|
||||
* @return Newly created instance
|
||||
*/
|
||||
static NetworkTableInstance Create();
|
||||
|
||||
/**
|
||||
* Destroys an instance (note: this has global effect).
|
||||
* @param inst Instance
|
||||
*/
|
||||
static void Destroy(NetworkTableInstance inst);
|
||||
|
||||
/**
|
||||
* Gets the native handle for the entry.
|
||||
* @return Native handle
|
||||
*/
|
||||
NT_Inst GetHandle() const;
|
||||
|
||||
/**
|
||||
* Gets the entry for a key.
|
||||
* @param name Key
|
||||
* @return Network table entry.
|
||||
*/
|
||||
NetworkTableEntry GetEntry(StringRef name);
|
||||
|
||||
/**
|
||||
* Get entries starting with the given prefix.
|
||||
* The results are optionally filtered by string prefix and entry type to
|
||||
* only return a subset of all entries.
|
||||
*
|
||||
* @param prefix entry name required prefix; only entries whose name
|
||||
* starts with this string are returned
|
||||
* @param types bitmask of types; 0 is treated as a "don't care"
|
||||
* @return Array of entries.
|
||||
*/
|
||||
std::vector<NetworkTableEntry> GetEntries(StringRef prefix,
|
||||
unsigned int types);
|
||||
|
||||
/**
|
||||
* Get information about entries starting with the given prefix.
|
||||
* The results are optionally filtered by string prefix and entry type to
|
||||
* only return a subset of all entries.
|
||||
*
|
||||
* @param prefix entry name required prefix; only entries whose name
|
||||
* starts with this string are returned
|
||||
* @param types bitmask of types; 0 is treated as a "don't care"
|
||||
* @return Array of entry information.
|
||||
*/
|
||||
std::vector<EntryInfo> GetEntryInfo(StringRef prefix,
|
||||
unsigned int types) const;
|
||||
|
||||
/**
|
||||
* Gets the table with the specified key.
|
||||
*
|
||||
* @param key the key name
|
||||
* @return The network table
|
||||
*/
|
||||
std::shared_ptr<NetworkTable> GetTable(StringRef key) const;
|
||||
|
||||
/**
|
||||
* Deletes ALL keys in ALL subtables (except persistent values).
|
||||
* Use with caution!
|
||||
*/
|
||||
void DeleteAllEntries();
|
||||
|
||||
/**
|
||||
* @defgroup EntryListenerFunctions Entry Listener Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add a listener for all entries starting with a certain prefix.
|
||||
*
|
||||
* @param prefix UTF-8 string prefix
|
||||
* @param callback listener to add
|
||||
* @param flags EntryListenerFlags bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_EntryListener AddEntryListener(
|
||||
StringRef prefix,
|
||||
std::function<void(const EntryNotification& event)> callback,
|
||||
unsigned int flags) const;
|
||||
|
||||
/**
|
||||
* Remove an entry listener.
|
||||
* @param entry_listener Listener handle to remove
|
||||
*/
|
||||
static void RemoveEntryListener(NT_EntryListener entry_listener);
|
||||
|
||||
/**
|
||||
* Wait for the entry listener queue to be empty. This is primarily useful
|
||||
* for deterministic testing. This blocks until either the entry listener
|
||||
* queue is empty (e.g. there are no more events that need to be passed along
|
||||
* to callbacks or poll queues) or the timeout expires.
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior,
|
||||
* or a negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForEntryListenerQueue(double timeout);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup ConnectionListenerFunctions Connection Listener Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add a connection listener.
|
||||
*
|
||||
* @param callback listener to add
|
||||
* @param immediate_notify notify listener of all existing connections
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ConnectionListener AddConnectionListener(
|
||||
std::function<void(const ConnectionNotification& event)> callback,
|
||||
bool immediate_notify) const;
|
||||
|
||||
/**
|
||||
* Remove a connection listener.
|
||||
* @param conn_listener Listener handle to remove
|
||||
*/
|
||||
static void RemoveConnectionListener(NT_ConnectionListener conn_listener);
|
||||
|
||||
/**
|
||||
* Wait for the connection listener queue to be empty. This is primarily useful
|
||||
* for deterministic testing. This blocks until either the connection listener
|
||||
* queue is empty (e.g. there are no more events that need to be passed along
|
||||
* to callbacks or poll queues) or the timeout expires.
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior,
|
||||
* or a negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForConnectionListenerQueue(double timeout);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup RpcFunctions Remote Procedure Call Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Wait for the incoming RPC call queue to be empty. This is primarily useful
|
||||
* for deterministic testing. This blocks until either the RPC call
|
||||
* queue is empty (e.g. there are no more events that need to be passed along
|
||||
* to callbacks or poll queues) or the timeout expires.
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior,
|
||||
* or a negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForRpcCallQueue(double timeout);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup NetworkFunctions Client/Server Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set the network identity of this node.
|
||||
* This is the name used during the initial connection handshake, and is
|
||||
* visible through ConnectionInfo on the remote node.
|
||||
* @param name identity to advertise
|
||||
*/
|
||||
void SetNetworkIdentity(StringRef name);
|
||||
|
||||
/**
|
||||
* Get the current network mode.
|
||||
* @return Bitmask of NetworkMode.
|
||||
*/
|
||||
unsigned int GetNetworkMode() const;
|
||||
|
||||
/**
|
||||
* Starts a server using the specified filename, listening address, and port.
|
||||
*
|
||||
* @param persist_filename the name of the persist file to use (UTF-8 string,
|
||||
* null terminated)
|
||||
* @param listen_address the address to listen on, or null to listen on any
|
||||
* address (UTF-8 string, null terminated)
|
||||
* @param port port to communicate over
|
||||
*/
|
||||
void StartServer(StringRef persist_filename = "networktables.ini",
|
||||
const char* listen_address = "",
|
||||
unsigned int port = kDefaultPort);
|
||||
|
||||
/**
|
||||
* Stops the server if it is running.
|
||||
*/
|
||||
void StopServer();
|
||||
|
||||
/**
|
||||
* Starts a client. Use SetServer to set the server name and port.
|
||||
*/
|
||||
void StartClient();
|
||||
|
||||
/**
|
||||
* Starts a client using the specified server and port
|
||||
*
|
||||
* @param server_name server name (UTF-8 string, null terminated)
|
||||
* @param port port to communicate over
|
||||
*/
|
||||
void StartClient(const char* server_name, unsigned int port = kDefaultPort);
|
||||
|
||||
/**
|
||||
* Starts a client using the specified (server, port) combinations. The
|
||||
* client will attempt to connect to each server in round robin fashion.
|
||||
*
|
||||
* @param servers array of server name and port pairs
|
||||
*/
|
||||
void StartClient(ArrayRef<std::pair<StringRef, unsigned int>> servers);
|
||||
|
||||
/**
|
||||
* Starts a client using the specified servers and port. The
|
||||
* client will attempt to connect to each server in round robin fashion.
|
||||
*
|
||||
* @param servers array of server names
|
||||
* @param port port to communicate over
|
||||
*/
|
||||
void StartClient(ArrayRef<StringRef> servers,
|
||||
unsigned int port = kDefaultPort);
|
||||
|
||||
/**
|
||||
* Starts a client using commonly known robot addresses for the specified
|
||||
* team.
|
||||
*
|
||||
* @param team team number
|
||||
* @param port port to communicate over
|
||||
*/
|
||||
void StartClientTeam(unsigned int team, unsigned int port = kDefaultPort);
|
||||
|
||||
/**
|
||||
* Stops the client if it is running.
|
||||
*/
|
||||
void StopClient();
|
||||
|
||||
/**
|
||||
* Sets server address and port for client (without restarting client).
|
||||
*
|
||||
* @param server_name server name (UTF-8 string, null terminated)
|
||||
* @param port port to communicate over
|
||||
*/
|
||||
void SetServer(const char* server_name, unsigned int port = kDefaultPort);
|
||||
|
||||
/**
|
||||
* Sets server addresses and ports for client (without restarting client).
|
||||
* The client will attempt to connect to each server in round robin fashion.
|
||||
*
|
||||
* @param servers array of server name and port pairs
|
||||
*/
|
||||
void SetServer(ArrayRef<std::pair<StringRef, unsigned int>> servers);
|
||||
|
||||
/**
|
||||
* Sets server addresses and port for client (without restarting client).
|
||||
* The client will attempt to connect to each server in round robin fashion.
|
||||
*
|
||||
* @param servers array of server names
|
||||
* @param port port to communicate over
|
||||
*/
|
||||
void SetServer(ArrayRef<StringRef> servers, unsigned int port = kDefaultPort);
|
||||
|
||||
/**
|
||||
* Sets server addresses and port for client (without restarting client).
|
||||
* Connects using commonly known robot addresses for the specified team.
|
||||
*
|
||||
* @param team team number
|
||||
* @param port port to communicate over
|
||||
*/
|
||||
void SetServerTeam(unsigned int team, unsigned int port = kDefaultPort);
|
||||
|
||||
/**
|
||||
* Starts requesting server address from Driver Station.
|
||||
* This connects to the Driver Station running on localhost to obtain the
|
||||
* server IP address.
|
||||
*
|
||||
* @param port server port to use in combination with IP from DS
|
||||
*/
|
||||
void StartDSClient(unsigned int port = kDefaultPort);
|
||||
|
||||
/**
|
||||
* Stops requesting server address from Driver Station.
|
||||
*/
|
||||
void StopDSClient();
|
||||
|
||||
/**
|
||||
* Set the periodic update rate.
|
||||
* Sets how frequently updates are sent to other nodes over the network.
|
||||
*
|
||||
* @param interval update interval in seconds (range 0.01 to 1.0)
|
||||
*/
|
||||
void SetUpdateRate(double interval);
|
||||
|
||||
/**
|
||||
* Flushes all updated values immediately to the network.
|
||||
* @note This is rate-limited to protect the network from flooding.
|
||||
* This is primarily useful for synchronizing network updates with
|
||||
* user code.
|
||||
*/
|
||||
void Flush() const;
|
||||
|
||||
/**
|
||||
* Get information on the currently established network connections.
|
||||
* If operating as a client, this will return either zero or one values.
|
||||
* @return array of connection information
|
||||
*/
|
||||
std::vector<ConnectionInfo> GetConnections() const;
|
||||
|
||||
/**
|
||||
* Return whether or not the instance is connected to another node.
|
||||
* @return True if connected.
|
||||
*/
|
||||
bool IsConnected() const;
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup PersistentFunctions Persistent Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Save persistent values to a file. The server automatically does this,
|
||||
* but this function provides a way to save persistent values in the same
|
||||
* format to a file on either a client or a server.
|
||||
* @param filename filename
|
||||
* @return error string, or nullptr if successful
|
||||
*/
|
||||
const char* SavePersistent(StringRef filename) const;
|
||||
|
||||
/**
|
||||
* Load persistent values from a file. The server automatically does this
|
||||
* at startup, but this function provides a way to restore persistent values
|
||||
* in the same format from a file at any time on either a client or a server.
|
||||
* @param filename filename
|
||||
* @param warn callback function for warnings
|
||||
* @return error string, or nullptr if successful
|
||||
*/
|
||||
const char* LoadPersistent(
|
||||
StringRef filename,
|
||||
std::function<void(size_t line, const char* msg)> warn);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup LoggerFunctions Logger Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add logger callback function. By default, log messages are sent to stderr;
|
||||
* this function sends log messages with the specified levels to the provided
|
||||
* callback function instead. The callback function will only be called for
|
||||
* log messages with level greater than or equal to minLevel and less than or
|
||||
* equal to maxLevel; messages outside this range will be silently ignored.
|
||||
*
|
||||
* @param func log callback function
|
||||
* @param minLevel minimum log level
|
||||
* @param maxLevel maximum log level
|
||||
* @return Logger handle
|
||||
*/
|
||||
NT_Logger AddLogger(std::function<void(const LogMessage& msg)> func,
|
||||
unsigned int min_level, unsigned int max_level);
|
||||
|
||||
/**
|
||||
* Remove a logger.
|
||||
* @param logger Logger handle to remove
|
||||
*/
|
||||
static void RemoveLogger(NT_Logger logger);
|
||||
|
||||
/**
|
||||
* Wait for the incoming log event queue to be empty. This is primarily useful
|
||||
* for deterministic testing. This blocks until either the log event
|
||||
* queue is empty (e.g. there are no more events that need to be passed along
|
||||
* to callbacks or poll queues) or the timeout expires.
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior,
|
||||
* or a negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForLoggerQueue(double timeout);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Equality operator. Returns true if both instances refer to the same
|
||||
* native handle.
|
||||
*/
|
||||
bool operator==(const NetworkTableInstance& other) const {
|
||||
return m_handle == other.m_handle;
|
||||
}
|
||||
|
||||
/** Inequality operator. */
|
||||
bool operator!=(const NetworkTableInstance& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
/* Native handle */
|
||||
NT_Inst m_handle;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#include "networktables/NetworkTableInstance.inl"
|
||||
|
||||
#endif // NT_INSTANCE_H_
|
||||
176
src/main/native/include/networktables/NetworkTableInstance.inl
Normal file
176
src/main/native/include/networktables/NetworkTableInstance.inl
Normal file
@@ -0,0 +1,176 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NT_INSTANCE_INL_
|
||||
#define NT_INSTANCE_INL_
|
||||
|
||||
namespace nt {
|
||||
|
||||
inline NetworkTableInstance::NetworkTableInstance() NT_NOEXCEPT : m_handle{0} {}
|
||||
|
||||
inline NetworkTableInstance::NetworkTableInstance(NT_Inst handle) NT_NOEXCEPT
|
||||
: m_handle{handle} {}
|
||||
|
||||
inline NetworkTableInstance NetworkTableInstance::GetDefault() {
|
||||
return NetworkTableInstance{GetDefaultInstance()};
|
||||
}
|
||||
|
||||
inline NetworkTableInstance NetworkTableInstance::Create() {
|
||||
return NetworkTableInstance{CreateInstance()};
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::Destroy(NetworkTableInstance inst) {
|
||||
if (inst.m_handle != 0) DestroyInstance(inst.m_handle);
|
||||
}
|
||||
|
||||
inline NT_Inst NetworkTableInstance::GetHandle() const { return m_handle; }
|
||||
|
||||
inline NetworkTableEntry NetworkTableInstance::GetEntry(StringRef name) {
|
||||
return NetworkTableEntry{::nt::GetEntry(m_handle, name)};
|
||||
}
|
||||
|
||||
inline std::vector<NetworkTableEntry> NetworkTableInstance::GetEntries(
|
||||
StringRef prefix, unsigned int types) {
|
||||
std::vector<NetworkTableEntry> entries;
|
||||
for (auto entry : ::nt::GetEntries(m_handle, prefix, types))
|
||||
entries.emplace_back(entry);
|
||||
return entries;
|
||||
}
|
||||
|
||||
inline std::vector<EntryInfo> NetworkTableInstance::GetEntryInfo(
|
||||
StringRef prefix, unsigned int types) const {
|
||||
return ::nt::GetEntryInfo(m_handle, prefix, types);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::DeleteAllEntries() {
|
||||
::nt::DeleteAllEntries(m_handle);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::RemoveEntryListener(
|
||||
NT_EntryListener entry_listener) {
|
||||
::nt::RemoveEntryListener(entry_listener);
|
||||
}
|
||||
|
||||
inline bool NetworkTableInstance::WaitForEntryListenerQueue(double timeout) {
|
||||
return ::nt::WaitForEntryListenerQueue(m_handle, timeout);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::RemoveConnectionListener(
|
||||
NT_ConnectionListener conn_listener) {
|
||||
::nt::RemoveConnectionListener(conn_listener);
|
||||
}
|
||||
|
||||
inline bool NetworkTableInstance::WaitForConnectionListenerQueue(
|
||||
double timeout) {
|
||||
return ::nt::WaitForConnectionListenerQueue(m_handle, timeout);
|
||||
}
|
||||
|
||||
inline bool NetworkTableInstance::WaitForRpcCallQueue(double timeout) {
|
||||
return ::nt::WaitForRpcCallQueue(m_handle, timeout);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::SetNetworkIdentity(StringRef name) {
|
||||
::nt::SetNetworkIdentity(m_handle, name);
|
||||
}
|
||||
|
||||
inline unsigned int NetworkTableInstance::GetNetworkMode() const {
|
||||
return ::nt::GetNetworkMode(m_handle);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::StartServer(StringRef persist_filename,
|
||||
const char* listen_address,
|
||||
unsigned int port) {
|
||||
::nt::StartServer(m_handle, persist_filename, listen_address, port);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::StopServer() { ::nt::StopServer(m_handle); }
|
||||
|
||||
inline void NetworkTableInstance::StartClient() { ::nt::StartClient(m_handle); }
|
||||
|
||||
inline void NetworkTableInstance::StartClient(const char* server_name,
|
||||
unsigned int port) {
|
||||
::nt::StartClient(m_handle, server_name, port);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::StartClient(
|
||||
ArrayRef<std::pair<StringRef, unsigned int>> servers) {
|
||||
::nt::StartClient(m_handle, servers);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::StartClientTeam(unsigned int team,
|
||||
unsigned int port) {
|
||||
::nt::StartClientTeam(m_handle, team, port);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::StopClient() { ::nt::StopClient(m_handle); }
|
||||
|
||||
inline void NetworkTableInstance::SetServer(const char* server_name,
|
||||
unsigned int port) {
|
||||
::nt::SetServer(m_handle, server_name, port);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::SetServer(
|
||||
ArrayRef<std::pair<StringRef, unsigned int>> servers) {
|
||||
::nt::SetServer(m_handle, servers);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::SetServerTeam(unsigned int team,
|
||||
unsigned int port) {
|
||||
::nt::SetServerTeam(m_handle, team, port);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::StartDSClient(unsigned int port) {
|
||||
::nt::StartDSClient(m_handle, port);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::StopDSClient() {
|
||||
::nt::StopDSClient(m_handle);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::SetUpdateRate(double interval) {
|
||||
::nt::SetUpdateRate(m_handle, interval);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::Flush() const { ::nt::Flush(m_handle); }
|
||||
|
||||
inline std::vector<ConnectionInfo> NetworkTableInstance::GetConnections()
|
||||
const {
|
||||
return ::nt::GetConnections(m_handle);
|
||||
}
|
||||
|
||||
inline bool NetworkTableInstance::IsConnected() const {
|
||||
return ::nt::IsConnected(m_handle);
|
||||
}
|
||||
|
||||
inline const char* NetworkTableInstance::SavePersistent(
|
||||
StringRef filename) const {
|
||||
return ::nt::SavePersistent(m_handle, filename);
|
||||
}
|
||||
|
||||
inline const char* NetworkTableInstance::LoadPersistent(
|
||||
StringRef filename,
|
||||
std::function<void(size_t line, const char* msg)> warn) {
|
||||
return ::nt::LoadPersistent(m_handle, filename, warn);
|
||||
}
|
||||
|
||||
inline NT_Logger NetworkTableInstance::AddLogger(
|
||||
std::function<void(const LogMessage& msg)> func, unsigned int min_level,
|
||||
unsigned int max_level) {
|
||||
return ::nt::AddLogger(m_handle, func, min_level, max_level);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::RemoveLogger(NT_Logger logger) {
|
||||
::nt::RemoveLogger(logger);
|
||||
}
|
||||
|
||||
inline bool NetworkTableInstance::WaitForLoggerQueue(double timeout) {
|
||||
return ::nt::WaitForLoggerQueue(m_handle, timeout);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NT_INSTANCE_INL_
|
||||
29
src/main/native/include/networktables/NetworkTableType.h
Normal file
29
src/main/native/include/networktables/NetworkTableType.h
Normal 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_TYPE_H_
|
||||
#define NT_TYPE_H_
|
||||
|
||||
#include "ntcore_c.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
enum class NetworkTableType {
|
||||
kUnassigned = NT_UNASSIGNED,
|
||||
kBoolean = NT_BOOLEAN,
|
||||
kDouble = NT_DOUBLE,
|
||||
kString = NT_STRING,
|
||||
kRaw = NT_RAW,
|
||||
kBooleanArray = NT_BOOLEAN_ARRAY,
|
||||
kDoubleArray = NT_DOUBLE_ARRAY,
|
||||
kStringArray = NT_STRING_ARRAY,
|
||||
kRpc = NT_RPC
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NT_TYPE_H_
|
||||
404
src/main/native/include/networktables/NetworkTableValue.h
Normal file
404
src/main/native/include/networktables/NetworkTableValue.h
Normal file
@@ -0,0 +1,404 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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_VALUE_H_
|
||||
#define NT_VALUE_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "llvm/ArrayRef.h"
|
||||
#include "llvm/StringRef.h"
|
||||
|
||||
#include "ntcore_c.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
using llvm::ArrayRef;
|
||||
using llvm::StringRef;
|
||||
|
||||
/**
|
||||
* A network table entry value.
|
||||
*/
|
||||
class Value final {
|
||||
struct private_init {};
|
||||
|
||||
public:
|
||||
Value();
|
||||
Value(NT_Type type, unsigned long long time, const private_init&);
|
||||
~Value();
|
||||
|
||||
/**
|
||||
* Get the data type.
|
||||
* @return The type.
|
||||
*/
|
||||
NT_Type type() const { return m_val.type; }
|
||||
|
||||
/**
|
||||
* Get the data value stored.
|
||||
* @return The type.
|
||||
*/
|
||||
const NT_Value& value() const { return m_val; }
|
||||
|
||||
/**
|
||||
* Get the creation time of the value.
|
||||
* @return The time, in the units returned by nt::Now().
|
||||
*/
|
||||
unsigned long long last_change() const { return m_val.last_change; }
|
||||
|
||||
/**
|
||||
* Get the creation time of the value.
|
||||
* @return The time, in the units returned by nt::Now().
|
||||
*/
|
||||
unsigned long long time() const { return m_val.last_change; }
|
||||
|
||||
/**
|
||||
* @defgroup TypeCheckers Type Checkers
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a value or is unassigned.
|
||||
* @return True if the entry value contains a value.
|
||||
*/
|
||||
bool IsValid() const { return m_val.type != NT_UNASSIGNED; }
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a boolean.
|
||||
* @return True if the entry value is of boolean type.
|
||||
*/
|
||||
bool IsBoolean() const { return m_val.type == NT_BOOLEAN; }
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a double.
|
||||
* @return True if the entry value is of double type.
|
||||
*/
|
||||
bool IsDouble() const { return m_val.type == NT_DOUBLE; }
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a string.
|
||||
* @return True if the entry value is of string type.
|
||||
*/
|
||||
bool IsString() const { return m_val.type == NT_STRING; }
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a raw.
|
||||
* @return True if the entry value is of raw type.
|
||||
*/
|
||||
bool IsRaw() const { return m_val.type == NT_RAW; }
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a rpc definition.
|
||||
* @return True if the entry value is of rpc definition type.
|
||||
*/
|
||||
bool IsRpc() const { return m_val.type == NT_RPC; }
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a boolean array.
|
||||
* @return True if the entry value is of boolean array type.
|
||||
*/
|
||||
bool IsBooleanArray() const { return m_val.type == NT_BOOLEAN_ARRAY; }
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a double array.
|
||||
* @return True if the entry value is of double array type.
|
||||
*/
|
||||
bool IsDoubleArray() const { return m_val.type == NT_DOUBLE_ARRAY; }
|
||||
|
||||
/**
|
||||
* Determine if entry value contains a string array.
|
||||
* @return True if the entry value is of string array type.
|
||||
*/
|
||||
bool IsStringArray() const { return m_val.type == NT_STRING_ARRAY; }
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup TypeSafeGetters Type-Safe Getters
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the entry's boolean value.
|
||||
* @return The boolean value.
|
||||
*/
|
||||
bool GetBoolean() const {
|
||||
assert(m_val.type == NT_BOOLEAN);
|
||||
return m_val.data.v_boolean != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's double value.
|
||||
* @return The double value.
|
||||
*/
|
||||
double GetDouble() const {
|
||||
assert(m_val.type == NT_DOUBLE);
|
||||
return m_val.data.v_double;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's string value.
|
||||
* @return The string value.
|
||||
*/
|
||||
StringRef GetString() const {
|
||||
assert(m_val.type == NT_STRING);
|
||||
return m_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's raw value.
|
||||
* @return The raw value.
|
||||
*/
|
||||
StringRef GetRaw() const {
|
||||
assert(m_val.type == NT_RAW);
|
||||
return m_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's rpc definition value.
|
||||
* @return The rpc definition value.
|
||||
*/
|
||||
StringRef GetRpc() const {
|
||||
assert(m_val.type == NT_RPC);
|
||||
return m_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's boolean array value.
|
||||
* @return The boolean array value.
|
||||
*/
|
||||
ArrayRef<int> GetBooleanArray() const {
|
||||
assert(m_val.type == NT_BOOLEAN_ARRAY);
|
||||
return ArrayRef<int>(m_val.data.arr_boolean.arr,
|
||||
m_val.data.arr_boolean.size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's double array value.
|
||||
* @return The double array value.
|
||||
*/
|
||||
ArrayRef<double> GetDoubleArray() const {
|
||||
assert(m_val.type == NT_DOUBLE_ARRAY);
|
||||
return ArrayRef<double>(m_val.data.arr_double.arr,
|
||||
m_val.data.arr_double.size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry's string array value.
|
||||
* @return The string array value.
|
||||
*/
|
||||
ArrayRef<std::string> GetStringArray() const {
|
||||
assert(m_val.type == NT_STRING_ARRAY);
|
||||
return m_string_array;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup Factories Factory functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a boolean entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
static std::shared_ptr<Value> MakeBoolean(bool value,
|
||||
unsigned long long time = 0) {
|
||||
auto val = std::make_shared<Value>(NT_BOOLEAN, time, private_init());
|
||||
val->m_val.data.v_boolean = value;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a double entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
static std::shared_ptr<Value> MakeDouble(double value,
|
||||
unsigned long long time = 0) {
|
||||
auto val = std::make_shared<Value>(NT_DOUBLE, time, private_init());
|
||||
val->m_val.data.v_double = value;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
static std::shared_ptr<Value> MakeString(StringRef value,
|
||||
unsigned long long time = 0) {
|
||||
auto val = std::make_shared<Value>(NT_STRING, time, private_init());
|
||||
val->m_string = value;
|
||||
val->m_val.data.v_string.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_string.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
template <typename T,
|
||||
typename = std::enable_if_t<std::is_same<T, std::string>>>
|
||||
#else
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_same<T, std::string>::value>::type>
|
||||
#endif
|
||||
static std::shared_ptr<Value> MakeString(T&& value,
|
||||
unsigned long long time = 0) {
|
||||
auto val = std::make_shared<Value>(NT_STRING, time, private_init());
|
||||
val->m_string = std::move(value);
|
||||
val->m_val.data.v_string.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_string.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a raw entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
static std::shared_ptr<Value> MakeRaw(StringRef value,
|
||||
unsigned long long time = 0) {
|
||||
auto val = std::make_shared<Value>(NT_RAW, time, private_init());
|
||||
val->m_string = value;
|
||||
val->m_val.data.v_raw.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_raw.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a raw entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
template <typename T,
|
||||
typename = std::enable_if_t<std::is_same<T, std::string>>>
|
||||
#else
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_same<T, std::string>::value>::type>
|
||||
#endif
|
||||
static std::shared_ptr<Value> MakeRaw(T&& value,
|
||||
unsigned long long time = 0) {
|
||||
auto val = std::make_shared<Value>(NT_RAW, time, private_init());
|
||||
val->m_string = std::move(value);
|
||||
val->m_val.data.v_raw.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_raw.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rpc entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
static std::shared_ptr<Value> MakeRpc(StringRef value,
|
||||
unsigned long long time = 0) {
|
||||
auto val = std::make_shared<Value>(NT_RPC, time, private_init());
|
||||
val->m_string = value;
|
||||
val->m_val.data.v_raw.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_raw.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rpc entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
template <typename T>
|
||||
static std::shared_ptr<Value> MakeRpc(T&& value,
|
||||
unsigned long long time = 0) {
|
||||
auto val = std::make_shared<Value>(NT_RPC, time, private_init());
|
||||
val->m_string = std::move(value);
|
||||
val->m_val.data.v_raw.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_raw.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boolean array entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
static std::shared_ptr<Value> MakeBooleanArray(ArrayRef<int> value,
|
||||
unsigned long long time = 0);
|
||||
|
||||
/**
|
||||
* Creates a double array entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
static std::shared_ptr<Value> MakeDoubleArray(ArrayRef<double> value,
|
||||
unsigned long long time = 0);
|
||||
|
||||
/**
|
||||
* Creates a string array entry value.
|
||||
* @param value the value
|
||||
* @param time if nonzero, the creation time to use (instead of the current
|
||||
* time)
|
||||
* @return The entry value
|
||||
*/
|
||||
static std::shared_ptr<Value> MakeStringArray(ArrayRef<std::string> value,
|
||||
unsigned long long time = 0);
|
||||
|
||||
// Note: This function moves the values out of the vector.
|
||||
static std::shared_ptr<Value> MakeStringArray(
|
||||
std::vector<std::string>&& value, unsigned long long time = 0);
|
||||
|
||||
/** @} */
|
||||
|
||||
Value(const Value&) = delete;
|
||||
Value& operator=(const Value&) = delete;
|
||||
friend bool operator==(const Value& lhs, const Value& rhs);
|
||||
|
||||
private:
|
||||
NT_Value m_val;
|
||||
std::string m_string;
|
||||
std::vector<std::string> m_string_array;
|
||||
};
|
||||
|
||||
bool operator==(const Value& lhs, const Value& rhs);
|
||||
inline bool operator!=(const Value& lhs, const Value& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/** NetworkTable Value alias for similarity with Java. */
|
||||
typedef Value NetworkTableValue;
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NT_VALUE_H_
|
||||
101
src/main/native/include/networktables/RpcCall.h
Normal file
101
src/main/native/include/networktables/RpcCall.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NT_RPCCALL_H_
|
||||
#define NT_RPCCALL_H_
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "ntcore_c.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class NetworkTableEntry;
|
||||
|
||||
/** NetworkTables Remote Procedure Call */
|
||||
class RpcCall final {
|
||||
public:
|
||||
/**
|
||||
* Construct invalid instance.
|
||||
*/
|
||||
RpcCall() : m_entry(0), m_call(0) {}
|
||||
|
||||
/**
|
||||
* Construct from native handles.
|
||||
* @param entry Entry handle
|
||||
* @param call Call handle
|
||||
*/
|
||||
RpcCall(NT_Entry entry, NT_RpcCall call)
|
||||
: m_entry(entry), m_call(call) {}
|
||||
|
||||
RpcCall(RpcCall&& other);
|
||||
RpcCall(const RpcCall&) = delete;
|
||||
RpcCall& operator=(const RpcCall&) = delete;
|
||||
|
||||
/**
|
||||
* Destructor. Cancels the result if no other action taken.
|
||||
*/
|
||||
~RpcCall();
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
explicit operator bool() const { return m_call != 0; }
|
||||
|
||||
/**
|
||||
* Get the RPC entry.
|
||||
* @return NetworkTableEntry for the RPC.
|
||||
*/
|
||||
NetworkTableEntry GetEntry() const;
|
||||
|
||||
/**
|
||||
* Get the call native handle.
|
||||
* @return Native handle.
|
||||
*/
|
||||
NT_RpcCall GetCall() const { return m_call; }
|
||||
|
||||
/**
|
||||
* Get the result (return value). This function blocks until
|
||||
* the result is received.
|
||||
* @param result received result (output)
|
||||
* @return False on error, true otherwise.
|
||||
*/
|
||||
bool GetResult(std::string* result);
|
||||
|
||||
/**
|
||||
* Get the result (return value). This function blocks until
|
||||
* the result is received or it times out.
|
||||
* @param result received result (output)
|
||||
* @param timeout timeout, in seconds
|
||||
* @param timed_out true if the timeout period elapsed (output)
|
||||
* @return False on error or timeout, true otherwise.
|
||||
*/
|
||||
bool GetResult(std::string* result, double timeout, bool* timed_out);
|
||||
|
||||
/**
|
||||
* Ignore the result. This function is non-blocking.
|
||||
*/
|
||||
void CancelResult();
|
||||
|
||||
friend void swap(RpcCall& first, RpcCall& second) {
|
||||
using std::swap;
|
||||
swap(first.m_entry, second.m_entry);
|
||||
swap(first.m_call, second.m_call);
|
||||
}
|
||||
|
||||
private:
|
||||
NT_Entry m_entry;
|
||||
NT_RpcCall m_call;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#include "networktables/RpcCall.inl"
|
||||
|
||||
#endif // NT_RPCCALL_H_
|
||||
48
src/main/native/include/networktables/RpcCall.inl
Normal file
48
src/main/native/include/networktables/RpcCall.inl
Normal file
@@ -0,0 +1,48 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NT_RPCCALL_INL_
|
||||
#define NT_RPCCALL_INL_
|
||||
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
inline RpcCall::RpcCall(RpcCall&& other) : RpcCall() {
|
||||
swap(*this, other);
|
||||
}
|
||||
|
||||
inline RpcCall::~RpcCall() {
|
||||
// automatically cancel result if user didn't request it
|
||||
if (m_call != 0) CancelResult();
|
||||
}
|
||||
|
||||
inline bool RpcCall::GetResult(std::string* result) {
|
||||
if (GetRpcResult(m_entry, m_call, result)) {
|
||||
m_call = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool RpcCall::GetResult(std::string* result, double timeout,
|
||||
bool* timed_out) {
|
||||
if (GetRpcResult(m_entry, m_call, result, timeout, timed_out)) {
|
||||
m_call = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void RpcCall::CancelResult() {
|
||||
CancelRpcResult(m_entry, m_call);
|
||||
m_call = 0;
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NT_RPCCALL_INL_
|
||||
43
src/main/native/include/networktables/TableEntryListener.h
Normal file
43
src/main/native/include/networktables/TableEntryListener.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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_TABLEENTRYLISTENER_H_
|
||||
#define NT_TABLEENTRYLISTENER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "llvm/StringRef.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class NetworkTable;
|
||||
class NetworkTableEntry;
|
||||
class Value;
|
||||
|
||||
using llvm::StringRef;
|
||||
|
||||
/**
|
||||
* A listener that listens to changes in values in a NetworkTable.
|
||||
*
|
||||
* Called when a key-value pair is changed in a NetworkTable.
|
||||
*
|
||||
* @param table the table the key-value pair exists in
|
||||
* @param key the key associated with the value that changed
|
||||
* @param entry the entry associated with the value that changed
|
||||
* @param value the new value
|
||||
* @param flags update flags; for example, EntryListenerFlags.kNew if the key
|
||||
* did not previously exist
|
||||
*/
|
||||
typedef std::function<void(NetworkTable* table, StringRef name,
|
||||
NetworkTableEntry entry,
|
||||
std::shared_ptr<Value> value, int flags)>
|
||||
TableEntryListener;
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NT_TABLEENTRYLISTENER_H_
|
||||
37
src/main/native/include/networktables/TableListener.h
Normal file
37
src/main/native/include/networktables/TableListener.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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_TABLELISTENER_H_
|
||||
#define NT_TABLELISTENER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "llvm/StringRef.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class NetworkTable;
|
||||
|
||||
using llvm::StringRef;
|
||||
|
||||
/**
|
||||
* A listener that listens to new sub-tables in a NetworkTable.
|
||||
*
|
||||
* Called when a new table is created.
|
||||
*
|
||||
* @param parent the parent of the table
|
||||
* @param name the name of the new table
|
||||
* @param table the new table
|
||||
*/
|
||||
typedef std::function<void(NetworkTable* parent, StringRef name,
|
||||
NetworkTable* table)>
|
||||
TableListener;
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NT_TABLELISTENER_H_
|
||||
@@ -1,183 +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_VALUE_H_
|
||||
#define NT_VALUE_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "llvm/ArrayRef.h"
|
||||
#include "llvm/StringRef.h"
|
||||
|
||||
#include "ntcore_c.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
using llvm::ArrayRef;
|
||||
using llvm::StringRef;
|
||||
|
||||
/** NetworkTables Entry Value */
|
||||
class Value {
|
||||
struct private_init {};
|
||||
|
||||
public:
|
||||
Value();
|
||||
Value(NT_Type type, const private_init&);
|
||||
~Value();
|
||||
|
||||
NT_Type type() const { return m_val.type; }
|
||||
const NT_Value& value() const { return m_val; }
|
||||
unsigned long long last_change() const { return m_val.last_change; }
|
||||
|
||||
/*
|
||||
* Type Checkers
|
||||
*/
|
||||
bool IsBoolean() const { return m_val.type == NT_BOOLEAN; }
|
||||
bool IsDouble() const { return m_val.type == NT_DOUBLE; }
|
||||
bool IsString() const { return m_val.type == NT_STRING; }
|
||||
bool IsRaw() const { return m_val.type == NT_RAW; }
|
||||
bool IsRpc() const { return m_val.type == NT_RPC; }
|
||||
bool IsBooleanArray() const { return m_val.type == NT_BOOLEAN_ARRAY; }
|
||||
bool IsDoubleArray() const { return m_val.type == NT_DOUBLE_ARRAY; }
|
||||
bool IsStringArray() const { return m_val.type == NT_STRING_ARRAY; }
|
||||
|
||||
/*
|
||||
* Type-Safe Getters
|
||||
*/
|
||||
bool GetBoolean() const {
|
||||
assert(m_val.type == NT_BOOLEAN);
|
||||
return m_val.data.v_boolean != 0;
|
||||
}
|
||||
double GetDouble() const {
|
||||
assert(m_val.type == NT_DOUBLE);
|
||||
return m_val.data.v_double;
|
||||
}
|
||||
StringRef GetString() const {
|
||||
assert(m_val.type == NT_STRING);
|
||||
return m_string;
|
||||
}
|
||||
StringRef GetRaw() const {
|
||||
assert(m_val.type == NT_RAW);
|
||||
return m_string;
|
||||
}
|
||||
StringRef GetRpc() const {
|
||||
assert(m_val.type == NT_RPC);
|
||||
return m_string;
|
||||
}
|
||||
ArrayRef<int> GetBooleanArray() const {
|
||||
assert(m_val.type == NT_BOOLEAN_ARRAY);
|
||||
return ArrayRef<int>(m_val.data.arr_boolean.arr,
|
||||
m_val.data.arr_boolean.size);
|
||||
}
|
||||
ArrayRef<double> GetDoubleArray() const {
|
||||
assert(m_val.type == NT_DOUBLE_ARRAY);
|
||||
return ArrayRef<double>(m_val.data.arr_double.arr,
|
||||
m_val.data.arr_double.size);
|
||||
}
|
||||
ArrayRef<std::string> GetStringArray() const {
|
||||
assert(m_val.type == NT_STRING_ARRAY);
|
||||
return m_string_array;
|
||||
}
|
||||
|
||||
static std::shared_ptr<Value> MakeBoolean(bool value) {
|
||||
auto val = std::make_shared<Value>(NT_BOOLEAN, private_init());
|
||||
val->m_val.data.v_boolean = value;
|
||||
return val;
|
||||
}
|
||||
static std::shared_ptr<Value> MakeDouble(double value) {
|
||||
auto val = std::make_shared<Value>(NT_DOUBLE, private_init());
|
||||
val->m_val.data.v_double = value;
|
||||
return val;
|
||||
}
|
||||
static std::shared_ptr<Value> MakeString(StringRef value) {
|
||||
auto val = std::make_shared<Value>(NT_STRING, private_init());
|
||||
val->m_string = value;
|
||||
val->m_val.data.v_string.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_string.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
template <typename T,
|
||||
typename = std::enable_if_t<std::is_same<T, std::string>>>
|
||||
#else
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_same<T, std::string>::value>::type>
|
||||
#endif
|
||||
static std::shared_ptr<Value> MakeString(T&& value) {
|
||||
auto val = std::make_shared<Value>(NT_STRING, private_init());
|
||||
val->m_string = std::move(value);
|
||||
val->m_val.data.v_string.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_string.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
static std::shared_ptr<Value> MakeRaw(StringRef value) {
|
||||
auto val = std::make_shared<Value>(NT_RAW, private_init());
|
||||
val->m_string = value;
|
||||
val->m_val.data.v_raw.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_raw.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
template <typename T,
|
||||
typename = std::enable_if_t<std::is_same<T, std::string>>>
|
||||
#else
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_same<T, std::string>::value>::type>
|
||||
#endif
|
||||
static std::shared_ptr<Value> MakeRaw(T&& value) {
|
||||
auto val = std::make_shared<Value>(NT_RAW, private_init());
|
||||
val->m_string = std::move(value);
|
||||
val->m_val.data.v_raw.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_raw.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
static std::shared_ptr<Value> MakeRpc(StringRef value) {
|
||||
auto val = std::make_shared<Value>(NT_RPC, private_init());
|
||||
val->m_string = value;
|
||||
val->m_val.data.v_raw.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_raw.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
template <typename T>
|
||||
static std::shared_ptr<Value> MakeRpc(T&& value) {
|
||||
auto val = std::make_shared<Value>(NT_RPC, private_init());
|
||||
val->m_string = std::move(value);
|
||||
val->m_val.data.v_raw.str = const_cast<char*>(val->m_string.c_str());
|
||||
val->m_val.data.v_raw.len = val->m_string.size();
|
||||
return val;
|
||||
}
|
||||
|
||||
static std::shared_ptr<Value> MakeBooleanArray(ArrayRef<int> value);
|
||||
static std::shared_ptr<Value> MakeDoubleArray(ArrayRef<double> value);
|
||||
static std::shared_ptr<Value> MakeStringArray(ArrayRef<std::string> value);
|
||||
|
||||
// Note: This function moves the values out of the vector.
|
||||
static std::shared_ptr<Value> MakeStringArray(
|
||||
std::vector<std::string>&& value);
|
||||
|
||||
Value(const Value&) = delete;
|
||||
Value& operator=(const Value&) = delete;
|
||||
friend bool operator==(const Value& lhs, const Value& rhs);
|
||||
|
||||
private:
|
||||
NT_Value m_val;
|
||||
std::string m_string;
|
||||
std::vector<std::string> m_string_array;
|
||||
};
|
||||
|
||||
bool operator==(const Value& lhs, const Value& rhs);
|
||||
inline bool operator!=(const Value& lhs, const Value& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NT_VALUE_H_
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -11,15 +11,19 @@
|
||||
#include <memory>
|
||||
|
||||
#include "llvm/StringRef.h"
|
||||
#include "nt_Value.h"
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
#include "support/deprecated.h"
|
||||
|
||||
namespace nt {
|
||||
class NetworkTable;
|
||||
} // namespace nt
|
||||
|
||||
class ITableListener;
|
||||
|
||||
/**
|
||||
* A table whose values can be read and written to
|
||||
*/
|
||||
class ITable {
|
||||
class WPI_DEPRECATED("Use NetworkTable directly") ITable {
|
||||
public:
|
||||
/**
|
||||
* Determines whether the given key is in this table.
|
||||
@@ -45,7 +49,7 @@ class ITable {
|
||||
* @param key the name of the table relative to this one
|
||||
* @return a sub table relative to this one
|
||||
*/
|
||||
virtual std::shared_ptr<ITable> GetSubTable(llvm::StringRef key) const = 0;
|
||||
virtual std::shared_ptr<nt::NetworkTable> GetSubTable(llvm::StringRef key) const = 0;
|
||||
|
||||
/**
|
||||
* @param types bitmask of types; 0 is treated as a "don't care".
|
||||
|
||||
@@ -8,14 +8,22 @@
|
||||
#include <memory>
|
||||
|
||||
#include "llvm/StringRef.h"
|
||||
#include "nt_Value.h"
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
#include "support/deprecated.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
class ITable;
|
||||
|
||||
/**
|
||||
* A listener that listens to changes in values in a {@link ITable}
|
||||
*/
|
||||
class ITableListener {
|
||||
class WPI_DEPRECATED(
|
||||
"Use EntryListener, TableEntryListener, or TableListener as appropriate")
|
||||
ITableListener {
|
||||
public:
|
||||
virtual ~ITableListener() = default;
|
||||
/**
|
||||
@@ -44,4 +52,8 @@ class ITableListener {
|
||||
unsigned int flags);
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif /* ITABLELISTENER_H_ */
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class ConnectionListenerTest extends TestCase {
|
||||
|
||||
NetworkTableInstance serverInst;
|
||||
NetworkTableInstance clientInst;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
serverInst = NetworkTableInstance.create();
|
||||
serverInst.setNetworkIdentity("server");
|
||||
|
||||
clientInst = NetworkTableInstance.create();
|
||||
clientInst.setNetworkIdentity("client");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
clientInst.free();
|
||||
serverInst.free();
|
||||
}
|
||||
|
||||
private void connect() {
|
||||
serverInst.startServer("connectionlistenertest.ini", "127.0.0.1", 10000);
|
||||
clientInst.startClient("127.0.0.1", 10000);
|
||||
|
||||
// wait for client to report it's started, then wait another 0.1 sec
|
||||
try {
|
||||
while ((clientInst.getNetworkMode() & NetworkTableInstance.kNetModeStarting) != 0) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ex) {
|
||||
fail("interrupted while waiting for client to start");
|
||||
}
|
||||
}
|
||||
|
||||
public void testJNI() {
|
||||
// set up the poller
|
||||
int poller = NetworkTablesJNI.createConnectionListenerPoller(serverInst.getHandle());
|
||||
assertTrue("bad poller handle", poller != 0);
|
||||
int handle = NetworkTablesJNI.addPolledConnectionListener(poller, false);
|
||||
assertTrue("bad listener handle", handle != 0);
|
||||
|
||||
// trigger a connect event
|
||||
connect();
|
||||
|
||||
// get the event
|
||||
assertTrue(serverInst.waitForConnectionListenerQueue(1.0));
|
||||
ConnectionNotification[] events = null;
|
||||
try {
|
||||
events = NetworkTablesJNI.pollConnectionListenerTimeout(serverInst, poller, 0.0);
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
fail("unexpected interrupted exception" + ex);
|
||||
}
|
||||
|
||||
assertNotNull(events);
|
||||
assertEquals(events.length, 1);
|
||||
assertEquals(handle, events[0].listener);
|
||||
assertTrue(events[0].connected);
|
||||
|
||||
// trigger a disconnect event
|
||||
clientInst.stopClient();
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ex) {
|
||||
fail("interrupted while waiting for client to stop");
|
||||
}
|
||||
|
||||
// get the event
|
||||
assertTrue(serverInst.waitForConnectionListenerQueue(1.0));
|
||||
try {
|
||||
events = NetworkTablesJNI.pollConnectionListenerTimeout(serverInst, poller, 0.0);
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
fail("unexpected interrupted exception" + ex);
|
||||
}
|
||||
|
||||
assertNotNull(events);
|
||||
assertEquals(events.length, 1);
|
||||
assertEquals(handle, events[0].listener);
|
||||
assertFalse(events[0].connected);
|
||||
|
||||
}
|
||||
|
||||
public void testThreaded() {
|
||||
serverInst.startServer("connectionlistenertest.ini", "127.0.0.1", 10000);
|
||||
List<ConnectionNotification> events = new ArrayList<>();
|
||||
int handle = serverInst.addConnectionListener((event) -> events.add(event), false);
|
||||
|
||||
// trigger a connect event
|
||||
clientInst.startClient("127.0.0.1", 10000);
|
||||
|
||||
// wait for client to report it's started, then wait another 0.1 sec
|
||||
try {
|
||||
while ((clientInst.getNetworkMode() & NetworkTableInstance.kNetModeStarting) != 0) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ex) {
|
||||
fail("interrupted while waiting for client to start");
|
||||
}
|
||||
assertTrue(serverInst.waitForConnectionListenerQueue(1.0));
|
||||
|
||||
// get the event
|
||||
assertEquals(events.size(), 1);
|
||||
assertEquals(handle, events.get(0).listener);
|
||||
assertTrue(events.get(0).connected);
|
||||
events.clear();
|
||||
|
||||
// trigger a disconnect event
|
||||
clientInst.stopClient();
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ex) {
|
||||
fail("interrupted while waiting for client to stop");
|
||||
}
|
||||
|
||||
// get the event
|
||||
assertTrue(serverInst.waitForConnectionListenerQueue(1.0));
|
||||
assertEquals(events.size(), 1);
|
||||
assertEquals(handle, events.get(0).listener);
|
||||
assertFalse(events.get(0).connected);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class EntryListenerTest extends TestCase {
|
||||
|
||||
NetworkTableInstance serverInst;
|
||||
NetworkTableInstance clientInst;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
serverInst = NetworkTableInstance.create();
|
||||
serverInst.setNetworkIdentity("server");
|
||||
|
||||
clientInst = NetworkTableInstance.create();
|
||||
clientInst.setNetworkIdentity("client");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
clientInst.free();
|
||||
serverInst.free();
|
||||
}
|
||||
|
||||
private void connect() {
|
||||
serverInst.startServer("connectionlistenertest.ini", "127.0.0.1", 10000);
|
||||
clientInst.startClient("127.0.0.1", 10000);
|
||||
|
||||
// Use connection listener to ensure we've connected
|
||||
int poller = NetworkTablesJNI.createConnectionListenerPoller(clientInst.getHandle());
|
||||
NetworkTablesJNI.addPolledConnectionListener(poller, false);
|
||||
try {
|
||||
if (NetworkTablesJNI.pollConnectionListenerTimeout(clientInst, poller, 1.0).length == 0) {
|
||||
fail("client didn't connect to server");
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
fail("interrupted while waiting for server connection");
|
||||
}
|
||||
}
|
||||
|
||||
public void testPrefixNewRemote() {
|
||||
connect();
|
||||
List<EntryNotification> events = new ArrayList<>();
|
||||
int handle = serverInst.addEntryListener("/foo", (event) -> events.add(event),
|
||||
EntryListenerFlags.kNew);
|
||||
|
||||
// Trigger an event
|
||||
clientInst.getEntry("/foo/bar").setDouble(1.0);
|
||||
clientInst.getEntry("/baz").setDouble(1.0);
|
||||
clientInst.flush();
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ex) {
|
||||
fail("interrupted while waiting for entries to update");
|
||||
}
|
||||
|
||||
assertTrue(serverInst.waitForEntryListenerQueue(1.0));
|
||||
|
||||
// Check the event
|
||||
assertEquals(events.size(), 1);
|
||||
assertEquals(events.get(0).listener, handle);
|
||||
assertEquals(events.get(0).getEntry(), serverInst.getEntry("/foo/bar"));
|
||||
assertEquals(events.get(0).name, "/foo/bar");
|
||||
assertEquals(events.get(0).value, NetworkTableValue.makeDouble(1.0));
|
||||
assertEquals(events.get(0).flags, EntryListenerFlags.kNew);
|
||||
}
|
||||
}
|
||||
12
src/test/java/edu/wpi/first/networktables/JNITest.java
Normal file
12
src/test/java/edu/wpi/first/networktables/JNITest.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class JNITest {
|
||||
@Test
|
||||
public void jniLinkTest() {
|
||||
// Test to verify that the JNI test link works correctly.
|
||||
int inst = NetworkTablesJNI.getDefaultInstance();
|
||||
NetworkTablesJNI.flush(inst);
|
||||
}
|
||||
}
|
||||
39
src/test/java/edu/wpi/first/networktables/LoggerTest.java
Normal file
39
src/test/java/edu/wpi/first/networktables/LoggerTest.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class LoggerTest extends TestCase {
|
||||
|
||||
NetworkTableInstance clientInst;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
clientInst = NetworkTableInstance.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
clientInst.free();
|
||||
}
|
||||
|
||||
public void testLogger() {
|
||||
List<LogMessage> msgs = new ArrayList<>();
|
||||
clientInst.addLogger((msg) -> msgs.add(msg), LogMessage.kInfo, 100);
|
||||
|
||||
clientInst.startClient("127.0.0.1", 10000);
|
||||
|
||||
// wait for client to report it's started, then wait another 0.1 sec
|
||||
try {
|
||||
while ((clientInst.getNetworkMode() & NetworkTableInstance.kNetModeStarting) != 0) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ex) {
|
||||
fail("interrupted while waiting for client to start");
|
||||
}
|
||||
|
||||
assertFalse(msgs.isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package edu.wpi.first.wpilibj.networktables;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class JNITest {
|
||||
@Test
|
||||
public void jniLinkTest() {
|
||||
// Test to verify that the JNI test link works correctly.
|
||||
edu.wpi.first.wpilibj.networktables.NetworkTablesJNI.flush();
|
||||
}
|
||||
}
|
||||
109
src/test/native/cpp/ConnectionListenerTest.cpp
Normal file
109
src/test/native/cpp/ConnectionListenerTest.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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 "ntcore_cpp.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "TestPrinters.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
class ConnectionListenerTest : public ::testing::Test {
|
||||
public:
|
||||
ConnectionListenerTest()
|
||||
: server_inst(nt::CreateInstance()), client_inst(nt::CreateInstance()) {
|
||||
nt::SetNetworkIdentity(server_inst, "server");
|
||||
nt::SetNetworkIdentity(client_inst, "client");
|
||||
}
|
||||
|
||||
~ConnectionListenerTest() override {
|
||||
nt::DestroyInstance(server_inst);
|
||||
nt::DestroyInstance(client_inst);
|
||||
}
|
||||
|
||||
void Connect();
|
||||
|
||||
protected:
|
||||
NT_Inst server_inst;
|
||||
NT_Inst client_inst;
|
||||
};
|
||||
|
||||
void ConnectionListenerTest::Connect() {
|
||||
nt::StartServer(server_inst, "connectionlistenertest.ini", "127.0.0.1",
|
||||
10000);
|
||||
nt::StartClient(client_inst, "127.0.0.1", 10000);
|
||||
|
||||
// wait for client to report it's started, then wait another 0.1 sec
|
||||
while ((nt::GetNetworkMode(client_inst) & NT_NET_MODE_STARTING) != 0)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
TEST_F(ConnectionListenerTest, Polled) {
|
||||
// set up the poller
|
||||
NT_ConnectionListenerPoller poller =
|
||||
nt::CreateConnectionListenerPoller(server_inst);
|
||||
ASSERT_NE(poller, 0u);
|
||||
NT_ConnectionListener handle = nt::AddPolledConnectionListener(poller, false);
|
||||
ASSERT_NE(handle, 0u);
|
||||
|
||||
// trigger a connect event
|
||||
Connect();
|
||||
|
||||
// get the event
|
||||
ASSERT_TRUE(nt::WaitForConnectionListenerQueue(server_inst, 1.0));
|
||||
bool timed_out = false;
|
||||
auto result = nt::PollConnectionListener(poller, 0.1, &timed_out);
|
||||
EXPECT_FALSE(timed_out);
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_TRUE(result[0].connected);
|
||||
|
||||
// trigger a disconnect event
|
||||
nt::StopClient(client_inst);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
// get the event
|
||||
ASSERT_TRUE(nt::WaitForConnectionListenerQueue(server_inst, 1.0));
|
||||
timed_out = false;
|
||||
result = nt::PollConnectionListener(poller, 0.1, &timed_out);
|
||||
EXPECT_FALSE(timed_out);
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_FALSE(result[0].connected);
|
||||
|
||||
// trigger a disconnect event
|
||||
}
|
||||
|
||||
TEST_F(ConnectionListenerTest, Threaded) {
|
||||
std::vector<nt::ConnectionNotification> result;
|
||||
auto handle = nt::AddConnectionListener(
|
||||
server_inst,
|
||||
[&](const nt::ConnectionNotification& event) { result.push_back(event); },
|
||||
false);
|
||||
|
||||
// trigger a connect event
|
||||
Connect();
|
||||
|
||||
ASSERT_TRUE(nt::WaitForConnectionListenerQueue(server_inst, 1.0));
|
||||
|
||||
// get the event
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_TRUE(result[0].connected);
|
||||
result.clear();
|
||||
|
||||
// trigger a disconnect event
|
||||
nt::StopClient(client_inst);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
// get the event
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_FALSE(result[0].connected);
|
||||
}
|
||||
165
src/test/native/cpp/EntryListenerTest.cpp
Normal file
165
src/test/native/cpp/EntryListenerTest.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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 "ntcore_cpp.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "TestPrinters.h"
|
||||
#include "ValueMatcher.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
class EntryListenerTest : public ::testing::Test {
|
||||
public:
|
||||
EntryListenerTest()
|
||||
: server_inst(nt::CreateInstance()), client_inst(nt::CreateInstance()) {
|
||||
nt::SetNetworkIdentity(server_inst, "server");
|
||||
nt::SetNetworkIdentity(client_inst, "client");
|
||||
#if 0
|
||||
nt::AddLogger(server_inst,
|
||||
[](const nt::LogMessage& msg) {
|
||||
std::fprintf(stderr, "SERVER: %s\n", msg.message.c_str());
|
||||
},
|
||||
0, UINT_MAX);
|
||||
nt::AddLogger(client_inst,
|
||||
[](const nt::LogMessage& msg) {
|
||||
std::fprintf(stderr, "CLIENT: %s\n", msg.message.c_str());
|
||||
},
|
||||
0, UINT_MAX);
|
||||
#endif
|
||||
}
|
||||
|
||||
~EntryListenerTest() override {
|
||||
nt::DestroyInstance(server_inst);
|
||||
nt::DestroyInstance(client_inst);
|
||||
}
|
||||
|
||||
void Connect();
|
||||
|
||||
protected:
|
||||
NT_Inst server_inst;
|
||||
NT_Inst client_inst;
|
||||
};
|
||||
|
||||
void EntryListenerTest::Connect() {
|
||||
nt::StartServer(server_inst, "entrylistenertest.ini", "127.0.0.1", 10000);
|
||||
nt::StartClient(client_inst, "127.0.0.1", 10000);
|
||||
|
||||
// Use connection listener to ensure we've connected
|
||||
NT_ConnectionListenerPoller poller =
|
||||
nt::CreateConnectionListenerPoller(server_inst);
|
||||
nt::AddPolledConnectionListener(poller, false);
|
||||
bool timed_out = false;
|
||||
if (nt::PollConnectionListener(poller, 1.0, &timed_out).empty()) {
|
||||
FAIL() << "client didn't connect to server";
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(EntryListenerTest, EntryNewLocal) {
|
||||
std::vector<nt::EntryNotification> events;
|
||||
auto handle = nt::AddEntryListener(
|
||||
nt::GetEntry(server_inst, "/foo"),
|
||||
[&](const nt::EntryNotification& event) { events.push_back(event); },
|
||||
NT_NOTIFY_NEW | NT_NOTIFY_LOCAL);
|
||||
|
||||
// Trigger an event
|
||||
nt::SetEntryValue(nt::GetEntry(server_inst, "/foo/bar"),
|
||||
nt::Value::MakeDouble(2.0));
|
||||
nt::SetEntryValue(nt::GetEntry(server_inst, "/foo"),
|
||||
nt::Value::MakeDouble(1.0));
|
||||
|
||||
ASSERT_TRUE(nt::WaitForEntryListenerQueue(server_inst, 1.0));
|
||||
|
||||
// Check the event
|
||||
ASSERT_EQ(events.size(), 1u);
|
||||
ASSERT_EQ(events[0].listener, handle);
|
||||
ASSERT_EQ(events[0].entry, nt::GetEntry(server_inst, "/foo"));
|
||||
ASSERT_EQ(events[0].name, "/foo");
|
||||
ASSERT_THAT(events[0].value, nt::ValueEq(nt::Value::MakeDouble(1.0)));
|
||||
ASSERT_EQ(events[0].flags, (unsigned int)(NT_NOTIFY_NEW | NT_NOTIFY_LOCAL));
|
||||
}
|
||||
|
||||
TEST_F(EntryListenerTest, EntryNewRemote) {
|
||||
Connect();
|
||||
if (HasFatalFailure()) return;
|
||||
std::vector<nt::EntryNotification> events;
|
||||
auto handle = nt::AddEntryListener(
|
||||
nt::GetEntry(server_inst, "/foo"),
|
||||
[&](const nt::EntryNotification& event) { events.push_back(event); },
|
||||
NT_NOTIFY_NEW);
|
||||
|
||||
// Trigger an event
|
||||
nt::SetEntryValue(nt::GetEntry(client_inst, "/foo/bar"),
|
||||
nt::Value::MakeDouble(2.0));
|
||||
nt::SetEntryValue(nt::GetEntry(client_inst, "/foo"),
|
||||
nt::Value::MakeDouble(1.0));
|
||||
nt::Flush(client_inst);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
ASSERT_TRUE(nt::WaitForEntryListenerQueue(server_inst, 1.0));
|
||||
|
||||
// Check the event
|
||||
ASSERT_EQ(events.size(), 1u);
|
||||
ASSERT_EQ(events[0].listener, handle);
|
||||
ASSERT_EQ(events[0].entry, nt::GetEntry(server_inst, "/foo"));
|
||||
ASSERT_EQ(events[0].name, "/foo");
|
||||
ASSERT_THAT(events[0].value, nt::ValueEq(nt::Value::MakeDouble(1.0)));
|
||||
ASSERT_EQ(events[0].flags, NT_NOTIFY_NEW);
|
||||
}
|
||||
|
||||
TEST_F(EntryListenerTest, PrefixNewLocal) {
|
||||
std::vector<nt::EntryNotification> events;
|
||||
auto handle = nt::AddEntryListener(
|
||||
server_inst, "/foo",
|
||||
[&](const nt::EntryNotification& event) { events.push_back(event); },
|
||||
NT_NOTIFY_NEW | NT_NOTIFY_LOCAL);
|
||||
|
||||
// Trigger an event
|
||||
nt::SetEntryValue(nt::GetEntry(server_inst, "/foo/bar"),
|
||||
nt::Value::MakeDouble(1.0));
|
||||
nt::SetEntryValue(nt::GetEntry(server_inst, "/baz"),
|
||||
nt::Value::MakeDouble(1.0));
|
||||
|
||||
ASSERT_TRUE(nt::WaitForEntryListenerQueue(server_inst, 1.0));
|
||||
|
||||
// Check the event
|
||||
ASSERT_EQ(events.size(), 1u);
|
||||
ASSERT_EQ(events[0].listener, handle);
|
||||
ASSERT_EQ(events[0].entry, nt::GetEntry(server_inst, "/foo/bar"));
|
||||
ASSERT_EQ(events[0].name, "/foo/bar");
|
||||
ASSERT_THAT(events[0].value, nt::ValueEq(nt::Value::MakeDouble(1.0)));
|
||||
ASSERT_EQ(events[0].flags, (unsigned int)(NT_NOTIFY_NEW | NT_NOTIFY_LOCAL));
|
||||
}
|
||||
|
||||
TEST_F(EntryListenerTest, PrefixNewRemote) {
|
||||
Connect();
|
||||
if (HasFatalFailure()) return;
|
||||
std::vector<nt::EntryNotification> events;
|
||||
auto handle = nt::AddEntryListener(
|
||||
server_inst, "/foo",
|
||||
[&](const nt::EntryNotification& event) { events.push_back(event); },
|
||||
NT_NOTIFY_NEW);
|
||||
|
||||
// Trigger an event
|
||||
nt::SetEntryValue(nt::GetEntry(client_inst, "/foo/bar"),
|
||||
nt::Value::MakeDouble(1.0));
|
||||
nt::SetEntryValue(nt::GetEntry(client_inst, "/baz"),
|
||||
nt::Value::MakeDouble(1.0));
|
||||
nt::Flush(client_inst);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
ASSERT_TRUE(nt::WaitForEntryListenerQueue(server_inst, 1.0));
|
||||
|
||||
// Check the event
|
||||
ASSERT_EQ(events.size(), 1u);
|
||||
ASSERT_EQ(events[0].listener, handle);
|
||||
ASSERT_EQ(events[0].entry, nt::GetEntry(server_inst, "/foo/bar"));
|
||||
ASSERT_EQ(events[0].name, "/foo/bar");
|
||||
ASSERT_THAT(events[0].value, nt::ValueEq(nt::Value::MakeDouble(1.0)));
|
||||
ASSERT_EQ(events[0].flags, NT_NOTIFY_NEW);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user