Initial checkin of unified hierarchy of WPILib 2015

This commit is contained in:
Brad Miller
2013-12-15 18:30:16 -05:00
commit 3178911eef
1560 changed files with 410007 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>edu.wpi.first.wpilib.networktables.cpp</groupId>
<artifactId>parent</artifactId>
<packaging>pom</packaging>
<version>0.1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>1.0-alpha-7</version>
<extensions>true</extensions>
<configuration>
<sources>
<source>
<directory>src/main/native</directory>
<includes>
<include>**/*.cpp</include>
</includes>
</source>
<source>
<directory>../parent/src/main/native</directory>
<includes>
<include>**/*.cpp</include>
</includes>
</source>
</sources>
<compilerStartOptions>
<compilerStartOptions>-Wall -O3 -Wextra -Wno-unused-parameter -pedantic -Wpointer-arith -Wswitch-default -Wswitch-enum -Wundef -Wuninitialized</compilerStartOptions>
</compilerStartOptions>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<local-repository>C:/Users/wpilibj-buildmaster/maven-repository</local-repository>
</properties>
<profiles>
<profile>
<id>jenkins</id>
<distributionManagement>
<repository>
<id>myrepository</id>
<url>file:${local-repository}</url>
</repository>
</distributionManagement>
</profile>
</profiles>
</project>

View File

@@ -0,0 +1,340 @@
#include <map>
#include "networktables/NetworkTable.h"
#include <string>
#include "networktables2/thread/NTThreadManager.h"
#include "networktables2/thread/DefaultThreadManager.h"
#include "networktables2/NetworkTableEntry.h"
#include "networktables2/util/StringCache.h"
#include "networktables/NetworkTableProvider.h"
#include "networktables/NetworkTableMode.h"
#include "OSAL/Synchronized.h"
#include "tables/TableKeyNotDefinedException.h"
#include "networktables2/type/DefaultEntryTypes.h"
#include "tables/ITableListener.h"
#include "networktables/NetworkTableConnectionListenerAdapter.h"
#include "networktables/NetworkTableKeyListenerAdapter.h"
#include "networktables/NetworkTableListenerAdapter.h"
#include "networktables/NetworkTableSubListenerAdapter.h"
const char NetworkTable::PATH_SEPARATOR_CHAR = '/';
const std::string NetworkTable::PATH_SEPARATOR("/");
const int NetworkTable::DEFAULT_PORT = 1735;
DefaultThreadManager NetworkTable::threadManager;
NetworkTableProvider* NetworkTable::staticProvider = NULL;
NetworkTableNode* NetworkTable::staticNode = NULL;
void* NetworkTable::streamFactory = NULL;
NetworkTableEntryTypeManager* NetworkTable::typeManager = NULL;
StreamDeleter streamDeleter = NULL;
NetworkTableMode* NetworkTable::mode = &NetworkTableMode::Server;
int NetworkTable::port = DEFAULT_PORT;
std::string NetworkTable::ipAddress;
NTReentrantSemaphore NetworkTable::STATIC_LOCK;
void NetworkTable::CheckInit(){
printf("[NT] NetworkTable::CheckInit()...\n");
{
NTSynchronized sync(STATIC_LOCK);
if(staticProvider!=NULL)
throw new IllegalStateException("Network tables has already been initialized");
}
}
void NetworkTable::Initialize() {
CheckInit();
printf("[NT] NetworkTable::Initialize()...\n");
staticProvider = new NetworkTableProvider(*(staticNode = mode->CreateNode(ipAddress.c_str(), port, threadManager, streamFactory, streamDeleter, typeManager)));
printf("[NT] ...NetworkTable::Initialize().\n");
}
void NetworkTable::Shutdown()
{
if (staticProvider!=NULL)
{
delete staticProvider;
staticProvider=NULL;
}
if (staticNode!=NULL)
{
delete staticNode;
staticNode=NULL;
}
if (streamDeleter!=NULL && streamFactory!=NULL)
{
streamDeleter(streamFactory);
streamFactory=NULL;
streamDeleter=NULL;
}
if (typeManager!=NULL)
{
delete typeManager;
typeManager=NULL;
}
}
void NetworkTable::SetTableProvider(NetworkTableProvider* provider){
CheckInit();
staticProvider = provider;
}
void NetworkTable::SetClientMode(){
CheckInit();
mode = &NetworkTableMode::Client;
}
void NetworkTable::SetServerMode(){
CheckInit();
mode = &NetworkTableMode::Server;
}
void NetworkTable::SetTeam(int team){
char tmp[30];
sprintf(tmp, "%d.%d.%d.%d\n", 10, team/100, team%100, 2);
SetIPAddress(tmp);
}
void NetworkTable::SetIPAddress(const char* address){
CheckInit();
ipAddress = address;
}
NetworkTable* NetworkTable::GetTable(std::string key) {
printf("[NT] NetworkTable::GetTable()...\n");
if(staticProvider==NULL){
printf("[NT] \tInitializing...\n");
Initialize();
}
std::string tmp(PATH_SEPARATOR);
tmp+=key;
printf("[NT] ...Ready to get Table.\n");
return (NetworkTable*)staticProvider->GetTable(tmp);
}
NetworkTable::NetworkTable(std::string _path, NetworkTableProvider& _provider) :
path(_path), entryCache(_path), absoluteKeyCache(_path), provider(_provider), node(provider.GetNode()) {
}
NetworkTable::~NetworkTable(){
}
bool NetworkTable::IsConnected() {
return node.IsConnected();
}
bool NetworkTable::IsServer() {
return node.IsServer();
}
void NetworkTable::AddConnectionListener(IRemoteConnectionListener* listener, bool immediateNotify) {
map<IRemoteConnectionListener*, NetworkTableConnectionListenerAdapter*>::iterator itr = connectionListenerMap.find(listener);
if(itr != connectionListenerMap.end()){
throw IllegalStateException("Cannot add the same listener twice");
}
else{
NetworkTableConnectionListenerAdapter* adapter = new NetworkTableConnectionListenerAdapter(this, listener);
connectionListenerMap[listener] = adapter;
node.AddConnectionListener(adapter, immediateNotify);
}
}
void NetworkTable::RemoveConnectionListener(IRemoteConnectionListener* listener) {
map<IRemoteConnectionListener*, NetworkTableConnectionListenerAdapter*>::iterator itr = connectionListenerMap.find(listener);
if(itr != connectionListenerMap.end()){
node.RemoveConnectionListener(itr->second);
delete itr->second;
connectionListenerMap.erase(itr);
}
}
void NetworkTable::AddTableListener(ITableListener* listener) {
AddTableListener(listener, false);
}
void NetworkTable::AddTableListener(ITableListener* listener, bool immediateNotify) {
std::string tmp(path);
tmp+=PATH_SEPARATOR;
NetworkTableListenerAdapter* adapter = new NetworkTableListenerAdapter(tmp, this, listener);
listenerMap.insert ( pair<ITableListener*,ITableListener*>(listener, adapter) );
node.AddTableListener(adapter, immediateNotify);
}
void NetworkTable::AddTableListener(std::string key, ITableListener* listener, bool immediateNotify) {
NetworkTableKeyListenerAdapter* adapter = new NetworkTableKeyListenerAdapter(key, absoluteKeyCache.Get(key), this, listener);
listenerMap.insert ( pair<ITableListener*,ITableListener*>(listener, adapter) );
node.AddTableListener(adapter, immediateNotify);
}
void NetworkTable::AddSubTableListener(ITableListener* listener) {
NetworkTableSubListenerAdapter* adapter = new NetworkTableSubListenerAdapter(path, this, listener);
listenerMap.insert ( pair<ITableListener*,ITableListener*>(listener, adapter) );
node.AddTableListener(adapter, true);
}
void NetworkTable::RemoveTableListener(ITableListener* listener) {
multimap<ITableListener*,ITableListener*>::iterator itr;
pair<multimap<ITableListener*,ITableListener*>::iterator,multimap<ITableListener*,ITableListener*>::iterator> itrs = listenerMap.equal_range(listener);
for (itr=itrs.first; itr!=itrs.second; ++itr){
node.RemoveTableListener(itr->second);
delete itr->second;
}
listenerMap.erase(itrs.first, itrs.second);
}
NetworkTableEntry* NetworkTable::GetEntry(std::string key){
{
NTSynchronized sync(LOCK);
return entryCache.Get(key);
}
}
NetworkTable* NetworkTable::GetSubTable(std::string key) {
{
NTSynchronized sync(LOCK);
return (NetworkTable*)provider.GetTable(absoluteKeyCache.Get(key));
}
}
bool NetworkTable::ContainsKey(std::string key) {
return node.ContainsKey(absoluteKeyCache.Get(key));
}
bool NetworkTable::ContainsSubTable(std::string key){
std::string subtablePrefix(absoluteKeyCache.Get(key));
subtablePrefix+=PATH_SEPARATOR;
std::vector<std::string>* keys = node.GetEntryStore().keys();
for(unsigned int i = 0; i<keys->size(); ++i){
if(keys->at(i).compare(0, subtablePrefix.size(), subtablePrefix)==0){
delete keys;
return true;
}
}
delete keys;
return false;
}
void NetworkTable::PutNumber(std::string key, double value) {
EntryValue eValue;
eValue.f = value;
PutValue(key, &DefaultEntryTypes::DOUBLE, eValue);
}
double NetworkTable::GetNumber(std::string key) {
return node.GetDouble(absoluteKeyCache.Get(key));
}
double NetworkTable::GetNumber(std::string key, double defaultValue) {
try {
return node.GetDouble(absoluteKeyCache.Get(key));
} catch (TableKeyNotDefinedException& e) {
return defaultValue;
}
}
void NetworkTable::PutString(std::string key, std::string value) {
EntryValue eValue;
eValue.ptr = &value;
PutValue(key, &DefaultEntryTypes::STRING, eValue);
}
std::string NetworkTable::GetString(std::string key) {
return node.GetString(absoluteKeyCache.Get(key));
}
std::string NetworkTable::GetString(std::string key, std::string defaultValue) {
try {
return node.GetString(absoluteKeyCache.Get(key));
} catch (TableKeyNotDefinedException& e) {
return defaultValue;
}
}
void NetworkTable::PutBoolean(std::string key, bool value) {
EntryValue eValue;
eValue.b = value;
PutValue(key, &DefaultEntryTypes::BOOLEAN, eValue);
}
bool NetworkTable::GetBoolean(std::string key) {
return node.GetBoolean(absoluteKeyCache.Get(key));
}
bool NetworkTable::GetBoolean(std::string key, bool defaultValue) {
try {
return node.GetBoolean(absoluteKeyCache.Get(key));
} catch (TableKeyNotDefinedException& e) {
return defaultValue;
}
}
void NetworkTable::PutValue(std::string key, NetworkTableEntryType* type, EntryValue value){
NetworkTableEntry* entry = entryCache.Get(key);
if(entry!=NULL)
node.PutValue(entry, value);//TODO pass type along or do some kind of type check
else
node.PutValue(absoluteKeyCache.Get(key), type, value);
}
void NetworkTable::RetrieveValue(std::string key, ComplexData& externalValue) {
node.retrieveValue(absoluteKeyCache.Get(key), externalValue);
}
void NetworkTable::PutValue(std::string key, ComplexData& value){
EntryValue eValue;
eValue.ptr = &value;
PutValue(key, &value.GetType(), eValue);
}
EntryValue NetworkTable::GetValue(std::string key) {
return node.GetValue(absoluteKeyCache.Get(key));
}
EntryValue NetworkTable::GetValue(std::string key, EntryValue defaultValue) {
try {
return node.GetValue(absoluteKeyCache.Get(key));
} catch(TableKeyNotDefinedException& e){
return defaultValue;
}
}
//NetworkTableKeyCache
NetworkTableKeyCache::NetworkTableKeyCache(std::string _path):path(_path){}
NetworkTableKeyCache::~NetworkTableKeyCache(){}
std::string NetworkTableKeyCache::Calc(const std::string& key){
std::string tmp(path);
tmp+=NetworkTable::PATH_SEPARATOR;
tmp+=key;
return tmp;
}
//Entry Cache
EntryCache::EntryCache(std::string& _path):path(_path){}
EntryCache::~EntryCache(){}
NetworkTableEntry* EntryCache::Get(std::string& key){
return cache[key];
}

View File

@@ -0,0 +1,22 @@
/*
* NetworkTableConnectionListenerAdapter.cpp
*
* Created on: Oct 17, 2012
* Author: Mitchell Wills
*/
#include "networktables/NetworkTableConnectionListenerAdapter.h"
NetworkTableConnectionListenerAdapter::NetworkTableConnectionListenerAdapter(IRemote* _targetSource, IRemoteConnectionListener* _targetListener):
targetSource(_targetSource), targetListener(_targetListener){}
NetworkTableConnectionListenerAdapter::~NetworkTableConnectionListenerAdapter(){}
void NetworkTableConnectionListenerAdapter::Connected(IRemote* remote) {
targetListener->Connected(targetSource);
}
void NetworkTableConnectionListenerAdapter::Disconnected(IRemote* remote) {
targetListener->Disconnected(targetSource);
}

View File

@@ -0,0 +1,21 @@
/*
* NetworkTableKeyListenerAdapter.cpp
*
* Created on: Oct 17, 2012
* Author: Mitchell Wills
*/
#include "networktables/NetworkTableKeyListenerAdapter.h"
NetworkTableKeyListenerAdapter::NetworkTableKeyListenerAdapter(std::string _relativeKey, std::string _fullKey, NetworkTable* _targetSource, ITableListener* _targetListener):
relativeKey(_relativeKey), fullKey(_fullKey), targetSource(_targetSource), targetListener(_targetListener){}
NetworkTableKeyListenerAdapter::~NetworkTableKeyListenerAdapter(){}
void NetworkTableKeyListenerAdapter::ValueChanged(ITable* source, const std::string& key, EntryValue value, bool isNew) {
if(key==fullKey){
targetListener->ValueChanged(targetSource, relativeKey, value, isNew);
}
}

View File

@@ -0,0 +1,26 @@
/*
* NetworkTableListenerAdapter.cpp
*
* Created on: Oct 17, 2012
* Author: Mitchell Wills
*/
#include "networktables/NetworkTableListenerAdapter.h"
#include "networktables/NetworkTable.h"
NetworkTableListenerAdapter::NetworkTableListenerAdapter(std::string _prefix, ITable* _targetSource, ITableListener* _targetListener) :
prefix(_prefix), targetSource(_targetSource), targetListener(_targetListener){}
NetworkTableListenerAdapter::~NetworkTableListenerAdapter(){}
void NetworkTableListenerAdapter::ValueChanged(ITable* source, const std::string& key, EntryValue value, bool isNew) {//TODO use string cache
if(key.compare(0,prefix.size(),prefix)==0){
std::string relativeKey = key.substr(prefix.length());
if(std::string::npos != relativeKey.find(NetworkTable::PATH_SEPARATOR_CHAR))
return;
targetListener->ValueChanged(targetSource, relativeKey, value, isNew);
}
}

View File

@@ -0,0 +1,44 @@
/*
* NetworkTableMode.cpp
*
* Created on: Oct 16, 2012
* Author: Mitchell Wills
*/
#include <string>
#include "networktables2/thread/NTThreadManager.h"
#include "networktables2/NetworkTableNode.h"
#include "networktables2/server/NetworkTableServer.h"
#include "networktables2/client/NetworkTableClient.h"
#include "networktables2/stream/SocketServerStreamProvider.h"
#include "networktables2/stream/SocketStreamFactory.h"
#include "networktables/NetworkTableMode.h"
NetworkTableServerMode NetworkTableMode::Server;
NetworkTableClientMode NetworkTableMode::Client;
NetworkTableServerMode::NetworkTableServerMode(){}
NetworkTableClientMode::NetworkTableClientMode(){}
static void deleteIOStreamProvider(void* ptr){
delete (IOStreamProvider*)ptr;
}
static void deleteIOStreamFactory(void* ptr){
delete (IOStreamFactory*)ptr;
}
NetworkTableNode* NetworkTableServerMode::CreateNode(const char* ipAddress, int port, NTThreadManager& threadManager, void*& streamFactory_out, StreamDeleter& streamDeleter_out, NetworkTableEntryTypeManager*& typeManager_out){
IOStreamProvider* streamProvider = new SocketServerStreamProvider(port);
streamFactory_out = streamProvider;
typeManager_out = new NetworkTableEntryTypeManager();
streamDeleter_out = deleteIOStreamFactory;
return new NetworkTableServer(*streamProvider, *typeManager_out, threadManager);
}
NetworkTableNode* NetworkTableClientMode::CreateNode(const char* ipAddress, int port, NTThreadManager& threadManager, void*& streamFactory_out, StreamDeleter& streamDeleter_out, NetworkTableEntryTypeManager*& typeManager_out){
IOStreamFactory* streamFactory = new SocketStreamFactory(ipAddress, port);
streamFactory_out = streamFactory;
typeManager_out = new NetworkTableEntryTypeManager();
streamDeleter_out = deleteIOStreamProvider;
return new NetworkTableClient(*streamFactory, *typeManager_out, threadManager);
}

View File

@@ -0,0 +1,42 @@
#include <map>
#include <string>
#include "tables/ITableProvider.h"
#include "networktables2/NetworkTableNode.h"
#include "networktables/NetworkTable.h"
#include "networktables/NetworkTableProvider.h"
using namespace std;
NetworkTableProvider::NetworkTableProvider(NetworkTableNode& _node) : node(_node){}
NetworkTableProvider::~NetworkTableProvider(){
while(tables.size()>0){
map<std::string, NetworkTable*>::iterator it = tables.begin();
delete it->second;
tables.erase(it);
}
}
ITable* NetworkTableProvider::GetRootTable(){
return GetTable("");
}
ITable* NetworkTableProvider::GetTable(std::string key){
if(tables.find(key) != tables.end())
{
return tables[key];
}
else
{
NetworkTable* table = new NetworkTable(key, *this);
tables[key] = table;
return table;
}
}

View File

@@ -0,0 +1,43 @@
/*
* NetworkTableSubListenerAdapter.cpp
*
* Created on: Oct 17, 2012
* Author: Mitchell Wills
*/
#include "networktables/NetworkTableSubListenerAdapter.h"
/**
* Create a new adapter
* @param prefix the prefix of the current table
* @param targetSource the source that events passed to the target listener will appear to come from
* @param targetListener the listener where events are forwarded to
*/
NetworkTableSubListenerAdapter::NetworkTableSubListenerAdapter(std::string& _prefix, NetworkTable* _targetSource, ITableListener* _targetListener):
prefix(_prefix), targetSource(_targetSource), targetListener(_targetListener){}
NetworkTableSubListenerAdapter::~NetworkTableSubListenerAdapter(){}
void NetworkTableSubListenerAdapter::ValueChanged(ITable* source, const std::string& key, EntryValue value, bool isNew) {//TODO use string cache
if(key.compare(0,prefix.size(),prefix)==0){
std::string relativeKey = key.substr(prefix.length()+1);
int endSubTable = -1;//TODO implement sub table listening better
for(unsigned int i = 0; i<relativeKey.length(); ++i){
if(relativeKey.at(i)==NetworkTable::PATH_SEPARATOR_CHAR){//is sub table
endSubTable = i;
break;
}
}
if(endSubTable!=-1){
std::string subTableKey = relativeKey.substr(0, endSubTable);
if(notifiedTables.find(subTableKey)==notifiedTables.end()){
notifiedTables.insert(subTableKey);
EntryValue eValue;
eValue.ptr = targetSource->GetSubTable(subTableKey);
targetListener->ValueChanged(targetSource, subTableKey, eValue, true);
}
}
}
}

View File

@@ -0,0 +1,214 @@
/*
* AbstractNetworkTableEntryStore.cpp
*
* Created on: Sep 16, 2012
* Author: Mitchell Wills
*/
#include "networktables2/AbstractNetworkTableEntryStore.h"
#include "networktables2/TableKeyExistsWithDifferentTypeException.h"
#include <map>
#include <vector>
#include <iostream>
#include <stdio.h>
AbstractNetworkTableEntryStore::AbstractNetworkTableEntryStore(TableListenerManager& lstnManager):
listenerManager(lstnManager){
}
AbstractNetworkTableEntryStore::~AbstractNetworkTableEntryStore(){
LOCK.take();
std::map<std::string, NetworkTableEntry*>::iterator itr;
for(itr = namedEntries.begin(); itr != namedEntries.end();){
delete itr->second;
namedEntries.erase(itr++);
}
}
/**
* Get an entry based on it's name
* @param name the name of the entry to look for
* @return the entry or null if the entry does not exist
*/
NetworkTableEntry* AbstractNetworkTableEntryStore::GetEntry(std::string& name){
{
NTSynchronized sync(LOCK);
std::map<std::string, NetworkTableEntry*>::iterator value_itr = namedEntries.find(name);
if(value_itr != namedEntries.end()) {
return value_itr->second;
}
return NULL;
}
}
NetworkTableEntry* AbstractNetworkTableEntryStore::GetEntry(EntryId entryId){
{
NTSynchronized sync(LOCK);
std::map<EntryId, NetworkTableEntry*>::iterator value_itr = idEntries.find(entryId);
if(value_itr != idEntries.end()) {
return value_itr->second;
}
return NULL;
}
}
std::vector<std::string>* AbstractNetworkTableEntryStore::keys(){
{
NTSynchronized sync(LOCK);
std::vector<std::string>* keys = new std::vector<std::string>();
std::map<std::string, NetworkTableEntry*>::iterator itr;
for(itr = namedEntries.begin(); itr != namedEntries.end(); itr++)
{
std::string key = (*itr).first;
keys->push_back(key);
}
return (keys);
}
}
/**
* Remove all entries
* NOTE: This method should not be used with applications which cache entries which would lead to unknown results
* This method is for use in testing only
*/
void AbstractNetworkTableEntryStore::clearEntries(){
{
NTSynchronized sync(LOCK);
namedEntries.clear();
idEntries.clear();
}
}
/**
* clear the id's of all entries
*/
void AbstractNetworkTableEntryStore::clearIds(){
{
NTSynchronized sync(LOCK);
std::map<std::string, NetworkTableEntry*>::iterator itr;
idEntries.clear();
for(itr = namedEntries.begin(); itr != namedEntries.end(); itr++)
{
((NetworkTableEntry*)(*itr).second)->ClearId();
}
}
}
void AbstractNetworkTableEntryStore::SetOutgoingReceiver(OutgoingEntryReceiver* receiver){
outgoingReceiver = receiver;
}
void AbstractNetworkTableEntryStore::SetIncomingReceiver(OutgoingEntryReceiver* receiver){
incomingReceiver = receiver;
}
/**
* Stores the given value under the given name and queues it for
* transmission to the server.
*
* @param name The name under which to store the given value.
* @param type The type of the given value.
* @param value The value to store.
* @throws TableKeyExistsWithDifferentTypeException Thrown if an
* entry already exists with the given name and is of a different type.
*/
void AbstractNetworkTableEntryStore::PutOutgoing(std::string& name, NetworkTableEntryType* type, EntryValue value){
{
NTSynchronized sync(LOCK);
std::map<std::string, NetworkTableEntry*>::iterator index = namedEntries.find(name);
NetworkTableEntry* tableEntry;
if(index == namedEntries.end())//if the name does not exist in the current entries
{
tableEntry = new NetworkTableEntry(name, type, value);
if(addEntry(tableEntry))
{
tableEntry->FireListener(listenerManager);
outgoingReceiver->offerOutgoingAssignment(tableEntry);
}
}
else
{
tableEntry = index->second;
if(tableEntry->GetType()->id != type->id){
throw TableKeyExistsWithDifferentTypeException(name, tableEntry->GetType());
}
EntryValue oldValue = tableEntry->GetValue();
if(!type->areEqual(value, oldValue)){
if(updateEntry(tableEntry, (SequenceNumber)(tableEntry->GetSequenceNumber() + 1), value)){
outgoingReceiver->offerOutgoingUpdate(tableEntry);
}
tableEntry->FireListener(listenerManager);
}
}
}
}
void AbstractNetworkTableEntryStore::PutOutgoing(NetworkTableEntry* tableEntry, EntryValue value){
{
NTSynchronized sync(LOCK);
NetworkTableEntryType* type = tableEntry->GetType();
EntryValue oldValue = tableEntry->GetValue();
if(!type->areEqual(value, oldValue)){
if(updateEntry(tableEntry, (SequenceNumber)(tableEntry->GetSequenceNumber() + 1), value)){
outgoingReceiver->offerOutgoingUpdate(tableEntry);
}
tableEntry->FireListener(listenerManager);
}
}
}
void AbstractNetworkTableEntryStore::offerIncomingAssignment(NetworkTableEntry* entry){
{
NTSynchronized sync(LOCK);
std::map<std::string, NetworkTableEntry*>::iterator itr = namedEntries.find(entry->name);
NetworkTableEntry* tableEntry;
if(addEntry(entry)){
if(itr != namedEntries.end()){
tableEntry = itr->second;
}
else{
tableEntry = entry;
}
tableEntry->FireListener(listenerManager);//if we didnt have a pointer, then the copy of the version in the list would call this method, however with the pointer we are updating the version in the list
incomingReceiver->offerOutgoingAssignment(tableEntry);
}
else
delete entry;
}
}
void AbstractNetworkTableEntryStore::offerIncomingUpdate(NetworkTableEntry* entry, SequenceNumber squenceNumber, EntryValue value){
{
NTSynchronized sync(LOCK);
if(updateEntry(entry, squenceNumber, value)){
entry->FireListener(listenerManager);
incomingReceiver->offerOutgoingUpdate(entry);
}
}
}
/**
* Called to say that a listener should notify the listener manager of all of the entries
* @param listener
* @param table
*/
void AbstractNetworkTableEntryStore::notifyEntries(ITable* table, ITableListener* listener){
{
NTSynchronized sync(LOCK);
std::map<std::string, NetworkTableEntry*>::iterator itr;
for(itr = namedEntries.begin(); itr != namedEntries.end(); itr++)
{
NetworkTableEntry* entry = itr->second;
listener->ValueChanged(table, itr->first, entry->GetValue(), true);
}
}
}

View File

@@ -0,0 +1,123 @@
#include "networktables2/NetworkTableEntry.h"
#include "networktables2/AbstractNetworkTableEntryStore.h"
NetworkTableEntry::NetworkTableEntry(std::string& _name, NetworkTableEntryType* _type, EntryValue _value)
: name(_name), type(_type){
id = UNKNOWN_ID;
sequenceNumber = 0;
value = type->copyValue(_value);
m_isNew = true;
m_isDirty = false;
}
NetworkTableEntry::NetworkTableEntry(EntryId _id, std::string& _name, SequenceNumber _sequenceNumber, NetworkTableEntryType* _type, EntryValue _value)
:name(_name), type(_type){
id = _id;
sequenceNumber = _sequenceNumber;
value = type->copyValue(_value);
m_isNew = true;
m_isDirty = false;
}
NetworkTableEntry::~NetworkTableEntry(){
type->deleteValue(value);
}
EntryId NetworkTableEntry::GetId() {
return id;
}
EntryValue NetworkTableEntry::GetValue(){
return value;
}
NetworkTableEntryType* NetworkTableEntry::GetType(){
return type;
}
bool NetworkTableEntry::PutValue(SequenceNumber newSequenceNumber, EntryValue newValue) {
if( (sequenceNumber < newSequenceNumber && newSequenceNumber - sequenceNumber < HALF_OF_SEQUENCE_NUMBERS)
|| (sequenceNumber > newSequenceNumber && sequenceNumber - newSequenceNumber > HALF_OF_SEQUENCE_NUMBERS) ){
EntryValue newValueCopy = type->copyValue(newValue);
type->deleteValue(value);
value = newValueCopy;
sequenceNumber = newSequenceNumber;
return true;
}
return false;
}
/**
* force a value and new sequence number upon an entry
* @param newSequenceNumber
* @param newValue
*/
void NetworkTableEntry::ForcePut(SequenceNumber newSequenceNumber, EntryValue newValue) {
EntryValue newValueCopy = type->copyValue(newValue);
type->deleteValue(value);
value = newValueCopy;
sequenceNumber = newSequenceNumber;
}
/**
* force a value and new sequence number upon an entry, Will also set the type of the entry
* @param newSequenceNumber
* @param type
* @param newValue
*/
void NetworkTableEntry::ForcePut(SequenceNumber newSequenceNumber, NetworkTableEntryType* newType, EntryValue newValue) {
type->deleteValue(value);
type = newType;
value = newType->copyValue(newValue);
sequenceNumber = newSequenceNumber;
}
void NetworkTableEntry::MakeDirty() {
m_isDirty = true;
}
void NetworkTableEntry::MakeClean() {
m_isDirty = false;
}
bool NetworkTableEntry::IsDirty(){
return m_isDirty;
}
/**
* Send the value of the entry over the output stream
* @param os
* @throws IOException
*/
void NetworkTableEntry::SendValue(DataIOStream& iostream){
type->sendValue(value, iostream);
}
/**
* @return the current sequence number of the entry
*/
SequenceNumber NetworkTableEntry::GetSequenceNumber() {
return sequenceNumber;
}
/**
* Sets the id of the entry
* @param id the id of the entry
* @throws IllegalStateException if the entry already has a known id
*/
void NetworkTableEntry::SetId(EntryId _id){
if(id!=UNKNOWN_ID)
throw IllegalStateException("Cannot set the Id of a table entry that already has a valid id");
id = _id;
}
/**
* clear the id of the entry to unknown
*/
void NetworkTableEntry::ClearId() {
id = UNKNOWN_ID;
}
void NetworkTableEntry::Send(NetworkTableConnection& connection) {
connection.sendEntryAssignment(*this);
}
void NetworkTableEntry::FireListener(TableListenerManager& listenerManager) {
listenerManager.FireTableListeners(name, value, m_isNew);
m_isNew = false;
}

View File

@@ -0,0 +1,171 @@
/*
* NetworkTableNode.cpp
*
* Created on: Sep 24, 2012
* Author: Mitchell Wills
*/
#include "networktables2/NetworkTableNode.h"
#include "networktables2/TableKeyExistsWithDifferentTypeException.h"
#include "networktables2/type/DefaultEntryTypes.h"
#include "tables/TableKeyNotDefinedException.h"
#include <algorithm>
NetworkTableNode::NetworkTableNode(AbstractNetworkTableEntryStore& _entryStore):
entryStore(_entryStore){
}
NetworkTableNode::~NetworkTableNode(){}
AbstractNetworkTableEntryStore& NetworkTableNode::GetEntryStore(){
return entryStore;
}
void NetworkTableNode::PutBoolean(std::string& name, bool value){
EntryValue eValue;
eValue.b = value;
PutValue(name, &DefaultEntryTypes::BOOLEAN, eValue);
}
bool NetworkTableNode::GetBoolean(std::string& name){
NetworkTableEntry* entry = entryStore.GetEntry(name);
if(entry==NULL)
throw TableKeyNotDefinedException(name);
return entry->GetValue().b;
}
void NetworkTableNode::PutDouble(std::string& name, double value){
EntryValue eValue;
eValue.f = value;
PutValue(name, &DefaultEntryTypes::DOUBLE, eValue);
}
double NetworkTableNode::GetDouble(std::string& name){
NetworkTableEntry* entry = entryStore.GetEntry(name);
if(entry==NULL)
throw TableKeyNotDefinedException(name);
return entry->GetValue().f;
}
void NetworkTableNode::PutString(std::string& name, std::string& value){
EntryValue eValue;
eValue.ptr = &value;
PutValue(name, &DefaultEntryTypes::STRING, eValue);
}
std::string& NetworkTableNode::GetString(std::string& name) {
NetworkTableEntry* entry = entryStore.GetEntry(name);
if(entry==NULL)
throw TableKeyNotDefinedException(name);
return *(std::string*)(entry->GetValue().ptr);
}
void NetworkTableNode::PutComplex(std::string& name, ComplexData& value){
EntryValue eValue;
eValue.ptr = &value;
PutValue(name, &value.GetType(), eValue);
}
void NetworkTableNode::retrieveValue(std::string& name, ComplexData& externalData){
{
NTSynchronized sync(entryStore.LOCK);
NetworkTableEntry* entry = entryStore.GetEntry(name);
if(entry==NULL)
throw TableKeyNotDefinedException(name);
NetworkTableEntryType* entryType = entry->GetType();
if(!entryType->isComplex())
throw TableKeyExistsWithDifferentTypeException(name, entryType, "Is not a complex data type");
ComplexEntryType* complexType = (ComplexEntryType*)entryType;
complexType->exportValue(name, entry->GetValue(), externalData);
}
}
void NetworkTableNode::PutValue(std::string& name, NetworkTableEntryType* type, EntryValue value){
if(type->isComplex()){
{
NTSynchronized sync(entryStore.LOCK);
ComplexData* complexData = (ComplexData*)value.ptr;
ComplexEntryType* entryType = (ComplexEntryType*)type;
NetworkTableEntry* entry = entryStore.GetEntry(name);
if(entry!=NULL)
entryStore.PutOutgoing(entry, entryType->internalizeValue(entry->name, *complexData, entry->GetValue()));
else{
EntryValue nullValue = {0};
EntryValue entryValue = entryType->internalizeValue(name, *complexData, nullValue);
entryStore.PutOutgoing(name, type, entryValue);//TODO the entry gets copied when creating the entry should make lifecycle cleaner
type->deleteValue(entryValue);
}
}
}
else
entryStore.PutOutgoing(name, type, value);
}
void NetworkTableNode::PutValue(NetworkTableEntry* entry, EntryValue value){
if(entry->GetType()->isComplex()){
{
NTSynchronized sync(entryStore.LOCK);
ComplexEntryType* entryType = (ComplexEntryType*)entry->GetType();
entryStore.PutOutgoing(entry, entryType->internalizeValue(entry->name, *(ComplexData*)value.ptr, entry->GetValue()));
}
}
else
entryStore.PutOutgoing(entry, value);
}
EntryValue NetworkTableNode::GetValue(std::string& name){//TODO don't allow get of complex types
{
NTSynchronized sync(entryStore.LOCK);
NetworkTableEntry* entry = entryStore.GetEntry(name);
if(entry==NULL)
throw TableKeyNotDefinedException(name);
return entry->GetValue();
}
}
bool NetworkTableNode::ContainsKey(std::string& key){
return entryStore.GetEntry(key)!=NULL;
}
void NetworkTableNode::AddConnectionListener(IRemoteConnectionListener* listener, bool immediateNotify) {
remoteListeners.push_back(listener);
if(IsConnected())
listener->Connected(this);
else
listener->Disconnected(this);
}
void NetworkTableNode::RemoveConnectionListener(IRemoteConnectionListener* listener) {
std::vector<IRemoteConnectionListener*>::iterator listenerPosition = std::find(remoteListeners.begin(), remoteListeners.end(), listener);
if(listenerPosition!=remoteListeners.end())
remoteListeners.erase(listenerPosition);
}
void NetworkTableNode::FireConnectedEvent(){
for(unsigned int i = 0; i<remoteListeners.size(); ++i)
remoteListeners.at(i)->Connected(this);
}
void NetworkTableNode::FireDisconnectedEvent(){
for(unsigned int i = 0; i<remoteListeners.size(); ++i)
remoteListeners.at(i)->Disconnected(this);
}
void NetworkTableNode::AddTableListener(ITableListener* listener, bool immediateNotify) {
tableListeners.push_back(listener);
if(immediateNotify)
entryStore.notifyEntries(NULL, listener);
}
void NetworkTableNode::RemoveTableListener(ITableListener* listener) {
std::vector<ITableListener*>::iterator listenerPosition = std::find(tableListeners.begin(), tableListeners.end(), listener);
if(listenerPosition!=tableListeners.end())
tableListeners.erase(listenerPosition);
}
void NetworkTableNode::FireTableListeners(std::string& key, EntryValue value, bool isNew){
for(unsigned int i = 0; i<tableListeners.size(); ++i)
tableListeners.at(i)->ValueChanged(NULL, key, value, isNew);
}

View File

@@ -0,0 +1,13 @@
/*
* OutgoingEntryReciever.cpp
*
* Created on: Nov 3, 2012
* Author: Mitchell Wills
*/
#include "networktables2/OutgoingEntryReceiver.h"
OutgoingEntryReceiver_NULL_t OutgoingEntryReceiver_NULL;
void OutgoingEntryReceiver_NULL_t::offerOutgoingAssignment(NetworkTableEntry* entry){}
void OutgoingEntryReceiver_NULL_t::offerOutgoingUpdate(NetworkTableEntry* entry){}

View File

@@ -0,0 +1,22 @@
/*
* TableKeyExistsWithDifferentKeyException.cpp
*
* Created on: Sep 22, 2012
* Author: Mitchell Wills
*/
#include "networktables2/TableKeyExistsWithDifferentTypeException.h"
TableKeyExistsWithDifferentTypeException::TableKeyExistsWithDifferentTypeException(const std::string, NetworkTableEntryType* existingType) {
//TODO construct string this(existingKey, existingType, "");
}
TableKeyExistsWithDifferentTypeException::TableKeyExistsWithDifferentTypeException(const std::string, NetworkTableEntryType* existingType, const char* message) {
//TODO construct string super("Illegal put - key '" + existingKey + "' exists with type '" + existingType + "'. "+message);
}
TableKeyExistsWithDifferentTypeException::~TableKeyExistsWithDifferentTypeException() throw (){
//TODO delete message
}

View File

@@ -0,0 +1,33 @@
/*
* TransactionDirtier.cpp
*
* Created on: Sep 24, 2012
* Author: Mitchell Wills
*/
#include "networktables2/TransactionDirtier.h"
TransactionDirtier::TransactionDirtier(OutgoingEntryReceiver& _continuingReceiver) : continuingReceiver(_continuingReceiver){
}
void TransactionDirtier::offerOutgoingAssignment(NetworkTableEntry* entry) {
if(entry->IsDirty())
return;
entry->MakeDirty();
continuingReceiver.offerOutgoingAssignment(entry);
}
void TransactionDirtier::offerOutgoingUpdate(NetworkTableEntry* entry) {
if(entry->IsDirty())
return;
entry->MakeDirty();
continuingReceiver.offerOutgoingUpdate(entry);
}

View File

@@ -0,0 +1,129 @@
/*
* WriteManager.cpp
*
* Created on: Sep 25, 2012
* Author: Mitchell Wills
*/
#include "networktables2/WriteManager.h"
#include "networktables2/util/System.h"
#include "networktables2/AbstractNetworkTableEntryStore.h"
#include <iostream>
WriteManager::WriteManager(FlushableOutgoingEntryReceiver& _receiver, NTThreadManager& _threadManager, AbstractNetworkTableEntryStore& _entryStore, unsigned long _keepAliveDelay)
: receiver(_receiver), threadManager(_threadManager), entryStore(_entryStore), keepAliveDelay(_keepAliveDelay){
thread = NULL;
lastWrite = 0;
incomingAssignmentQueue = new std::queue<NetworkTableEntry*>();
incomingUpdateQueue = new std::queue<NetworkTableEntry*>();
outgoingAssignmentQueue = new std::queue<NetworkTableEntry*>();
outgoingUpdateQueue = new std::queue<NetworkTableEntry*>();
}
WriteManager::~WriteManager(){
stop();
//Note: this must occur after stop() to avoid deadlock
transactionsLock.take();
delete incomingAssignmentQueue;
delete incomingUpdateQueue;
delete outgoingAssignmentQueue;
delete outgoingUpdateQueue;
}
void WriteManager::start(){
if(thread!=NULL)
stop();
lastWrite = currentTimeMillis();
thread = threadManager.newBlockingPeriodicThread(this, "Write Manager Thread");
}
void WriteManager::stop(){
if(thread!=NULL){
thread->stop();
delete thread;
thread = NULL;
}
}
void WriteManager::offerOutgoingAssignment(NetworkTableEntry* entry) {
{
NTSynchronized sync(transactionsLock);
((std::queue<NetworkTableEntry*>*)incomingAssignmentQueue)->push(entry);
if(((std::queue<NetworkTableEntry*>*)incomingAssignmentQueue)->size()>=queueSize){
run();
writeWarning("assignment queue overflowed. decrease the rate at which you create new entries or increase the write buffer size");
}
}
}
void WriteManager::offerOutgoingUpdate(NetworkTableEntry* entry) {
{
NTSynchronized sync(transactionsLock);
((std::queue<NetworkTableEntry*>*)incomingUpdateQueue)->push(entry);
if(((std::queue<NetworkTableEntry*>*)incomingUpdateQueue)->size()>=queueSize){
run();
writeWarning("update queue overflowed. decrease the rate at which you update entries or increase the write buffer size");
}
}
}
void WriteManager::run() {
{
NTSynchronized sync(transactionsLock);
//swap the assignment and update queue
volatile std::queue<NetworkTableEntry*>* tmp = incomingAssignmentQueue;
incomingAssignmentQueue = outgoingAssignmentQueue;
outgoingAssignmentQueue = tmp;
tmp = incomingUpdateQueue;
incomingUpdateQueue = outgoingUpdateQueue;
outgoingUpdateQueue = tmp;
}
bool wrote = false;
NetworkTableEntry* entry;
while(!((std::queue<NetworkTableEntry*>*)outgoingAssignmentQueue)->empty()){
entry = ((std::queue<NetworkTableEntry*>*)outgoingAssignmentQueue)->front();
((std::queue<NetworkTableEntry*>*)outgoingAssignmentQueue)->pop();
{
NTSynchronized sync(entryStore.LOCK);
entry->MakeClean();
wrote = true;
receiver.offerOutgoingAssignment(entry);
}
}
while(!((std::queue<NetworkTableEntry*>*)outgoingUpdateQueue)->empty()){
entry = ((std::queue<NetworkTableEntry*>*)outgoingUpdateQueue)->front();
((std::queue<NetworkTableEntry*>*)outgoingUpdateQueue)->pop();
{
NTSynchronized sync(entryStore.LOCK);
entry->MakeClean();
wrote = true;
receiver.offerOutgoingUpdate(entry);
}
}
if(wrote){
receiver.flush();
lastWrite = currentTimeMillis();
}
else if(currentTimeMillis()-lastWrite>keepAliveDelay)
receiver.ensureAlive();
sleep_ms(20);
}

View File

@@ -0,0 +1,257 @@
/*
* ClientConnectionAdapter.cpp
*
* Created on: Nov 2, 2012
* Author: Mitchell Wills
*/
#include "networktables2/client/ClientConnectionAdapter.h"
#include "networktables2/util/System.h"
void ClientConnectionAdapter::gotoState(ClientConnectionState* newState){
{
NTSynchronized sync(LOCK);
if(connectionState!=newState){
fprintf(stdout, "[NT] %p entered connection state: %s\n", (void*)this, newState->toString());
fflush(stdout);
if(newState==&ClientConnectionState::IN_SYNC_WITH_SERVER)
connectionListenerManager.FireConnectedEvent();
if(connectionState==&ClientConnectionState::IN_SYNC_WITH_SERVER)
connectionListenerManager.FireDisconnectedEvent();
//TODO find better way to manage memory leak
ClientConnectionState_Error *temp=dynamic_cast<ClientConnectionState_Error *>(connectionState);
connectionState = newState;
if (temp)
delete temp;
}
}
}
/**
* @return the state of the connection
*/
ClientConnectionState* ClientConnectionAdapter::getConnectionState(){
return connectionState;
}
/**
* @return if the client is connected to the server
*/
bool ClientConnectionAdapter::isConnected() {
return getConnectionState()==&ClientConnectionState::IN_SYNC_WITH_SERVER;
}
/**
* Create a new ClientConnectionAdapter
* @param entryStore
* @param threadManager
* @param streamFactory
* @param transactionPool
* @param connectionListenerManager
*/
ClientConnectionAdapter::ClientConnectionAdapter(ClientNetworkTableEntryStore& _entryStore, NTThreadManager& _threadManager, IOStreamFactory& _streamFactory, ClientConnectionListenerManager& _connectionListenerManager, NetworkTableEntryTypeManager& _typeManager):
entryStore(_entryStore),
streamFactory(_streamFactory),
threadManager(_threadManager),
connectionListenerManager(_connectionListenerManager),
typeManager(_typeManager),
readThread(NULL),
monitor(NULL),
connection(NULL){
connectionState = &ClientConnectionState::DISCONNECTED_FROM_SERVER;
}
ClientConnectionAdapter::~ClientConnectionAdapter()
{
if(readThread!=NULL)
readThread->stop();
if (connection)
connection->close();
if(readThread!=NULL)
{
delete readThread;
readThread = NULL;
}
if(monitor!=NULL)
{
delete monitor;
monitor = NULL;
}
close();
if(connection!=NULL){
delete connection;
connection = NULL;
}
//TODO find better way to manage memory leak
ClientConnectionState_Error *temp=dynamic_cast<ClientConnectionState_Error *>(connectionState);
if (temp)
{
delete temp;
connectionState=NULL;
}
}
/*
* Connection management
*/
/**
* Reconnect the client to the server (even if the client is not currently connected)
*/
void ClientConnectionAdapter::reconnect() {
//This is in reconnect so that the entry store doesn't have to be valid when this object is deleted
//Note: clearIds() cannot be in a LOCK critical section
entryStore.clearIds();
{
NTSynchronized sync(LOCK);
close();//close the existing stream and monitor thread if needed
try{
IOStream* stream = streamFactory.createStream();
if(stream==NULL)
return;
if (!connection)
connection = new NetworkTableConnection(stream, typeManager);
else
connection->SetIOStream(stream);
m_IsConnectionClosed=false;
if (!monitor)
monitor = new ConnectionMonitorThread(*this, *connection);
if (!readThread)
readThread = threadManager.newBlockingPeriodicThread(monitor, "Client Connection Reader Thread");
connection->sendClientHello();
gotoState(&ClientConnectionState::CONNECTED_TO_SERVER);
} catch(IOException& e){
close();//make sure to clean everything up if we fail to connect
}
}
}
/**
* Close the client connection
*/
void ClientConnectionAdapter::close() {
close(&ClientConnectionState::DISCONNECTED_FROM_SERVER);
}
/**
* Close the connection to the server and enter the given state
* @param newState
*/
void ClientConnectionAdapter::close(ClientConnectionState* newState) {
{
NTSynchronized sync(LOCK);
gotoState(newState);
//Disconnect the socket
if(connection!=NULL)
{
connection->close();
connection->SetIOStream(NULL); //disconnect the table connection from the IO stream
}
m_IsConnectionClosed=true;
}
}
void ClientConnectionAdapter::badMessage(BadMessageException& e) {
close(new ClientConnectionState_Error(e));
sleep_ms(33); //avoid busy wait
}
void ClientConnectionAdapter::ioException(IOException& e) {
if(connectionState!=&ClientConnectionState::DISCONNECTED_FROM_SERVER)//will get io exception when on read thread connection is closed
{
reconnect();
sleep_ms(500);
}
else
{
sleep_ms(33); //avoid busy wait
}
}
NetworkTableEntry* ClientConnectionAdapter::GetEntry(EntryId id) {
return entryStore.GetEntry(id);
}
bool ClientConnectionAdapter::keepAlive() {
return true;
}
void ClientConnectionAdapter::clientHello(ProtocolVersion protocolRevision) {
throw BadMessageException("A client should not receive a client hello message");
}
void ClientConnectionAdapter::protocolVersionUnsupported(ProtocolVersion protocolRevision) {
close();
gotoState(new ClientConnectionState_ProtocolUnsuppotedByServer(protocolRevision));
}
void ClientConnectionAdapter::serverHelloComplete() {
if (connectionState==&ClientConnectionState::CONNECTED_TO_SERVER) {
try {
gotoState(&ClientConnectionState::IN_SYNC_WITH_SERVER);
entryStore.sendUnknownEntries(*connection);
} catch (IOException& e) {
ioException(e);
}
}
else
throw BadMessageException("A client should only receive a server hello complete once and only after it has connected to the server");
}
void ClientConnectionAdapter::offerIncomingAssignment(NetworkTableEntry* entry) {
entryStore.offerIncomingAssignment(entry);
}
void ClientConnectionAdapter::offerIncomingUpdate(NetworkTableEntry* entry, SequenceNumber sequenceNumber, EntryValue value) {
entryStore.offerIncomingUpdate(entry, sequenceNumber, value);
}
void ClientConnectionAdapter::offerOutgoingAssignment(NetworkTableEntry* entry) {
try {
{
NTSynchronized sync(LOCK);
if(connection!=NULL && connectionState==&ClientConnectionState::IN_SYNC_WITH_SERVER)
connection->sendEntryAssignment(*entry);
}
} catch(IOException& e){
ioException(e);
}
}
void ClientConnectionAdapter::offerOutgoingUpdate(NetworkTableEntry* entry) {
try {
{
NTSynchronized sync(LOCK);
if(connection!=NULL && connectionState==&ClientConnectionState::IN_SYNC_WITH_SERVER)
connection->sendEntryUpdate(*entry);
}
} catch(IOException& e){
ioException(e);
}
}
void ClientConnectionAdapter::flush() {
{
NTSynchronized sync(LOCK);
if(connection!=NULL) {
try {
connection->flush();
} catch (IOException& e) {
ioException(e);
}
}
}
}
void ClientConnectionAdapter::ensureAlive() {
{
NTSynchronized sync(LOCK);
if ((connection!=NULL)&&(!m_IsConnectionClosed)) {
try {
connection->sendKeepAlive();
} catch (IOException& e) {
ioException(e);
}
}
else
reconnect();//try to reconnect if not connected
}
}

View File

@@ -0,0 +1,77 @@
/*
* ClientConnectionState.cpp
*
* Created on: Nov 2, 2012
* Author: Mitchell Wills
*/
#include "networktables2/client/ClientConnectionState.h"
#ifndef _WRS_KERNEL
#include <stdint.h>
#endif
#include <stdlib.h>
#include <memory>
#include <cstring>
/**
* indicates that the client is disconnected from the server
*/
ClientConnectionState ClientConnectionState::DISCONNECTED_FROM_SERVER("DISCONNECTED_FROM_SERVER");
/**
* indicates that the client is connected to the server but has not yet begun communication
*/
ClientConnectionState ClientConnectionState::CONNECTED_TO_SERVER("CONNECTED_TO_SERVER");
/**
* represents that the client has sent the hello to the server and is waiting for a response
*/
ClientConnectionState ClientConnectionState::SENT_HELLO_TO_SERVER("SENT_HELLO_TO_SERVER");
/**
* represents that the client is now in sync with the server
*/
ClientConnectionState ClientConnectionState::IN_SYNC_WITH_SERVER("IN_SYNC_WITH_SERVER");
/**
* Create a new protocol unsupported state
* @param serverVersion
*/
ClientConnectionState_ProtocolUnsuppotedByServer::ClientConnectionState_ProtocolUnsuppotedByServer(ProtocolVersion _serverVersion):
ClientConnectionState("PROTOCOL_UNSUPPORTED_BY_SERVER"),
serverVersion(_serverVersion){}
/**
* @return the protocol version that the server reported it supports
*/
ProtocolVersion ClientConnectionState_ProtocolUnsuppotedByServer::getServerVersion(){
return serverVersion;
}
const char* ClientConnectionState_ProtocolUnsuppotedByServer::toString(){
return "PROTOCOL_UNSUPPORTED_BY_SERVER";
//return "PROTOCOL_UNSUPPORTED_BY_SERVER: Server Version: 0x"+Integer.toHexString(serverVersion);
}
/**
* Create a new error state
* @param e
*/
ClientConnectionState_Error::ClientConnectionState_Error(std::exception& _e):ClientConnectionState("CLIENT_ERROR"),e(_e){
strcpy(msg, "CLIENT_ERROR: ");
strcat(msg, e.what());
}
/**
* @return the exception that caused the client to enter an error state
*/
std::exception& ClientConnectionState_Error::getException(){
return e;
}
const char* ClientConnectionState_Error::toString(){
return msg;
//return "CLIENT_ERROR";
//return "CLIENT_ERROR: "+e.getClass()+": "+e.getMessage();
}
ClientConnectionState::ClientConnectionState(const char* _name){
name = _name;
}
const char* ClientConnectionState::toString(){
return name;
}

View File

@@ -0,0 +1,71 @@
/*
* ClientNetworkTableEntryStore.cpp
*
* Created on: Nov 2, 2012
* Author: Mitchell Wills
*/
#include "networktables2/client/ClientNetworkTableEntryStore.h"
/**
* Create a new ClientNetworkTableEntryStore
* @param transactionPool
* @param listenerManager
*/
ClientNetworkTableEntryStore::ClientNetworkTableEntryStore(TableListenerManager& listenerManager): AbstractNetworkTableEntryStore(listenerManager) {}
ClientNetworkTableEntryStore::~ClientNetworkTableEntryStore(){}
bool ClientNetworkTableEntryStore::addEntry(NetworkTableEntry* newEntry){
{
NTSynchronized sync(LOCK);
NetworkTableEntry* entry = (NetworkTableEntry*)namedEntries[newEntry->name];
if(entry!=NULL){
if(entry->GetId()!=newEntry->GetId()){
idEntries.erase(entry->GetId());
if(newEntry->GetId()!=NetworkTableEntry::UNKNOWN_ID){
entry->SetId(newEntry->GetId());
idEntries[newEntry->GetId()] = entry;
}
}
entry->ForcePut(newEntry->GetSequenceNumber(), newEntry->GetType(), newEntry->GetValue());
}
else{
if(newEntry->GetId()!=NetworkTableEntry::UNKNOWN_ID)
idEntries[newEntry->GetId()] = newEntry;
namedEntries[newEntry->name] = newEntry;
}
}
return true;
}
bool ClientNetworkTableEntryStore::updateEntry(NetworkTableEntry* entry, SequenceNumber sequenceNumber, EntryValue value) {
{
NTSynchronized sync(LOCK);
entry->ForcePut(sequenceNumber, value);
if(entry->GetId()==NetworkTableEntry::UNKNOWN_ID){
return false;
}
return true;
}
}
/**
* Send all unknown entries in the entry store to the given connection
* @param connection
* @throws IOException
*/
void ClientNetworkTableEntryStore::sendUnknownEntries(NetworkTableConnection& connection) {
{
NTSynchronized sync(LOCK);
std::map<std::string, NetworkTableEntry*>::iterator itr;
for(itr = namedEntries.begin(); itr != namedEntries.end(); itr++)
{
NetworkTableEntry* entry = (*itr).second;
if(entry->GetId()==NetworkTableEntry::UNKNOWN_ID)
connection.sendEntryAssignment(*entry);
}
connection.flush();
}
}

View File

@@ -0,0 +1,57 @@
/*
* NetworkTableClient.cpp
*
* Created on: Nov 3, 2012
* Author: Mitchell Wills
*/
#include "networktables2/client/NetworkTableClient.h"
/**
* Create a new NetworkTable Client
* @param streamFactory
* @param threadManager
* @param transactionPool
*/
NetworkTableClient::NetworkTableClient(IOStreamFactory& streamFactory, NetworkTableEntryTypeManager& typeManager, NTThreadManager& threadManager):
NetworkTableNode(*new ClientNetworkTableEntryStore(*this)),
adapter(*new ClientConnectionAdapter((ClientNetworkTableEntryStore&)entryStore, threadManager, streamFactory, *this, typeManager)),
writeManager(*new WriteManager(adapter, threadManager, GetEntryStore(), 1000)),
dirtier(new TransactionDirtier(writeManager)){
GetEntryStore().SetOutgoingReceiver(dirtier);
GetEntryStore().SetIncomingReceiver(&OutgoingEntryReceiver_NULL);
writeManager.start();
}
NetworkTableClient::~NetworkTableClient(){
//Closing this now will cause a reconnect from the write manager -James
//Close();
delete &writeManager;
delete &adapter;
delete &entryStore;
delete dirtier;
}
/**
* force the client to disconnect and reconnect to the server again. Will connect if the client is currently disconnected
*/
void NetworkTableClient::reconnect() {
adapter.reconnect();
}
void NetworkTableClient::Close() {
adapter.close();
}
void NetworkTableClient::stop() {
writeManager.stop();
Close();
}
bool NetworkTableClient::IsConnected() {
return adapter.isConnected();
}
bool NetworkTableClient::IsServer() {
return false;
}

View File

@@ -0,0 +1,23 @@
/*
* BadMessageException.cpp
*
* Created on: Sep 16, 2012
* Author: Mitchell Wills
*/
#include "networktables2/connection/BadMessageException.h"
BadMessageException::BadMessageException(const char* msg)
: message(msg)
{
}
BadMessageException::~BadMessageException() throw ()
{
}
const char* BadMessageException::what()
{
return message.c_str();
}

View File

@@ -0,0 +1,35 @@
/*
* ConnectionMonitorThread.cpp
*
* Created on: Sep 22, 2012
* Author: Mitchell Wills
*/
#include "networktables2/connection/ConnectionMonitorThread.h"
#include "networktables2/connection/BadMessageException.h"
#include "networktables2/util/System.h"
ConnectionMonitorThread::ConnectionMonitorThread(ConnectionAdapter& _adapter, NetworkTableConnection& _connection) :
adapter(_adapter), connection(_connection) {
}
void ConnectionMonitorThread::run() {
if (adapter.keepAlive())
{
try{
connection.read(adapter);
} catch(BadMessageException& e){
adapter.badMessage(e);
} catch(IOException& e){
adapter.ioException(e);
}
}
else
{
sleep_ms(10); //avoid busy-wait
//Test to see this working properly
//printf("--ConnectionMonitorThread::run Waiting to close\n");
}
}

View File

@@ -0,0 +1,86 @@
#include "networktables2/connection/DataIOStream.h"
//TODO remove this when alloca is solved
#ifdef WIN32
#include <malloc.h>
#endif
///This is used in case NULL is passed so all logic calls to IOstream can continue to assume there is never a null pointer
class InertStream : public IOStream
{
protected:
virtual int read(void* ptr, int numbytes) {return numbytes;} //return success
virtual int write(const void* ptr, int numbytes) {return numbytes;}
virtual void flush() {}
virtual void close() {}
};
static InertStream s_InertStream;
DataIOStream::DataIOStream(IOStream* _iostream) :
iostream(_iostream)
{
}
DataIOStream::~DataIOStream()
{
close();
if (iostream!=&s_InertStream)
delete iostream;
}
void DataIOStream::SetIOStream(IOStream* stream)
{
IOStream *temp=iostream;
iostream=stream ? stream : &s_InertStream; //We'll never assign NULL
if (temp!=&s_InertStream)
delete temp;
}
void DataIOStream::close()
{
iostream->close();
}
void DataIOStream::writeByte(uint8_t b)
{
iostream->write(&b, 1);
}
void DataIOStream::write2BytesBE(uint16_t s)
{
writeByte((uint8_t)(s >> 8));
writeByte((uint8_t)s);
}
void DataIOStream::writeString(std::string& str)
{
write2BytesBE(str.length());
iostream->write(str.c_str(), str.length());
}
void DataIOStream::flush()
{
iostream->flush();
}
uint8_t DataIOStream::readByte()
{
uint8_t value;
iostream->read(&value, 1);
return value;
}
uint16_t DataIOStream::read2BytesBE()
{
uint16_t value;
value = readByte()<<8 | readByte();
return value;
}
std::string* DataIOStream::readString()
{
unsigned int byteLength = read2BytesBE();
#ifndef WIN32
uint8_t bytes[byteLength+1];//FIXME figure out why this doesn't work on windows
#else
uint8_t* bytes = (uint8_t*)alloca(byteLength+1);
#endif
iostream->read(bytes, byteLength);
bytes[byteLength] = 0;
return new std::string((char*)bytes);//FIXME implement UTF-8 aware version
}

View File

@@ -0,0 +1,161 @@
/**
* An abstraction for the NetworkTable protocol
*
* @author mwills
*
*/
#include "networktables2/connection/NetworkTableConnection.h"
#include "networktables2/connection/BadMessageException.h"
NetworkTableConnection::NetworkTableConnection(IOStream* _ioStream, NetworkTableEntryTypeManager& _typeManager) :
ioStream(new DataIOStream(_ioStream)), typeManager(_typeManager) {
isValid = true;
}
NetworkTableConnection::~NetworkTableConnection(){
delete ioStream;
}
void NetworkTableConnection::SetIOStream(IOStream* stream)
{
ioStream->SetIOStream(stream); //just passing through
}
void NetworkTableConnection::close() {
{
NTSynchronized sync(WRITE_LOCK);
if (isValid) {
isValid = false;
ioStream->close();
}
}
}
void NetworkTableConnection::flush() {
{
NTSynchronized sync(WRITE_LOCK);
ioStream->flush();
}
}
void NetworkTableConnection::sendMessageHeader(
NetworkTableMessageType messageType) {
{
NTSynchronized sync(WRITE_LOCK);
ioStream->writeByte((uint8_t) messageType);
}
}
void NetworkTableConnection::sendKeepAlive() {
{
NTSynchronized sync(WRITE_LOCK);
sendMessageHeader(KEEP_ALIVE);
flush();
}
}
void NetworkTableConnection::sendClientHello() {
{
NTSynchronized sync(WRITE_LOCK);
sendMessageHeader(CLIENT_HELLO);
ioStream->write2BytesBE(PROTOCOL_REVISION);
flush();
}
}
void NetworkTableConnection::sendServerHelloComplete() {
{
NTSynchronized sync(WRITE_LOCK);
sendMessageHeader(SERVER_HELLO_COMPLETE);
flush();
}
}
void NetworkTableConnection::sendProtocolVersionUnsupported() {
{
NTSynchronized sync(WRITE_LOCK);
sendMessageHeader(PROTOCOL_VERSION_UNSUPPORTED);
ioStream->write2BytesBE(PROTOCOL_REVISION);
flush();
}
}
void NetworkTableConnection::sendEntryAssignment(NetworkTableEntry& entry) {
{
NTSynchronized sync(WRITE_LOCK);
sendMessageHeader(ENTRY_ASSIGNMENT);
ioStream->writeString(entry.name);
ioStream->writeByte(entry.GetType()->id);
ioStream->write2BytesBE(entry.GetId());
ioStream->write2BytesBE(entry.GetSequenceNumber());
entry.SendValue(*ioStream);
}
}
void NetworkTableConnection::sendEntryUpdate(NetworkTableEntry& entry) {
{
NTSynchronized sync(WRITE_LOCK);
sendMessageHeader(FIELD_UPDATE);
ioStream->write2BytesBE(entry.GetId());
ioStream->write2BytesBE(entry.GetSequenceNumber());
entry.SendValue(*ioStream);
}
}
void NetworkTableConnection::read(ConnectionAdapter& adapter) {
int messageType = ioStream->readByte();
switch (messageType) {
case KEEP_ALIVE:
adapter.keepAlive();
return;
case CLIENT_HELLO: {
uint16_t protocolRevision = ioStream->read2BytesBE();
adapter.clientHello(protocolRevision);
return;
}
case SERVER_HELLO_COMPLETE: {
adapter.serverHelloComplete();
return;
}
case PROTOCOL_VERSION_UNSUPPORTED: {
uint16_t protocolRevision = ioStream->read2BytesBE();
adapter.protocolVersionUnsupported(protocolRevision);
return;
}
case ENTRY_ASSIGNMENT: {
std::string* entryName = ioStream->readString();
TypeId typeId = ioStream->readByte();
NetworkTableEntryType* entryType = typeManager.GetType(typeId);
if (!entryType){
char exceptionMessageBuffer[50];
sprintf (exceptionMessageBuffer, "Unknown data type: %#x", typeId);
throw BadMessageException(exceptionMessageBuffer);
}
EntryId entryId = ioStream->read2BytesBE();
SequenceNumber entrySequenceNumber = ioStream->read2BytesBE();
EntryValue value = entryType->readValue(*ioStream);
adapter.offerIncomingAssignment(new NetworkTableEntry(entryId, *entryName, entrySequenceNumber, entryType, value));
entryType->deleteValue(value);
delete entryName;
return;
}
case FIELD_UPDATE: {
EntryId entryId = ioStream->read2BytesBE();
SequenceNumber entrySequenceNumber = ioStream->read2BytesBE();
NetworkTableEntry* entry = adapter.GetEntry(entryId);
if (!entry){
char exceptionMessageBuffer[50];
sprintf (exceptionMessageBuffer, "Received update for unknown entry id: %d", entryId);
throw BadMessageException(exceptionMessageBuffer);
}
EntryValue value = entry->GetType()->readValue(*ioStream);
adapter.offerIncomingUpdate(entry, entrySequenceNumber, value);
entry->GetType()->deleteValue(value);
return;
}
default:
char exceptionMessageBuffer[50];
sprintf (exceptionMessageBuffer, "Unknown Network Table Message Type: %d", messageType);
throw BadMessageException(exceptionMessageBuffer);
}
}

View File

@@ -0,0 +1,58 @@
/*
* NetworkTableServer.cpp
*
* Created on: Sep 27, 2012
* Author: Mitchell Wills
*/
#include "networktables2/server/NetworkTableServer.h"
#include "networktables2/server/ServerNetworkTableEntryStore.h"
#include <iostream>
#include <limits.h>
NetworkTableServer::NetworkTableServer(IOStreamProvider& _streamProvider, NetworkTableEntryTypeManager& typeManager, NTThreadManager& threadManager):
NetworkTableNode(*new ServerNetworkTableEntryStore(*this)),
streamProvider(_streamProvider),
incomingStreamMonitor(streamProvider, (ServerNetworkTableEntryStore&)entryStore, *this, connectionList, typeManager, threadManager),
connectionList(&incomingStreamMonitor),
writeManager(connectionList, threadManager, GetEntryStore(), ULONG_MAX),
continuingReceiver(writeManager){
GetEntryStore().SetIncomingReceiver(&continuingReceiver);
GetEntryStore().SetOutgoingReceiver(&continuingReceiver);
incomingStreamMonitor.start();
writeManager.start();
}
//TODO implement simplified NetworkTableServer constructor
/*NetworkTableServer::NetworkTableServer(IOStreamProvider& streamProvider){
this(streamProvider, new NetworkTableEntryTypeManager(), new DefaultThreadManager());
}*/
NetworkTableServer::~NetworkTableServer(){
Close();
delete &entryStore;
}
void NetworkTableServer::Close(){
try{
incomingStreamMonitor.stop();
writeManager.stop();
connectionList.closeAll();
} catch (const std::exception& ex) {
//TODO print stack trace?
}
}
void NetworkTableServer::OnNewConnection(ServerConnectionAdapter& connectionAdapter) {
connectionList.add(connectionAdapter);
}
bool NetworkTableServer::IsConnected() {
return true;
}
bool NetworkTableServer::IsServer() {
return true;
}

View File

@@ -0,0 +1,135 @@
/*
* ServerConnectionAdapter.cpp
*
* Created on: Sep 26, 2012
* Author: Mitchell Wills
*/
#include "networktables2/server/ServerConnectionAdapter.h"
#include <stdio.h>
void ServerConnectionAdapter::gotoState(ServerConnectionState* newState){
if(connectionState!=newState){
fprintf(stdout, "[NT] %p entered connection state: %s\n", (void*)this, newState->toString());
fflush(stdout);
connectionState = newState;
}
}
ServerConnectionAdapter::ServerConnectionAdapter(IOStream* stream, ServerNetworkTableEntryStore& _entryStore, IncomingEntryReceiver& _transactionReceiver, ServerAdapterManager& _adapterListener, NetworkTableEntryTypeManager& typeManager, NTThreadManager& threadManager) :
entryStore(_entryStore), transactionReceiver(_transactionReceiver), adapterListener(_adapterListener),
connection(stream, typeManager), monitorThread(*this, connection), m_IsAdapterListenerClosed(false) {
connectionState = &ServerConnectionState::CLIENT_DISCONNECTED;
gotoState(&ServerConnectionState::GOT_CONNECTION_FROM_CLIENT);
readThread = threadManager.newBlockingPeriodicThread(&monitorThread, "Server Connection Reader Thread");
}
ServerConnectionAdapter::~ServerConnectionAdapter(){
delete readThread;
}
void ServerConnectionAdapter::badMessage(BadMessageException& e) {
fprintf(stdout, "[NT] Bad message: %s\n", e.what());
fflush(stdout);
gotoState(new ServerConnectionState_Error(e));
adapterListener.close(*this, true);
m_IsAdapterListenerClosed=true;
}
void ServerConnectionAdapter::ioException(IOException& e) {
fprintf(stdout, "[NT] IOException message: %s\n", e.what());
fflush(stdout);
if(e.isEOF())
gotoState(&ServerConnectionState::CLIENT_DISCONNECTED);
else
gotoState(new ServerConnectionState_Error(e));
adapterListener.close(*this, false);
m_IsAdapterListenerClosed=true;
}
void ServerConnectionAdapter::shutdown(bool closeStream) {
readThread->stop();
if(closeStream)
connection.close();
}
bool ServerConnectionAdapter::keepAlive() {
return !m_IsAdapterListenerClosed; //returns true as long as the adapter listener has not been flagged for closing
}
void ServerConnectionAdapter::clientHello(ProtocolVersion protocolRevision) {
if(connectionState!=&ServerConnectionState::GOT_CONNECTION_FROM_CLIENT)
throw BadMessageException("A server should not receive a client hello after it has already connected/entered an error state");
if(protocolRevision!=NetworkTableConnection::PROTOCOL_REVISION){
connection.sendProtocolVersionUnsupported();
throw BadMessageException("Client Connected with bad protocol revision");
}
else{
entryStore.sendServerHello(connection);
gotoState(&ServerConnectionState::CONNECTED_TO_CLIENT);
}
}
void ServerConnectionAdapter::protocolVersionUnsupported(ProtocolVersion protocolRevision) {
throw BadMessageException("A server should not receive a protocol version unsupported message");
}
void ServerConnectionAdapter::serverHelloComplete() {
throw BadMessageException("A server should not receive a server hello complete message");
}
void ServerConnectionAdapter::offerIncomingAssignment(NetworkTableEntry* entry) {
transactionReceiver.offerIncomingAssignment(entry);
}
void ServerConnectionAdapter::offerIncomingUpdate(NetworkTableEntry* entry, SequenceNumber sequenceNumber, EntryValue value) {
transactionReceiver.offerIncomingUpdate(entry, sequenceNumber, value);
}
NetworkTableEntry* ServerConnectionAdapter::GetEntry(EntryId id) {
return entryStore.GetEntry(id);
}
void ServerConnectionAdapter::offerOutgoingAssignment(NetworkTableEntry* entry) {
try {
if(connectionState==&ServerConnectionState::CONNECTED_TO_CLIENT)
connection.sendEntryAssignment(*entry);
} catch (IOException& e) {
ioException(e);
}
}
void ServerConnectionAdapter::offerOutgoingUpdate(NetworkTableEntry* entry) {
try {
if(connectionState==&ServerConnectionState::CONNECTED_TO_CLIENT)
connection.sendEntryUpdate(*entry);
} catch (IOException& e) {
ioException(e);
}
}
void ServerConnectionAdapter::flush() {
try {
connection.flush();
} catch (IOException& e) {
ioException(e);
}
}
/**
* @return the state of the connection
*/
ServerConnectionState* ServerConnectionAdapter::getConnectionState() {
return connectionState;
}
void ServerConnectionAdapter::ensureAlive() {
try {
connection.sendKeepAlive();
} catch (IOException& e) {
ioException(e);
}
}

View File

@@ -0,0 +1,85 @@
/*
* ServerConnectionList.cpp
*
* Created on: Sep 26, 2012
* Author: Mitchell Wills
*/
#include "networktables2/server/ServerConnectionList.h"
#include "networktables2/server/ServerIncomingStreamMonitor.h"
#include <algorithm>
#include <stdio.h>
ServerConnectionList::ServerConnectionList(ServerIncomingStreamMonitor *Factory) : m_Factory(Factory)
{
}
ServerConnectionList::~ServerConnectionList()
{
connectionsLock.take();
closeAll();
}
void ServerConnectionList::add(ServerConnectionAdapter& connection)
{
NTSynchronized sync(connectionsLock);
connections.push_back(&connection);
}
void ServerConnectionList::close(ServerConnectionAdapter& connectionAdapter, bool closeStream)
{
NTSynchronized sync(connectionsLock);
std::vector<ServerConnectionAdapter*>::iterator connectionPosition = std::find(connections.begin(), connections.end(), &connectionAdapter);
if (connectionPosition != connections.end() && (*connectionPosition)==&connectionAdapter)
{
fprintf(stdout, "[NT] Close: %p\n", (void*)&connectionAdapter);
fflush(stdout);
connections.erase(connectionPosition);
m_Factory->close(&connectionAdapter);
//connectionAdapter.shutdown(closeStream);
//delete &connectionAdapter;
}
}
void ServerConnectionList::closeAll()
{
NTSynchronized sync(connectionsLock);
while(connections.size() > 0)
{
close(*connections[0], true);
}
}
void ServerConnectionList::offerOutgoingAssignment(NetworkTableEntry* entry)
{
NTSynchronized sync(connectionsLock);
for(unsigned int i = 0; i < connections.size(); ++i)
{
connections[i]->offerOutgoingAssignment(entry);
}
}
void ServerConnectionList::offerOutgoingUpdate(NetworkTableEntry* entry)
{
NTSynchronized sync(connectionsLock);
for(unsigned int i = 0; i < connections.size(); ++i)
{
connections[i]->offerOutgoingUpdate(entry);
}
}
void ServerConnectionList::flush()
{
NTSynchronized sync(connectionsLock);
for(unsigned int i = 0; i < connections.size(); ++i)
{
connections[i]->flush();
}
}
void ServerConnectionList::ensureAlive()
{
NTSynchronized sync(connectionsLock);
for(unsigned int i = 0; i < connections.size(); ++i)
{
connections[i]->ensureAlive();
}
}

View File

@@ -0,0 +1,31 @@
/*
* ServerConnectionState.cpp
*
* Created on: Sep 27, 2012
* Author: Mitchell Wills
*/
#include "networktables2/server/ServerConnectionState.h"
ServerConnectionState ServerConnectionState::GOT_CONNECTION_FROM_CLIENT("GOT_CONNECTION_FROM_CLIENT");
ServerConnectionState ServerConnectionState::CONNECTED_TO_CLIENT("CONNECTED_TO_CLIENT");
ServerConnectionState ServerConnectionState::CLIENT_DISCONNECTED("CLIENT_DISCONNECTED");
ServerConnectionState::ServerConnectionState(const char* _name):name(_name){
}
const char* ServerConnectionState::toString(){
return name;
}
ServerConnectionState_Error::ServerConnectionState_Error(std::exception& _e):ServerConnectionState("SERVER_ERROR"),e(_e){
}
const char* ServerConnectionState_Error::toString(){
return "SERVER_ERROR";
//TODO return "SERVER_ERROR: "+e.getClass()+": "+e.what();
}
std::exception& ServerConnectionState_Error::getException(){
return e;
}

View File

@@ -0,0 +1,84 @@
/*
* ServerIncomingStreamMonitor.cpp
*
* Created on: Sep 26, 2012
* Author: Mitchell Wills
*/
#include "networktables2/server/ServerIncomingStreamMonitor.h"
#include "networktables2/stream/IOStream.h"
ServerIncomingStreamMonitor::ServerIncomingStreamMonitor(IOStreamProvider& _streamProvider, ServerNetworkTableEntryStore& _entryStore,
ServerIncomingConnectionListener& _incomingListener, ServerAdapterManager& _adapterListener, NetworkTableEntryTypeManager& _typeManager,
NTThreadManager& _threadManager) :
streamProvider(_streamProvider), entryStore(_entryStore), incomingListener(_incomingListener), adapterListener(_adapterListener),
typeManager(_typeManager), threadManager(_threadManager), monitorThread(NULL)
{
}
ServerIncomingStreamMonitor::~ServerIncomingStreamMonitor()
{
stop();
}
/**
* Start the monitor thread
*/
void ServerIncomingStreamMonitor::start()
{
if (monitorThread != NULL)
stop();
monitorThread = threadManager.newBlockingPeriodicThread(this, "Server Incoming Stream Monitor Thread");
}
/**
* Stop the monitor thread
*/
void ServerIncomingStreamMonitor::stop()
{
if (monitorThread != NULL)
{
streamProvider.close(); //This would get called on deletion too
NTThread *temp=monitorThread;
monitorThread = NULL; //call this before stop for the check below to ensure a new server connection adapter will not happen
temp->stop();
delete temp;
}
}
void ServerIncomingStreamMonitor::run()
{
try
{
while (monitorThread!=NULL)
{
IOStream* newStream = streamProvider.accept();
{
NTSynchronized sync(BlockDeletionList);
for (size_t i=0;i<m_DeletionList.size();i++)
{
ServerConnectionAdapter *Element=m_DeletionList[i];
Element->shutdown(true); //TODO assume to always close stream
delete Element;
}
m_DeletionList.clear();
}
//Note: monitorThread must be checked to avoid crash on exit
// [8/31/2013 Terminator]
if ((monitorThread!=NULL)&&(newStream != NULL))
{
ServerConnectionAdapter* connectionAdapter = new ServerConnectionAdapter(newStream, entryStore, entryStore, adapterListener, typeManager, threadManager);
incomingListener.OnNewConnection(*connectionAdapter);
}
}
}
catch (IOException& e)
{
//could not get a new stream for some reason. ignore and continue
}
}
void ServerIncomingStreamMonitor::close(ServerConnectionAdapter *Adapter)
{
NTSynchronized sync(BlockDeletionList);
m_DeletionList.push_back(Adapter);
}

View File

@@ -0,0 +1,56 @@
/*
* ServerNetworkTableEntryStore.cpp
*
* Created on: Sep 26, 2012
* Author: Mitchell Wills
*/
#include "networktables2/server/ServerNetworkTableEntryStore.h"
ServerNetworkTableEntryStore::ServerNetworkTableEntryStore(TableListenerManager& _listenerManager) :
AbstractNetworkTableEntryStore(_listenerManager)
{
nextId = (EntryId)0;
}
ServerNetworkTableEntryStore::~ServerNetworkTableEntryStore()
{
}
bool ServerNetworkTableEntryStore::addEntry(NetworkTableEntry* newEntry)
{
NTSynchronized sync(LOCK);
NetworkTableEntry* entry = namedEntries[newEntry->name];
if (entry == NULL)
{
newEntry->SetId(nextId++);
idEntries[newEntry->GetId()] = newEntry;
namedEntries[newEntry->name] = newEntry;
return true;
}
return false;
}
bool ServerNetworkTableEntryStore::updateEntry(NetworkTableEntry* entry, SequenceNumber sequenceNumber, EntryValue value)
{
NTSynchronized sync(LOCK);
return entry->PutValue(sequenceNumber, value);
}
/**
* Send all entries in the entry store as entry assignments in a single transaction
* @param connection
* @throws IOException
*/
void ServerNetworkTableEntryStore::sendServerHello(NetworkTableConnection& connection)
{
NTSynchronized sync(LOCK);
std::map<std::string, NetworkTableEntry*>::iterator itr;
for (itr = namedEntries.begin(); itr != namedEntries.end(); itr++)
{
NetworkTableEntry* entry = itr->second;
connection.sendEntryAssignment(*entry);
}
connection.sendServerHelloComplete();
connection.flush();
}

View File

@@ -0,0 +1,75 @@
/*
* ArrayData.cpp
*
* Created on: Nov 14, 2012
* Author: Mitchell Wills
*/
#include "networktables2/type/ArrayData.h"
#ifndef _WRS_KERNEL
#include <stdint.h>
#endif
#include <cstring>
#include <stdlib.h>
#include <memory>
ArrayData::ArrayData(ArrayEntryType& type) : ComplexData(type), m_data_type(type){
m_size = 0;
data = NULL;
}
ArrayData::~ArrayData(){
free(data);
}
EntryValue ArrayData::_get(unsigned int index){//TODO bounds checking
return data[index];
}
void ArrayData::_set(unsigned int index, EntryValue value){//TODO bounds checking
m_data_type.deleteElement(data[index]);
data[index] = m_data_type.copyElement(value);
}
void ArrayData::_add(EntryValue value){
setSize(size()+1);
data[size()-1] = m_data_type.copyElement(value);
}
void ArrayData::remove(unsigned int index){
//if(index<0 || index>=size())
// throw IndexOutOfBoundsException();//TODO bounds check
m_data_type.deleteElement(data[index]);
EntryValue nullValue = {0};
data[index] = nullValue;
if(index < size()-1){
memcpy(data+index, data+index+1, (size()-index-1) * sizeof(EntryValue));
}
setSize(size()-1);
}
void ArrayData::setSize(unsigned int newSize){
if(newSize==m_size)//TODO bound check greater than max size
return;
EntryValue* newArray = (EntryValue*)malloc(newSize*sizeof(EntryValue));//TODO cache arrays
if(newSize<m_size){
memcpy(newArray, data, newSize * sizeof(EntryValue));
for(unsigned int i = newSize; i<m_size; ++i)
m_data_type.deleteElement(data[i]);
}
else{
if(data!=NULL)
memcpy(newArray, data, m_size * sizeof(EntryValue));
else
m_size = 0;//ensure that the current size is actually 0 otherwise will end up with uninitialized values in the array
EntryValue nullValue = {0};
for(unsigned int i = m_size; i<newSize; ++i)
newArray[i] = nullValue;
}
if(data!=NULL)
free(data);
data = newArray;
m_size = newSize;
}
unsigned int ArrayData::size(){
return m_size;
}

View File

@@ -0,0 +1,133 @@
/*
* ArrayEntryType.cpp
*
* Created on: Nov 14, 2012
* Author: Mitchell Wills
*/
#include "networktables2/type/ArrayEntryType.h"
ArrayEntryType::ArrayEntryType(TypeId id, NetworkTableEntryType& elementType)
: ComplexEntryType(id, "Array"), m_elementType(elementType){ //TODO super(id, "Array of [" + elementType.name + "]");
}
EntryValue ArrayEntryType::copyElement(EntryValue value){
return m_elementType.copyValue(value);
}
void ArrayEntryType::deleteElement(EntryValue value){
m_elementType.deleteValue(value);
}
bool ArrayEntryType::areElementsEqual(EntryValue v1, EntryValue v2){
return m_elementType.areEqual(v1, v2);
}
void ArrayEntryType::sendValue(EntryValue value, DataIOStream& os) {
ArrayEntryData* dataArray = (ArrayEntryData*) value.ptr;
/*if (dataArray->length > 255) {//TODO throw better exception
throw IOException("Cannot write " + value + " as " + name + ". Arrays have a max length of 255 values");
}*/
os.writeByte(dataArray->length);
for (int i = 0; i < dataArray->length; ++i) {
m_elementType.sendValue(dataArray->array[i], os);
}
}
EntryValue ArrayEntryType::readValue(DataIOStream& is) {
uint8_t length = is.readByte();
EntryValue* array = (EntryValue*)malloc(length*sizeof(EntryValue));//TODO cache object arrays
for (int i = 0; i < length; ++i) {
array[i] = m_elementType.readValue(is);
}
ArrayEntryData* dataArray = (ArrayEntryData*)malloc(sizeof(ArrayEntryData));
dataArray->length = length;
dataArray->array = array;
EntryValue eValue;
eValue.ptr = dataArray;
return eValue;
}
EntryValue ArrayEntryType::copyValue(EntryValue value){
ArrayEntryData* otherDataArray = (ArrayEntryData*) value.ptr;
EntryValue* array = (EntryValue*)malloc(otherDataArray->length*sizeof(EntryValue));
for (int i = 0; i < otherDataArray->length; ++i) {
array[i] = copyElement(otherDataArray->array[i]);
}
ArrayEntryData* dataArray = (ArrayEntryData*)malloc(sizeof(ArrayEntryData));
dataArray->length = otherDataArray->length;
dataArray->array = array;
EntryValue eValue;
eValue.ptr = dataArray;
return eValue;
}
void ArrayEntryType::deleteValue(EntryValue value){
ArrayEntryData* dataArray = (ArrayEntryData*) value.ptr;
if(dataArray!=NULL){
for (int i = 0; i < dataArray->length; ++i) {
deleteElement(dataArray->array[i]);
}
if(dataArray->array != NULL)
free(dataArray->array);
free(dataArray);
}
}
bool ArrayEntryType::areEqual(EntryValue v1, EntryValue v2) {
ArrayEntryData* a1 = (ArrayEntryData*) v1.ptr;
ArrayEntryData* a2 = (ArrayEntryData*) v2.ptr;
if(a1->length != a2->length)
return false;
for (int i = 0; i < a1->length; ++i) {
if(!areElementsEqual(a1->array[i], a2->array[i]))
return false;
}
return true;
}
EntryValue ArrayEntryType::internalizeValue(std::string& key, ComplexData& externalRepresentation, EntryValue currentInteralValue) {
// TODO: Argument 'key' appears unused.
ArrayData& externalArrayData = (ArrayData&)externalRepresentation;
EntryValue eValue = currentInteralValue;
ArrayEntryData* internalArray = (ArrayEntryData*) currentInteralValue.ptr;
if(internalArray == NULL){
internalArray = (ArrayEntryData*)malloc(sizeof(ArrayEntryData));
internalArray->length = 0;
internalArray->array = NULL;
eValue.ptr = internalArray;
}
if(internalArray->length==externalArrayData.size()){
for(int i = 0; i<internalArray->length; ++i){
deleteElement(internalArray->array[i]);
internalArray->array[i] = copyElement(externalArrayData._get(i));
}
}
else{
if(internalArray->array != NULL)
free(internalArray->array);
internalArray->length = externalArrayData.size();
if(internalArray->length == 0)
internalArray->array = NULL;
else
internalArray->array = (EntryValue*)malloc(externalArrayData.size()*sizeof(EntryValue));
for (int i = 0; i < internalArray->length; ++i) {
internalArray->array[i] = copyElement(externalArrayData._get(i));
}
}
return eValue;
}
void ArrayEntryType::exportValue(std::string& key, EntryValue internalData, ComplexData& externalRepresentation) {
ArrayEntryData* internalArray = (ArrayEntryData*) internalData.ptr;
ArrayData& externalArrayData = (ArrayData&)externalRepresentation;
externalArrayData.setSize(internalArray->length);
for(int i = 0; i<internalArray->length; ++i){
externalArrayData._set(i, copyElement(internalArray->array[i]));
}
}

View File

@@ -0,0 +1,32 @@
/*
* BooleanArray.cpp
*
* Created on: Nov 16, 2012
* Author: Mitchell Wills
*/
#include "networktables2/type/BooleanArray.h"
#include "networktables2/type/DefaultEntryTypes.h"
const TypeId BooleanArray::BOOLEAN_ARRAY_RAW_ID = 0x10;
ArrayEntryType BooleanArray::TYPE(BOOLEAN_ARRAY_RAW_ID, DefaultEntryTypes::BOOLEAN);
BooleanArray::BooleanArray() : ArrayData(TYPE) {
}
bool BooleanArray::get(int index){
return _get(index).b;
}
void BooleanArray::set(int index, bool value){
EntryValue eValue;
eValue.b = value;
_set(index, eValue);
}
void BooleanArray::add(bool value){
EntryValue eValue;
eValue.b = value;
_add(eValue);
}

View File

@@ -0,0 +1,15 @@
/*
* ComplexData.cpp
*
* Created on: Sep 24, 2012
* Author: Mitchell Wills
*/
#include "networktables2/type/ComplexData.h"
ComplexData::ComplexData(ComplexEntryType& _type) : type(_type){}
ComplexEntryType& ComplexData::GetType() {
return type;
}

View File

@@ -0,0 +1,15 @@
/*
* ComplexEntryType.cpp
*
* Created on: Sep 24, 2012
* Author: Mitchell Wills
*/
#include "networktables2/type/ComplexEntryType.h"
ComplexEntryType::ComplexEntryType(TypeId id, const char* name) : NetworkTableEntryType(id, name){}
bool ComplexEntryType::isComplex(){
return true;
}

View File

@@ -0,0 +1,96 @@
/*
* DefaultEntryTypes.cpp
*
* Created on: Sep 24, 2012
* Author: Mitchell Wills
*/
#include "networktables2/type/DefaultEntryTypes.h"
#include "networktables2/type/NetworkTableEntryType.h"
#include "networktables2/connection/DataIOStream.h"
#include "networktables2/type/BooleanArray.h"
#include "networktables2/type/NumberArray.h"
#include "networktables2/type/StringArray.h"
DefaultEntryTypes::BOOLEAN_t DefaultEntryTypes::BOOLEAN;
DefaultEntryTypes::DOUBLE_t DefaultEntryTypes::DOUBLE;
DefaultEntryTypes::STRING_t DefaultEntryTypes::STRING;
DefaultEntryTypes::BOOLEAN_t::BOOLEAN_t() : NetworkTableEntryType(BOOLEAN_RAW_ID, "Boolean"){}
void DefaultEntryTypes::BOOLEAN_t::sendValue(EntryValue value, DataIOStream& os) {
os.writeByte(value.b);
}
EntryValue DefaultEntryTypes::BOOLEAN_t::readValue(DataIOStream& is) {
EntryValue value;
value.b = (is.readByte()!=0);
return value;
}
bool DefaultEntryTypes::BOOLEAN_t::areEqual(EntryValue v1, EntryValue v2) {
return v1.b == v2.b;
}
DefaultEntryTypes::DOUBLE_t::DOUBLE_t() : NetworkTableEntryType(DOUBLE_RAW_ID, "Double"){}
void DefaultEntryTypes::DOUBLE_t::sendValue(EntryValue eValue, DataIOStream& os) {
uint64_t value = *reinterpret_cast<uint64_t*>(&eValue.f);
for(int i = 0; i<8; ++i){
os.writeByte((value>>56)&0xFF);
value<<=8;
}
}
EntryValue DefaultEntryTypes::DOUBLE_t::readValue(DataIOStream& is) {
uint64_t value = 0;
for(int i = 0; i<8; ++i){
value<<=8;
value |= (is.readByte()&0xFF);
}
EntryValue eValue;
eValue.f = *reinterpret_cast<double*>(&value);
return eValue;
}
bool DefaultEntryTypes::DOUBLE_t::areEqual(EntryValue v1, EntryValue v2) {
return v1.f == v2.f;
}
DefaultEntryTypes::STRING_t::STRING_t() : NetworkTableEntryType(STRING_RAW_ID, "String"){}
void DefaultEntryTypes::STRING_t::sendValue(EntryValue value, DataIOStream& os) {
os.writeString(*(std::string*)value.ptr);
}
EntryValue DefaultEntryTypes::STRING_t::readValue(DataIOStream& is) {
EntryValue value;
value.ptr = is.readString();
return value;
}
EntryValue DefaultEntryTypes::STRING_t::copyValue(EntryValue value){
if(value.ptr==NULL)
return value;
EntryValue newValue;
newValue.ptr = new std::string(*((std::string*)value.ptr));
return newValue;
}
void DefaultEntryTypes::STRING_t::deleteValue(EntryValue value){
if(value.ptr!=NULL)
delete (std::string*)value.ptr;
}
bool DefaultEntryTypes::STRING_t::areEqual(EntryValue v1, EntryValue v2) {
std::string* str1 = (std::string*)v1.ptr;
std::string* str2 = (std::string*)v2.ptr;
return str1->compare(*str2)==0;
}
void DefaultEntryTypes::registerTypes(NetworkTableEntryTypeManager* manager){
manager->RegisterType(BOOLEAN);
manager->RegisterType(DOUBLE);
manager->RegisterType(STRING);
manager->RegisterType(BooleanArray::TYPE);
manager->RegisterType(NumberArray::TYPE);
manager->RegisterType(StringArray::TYPE);
}

View File

@@ -0,0 +1,25 @@
/*
* NetworkTableEntryType.cpp
*
* Created on: Sep 16, 2012
* Author: Mitchell Wills
*/
#include "networktables2/type/NetworkTableEntryType.h"
NetworkTableEntryType::NetworkTableEntryType(TypeId _id, const char* _name) : id(_id), name(_name){
}
NetworkTableEntryType::~NetworkTableEntryType(){
}
EntryValue NetworkTableEntryType::copyValue(EntryValue value){
return value;
}
void NetworkTableEntryType::deleteValue(EntryValue value){
}
bool NetworkTableEntryType::isComplex(){
return false;
}

View File

@@ -0,0 +1,25 @@
/*
* NetworkTableEntryTypeManager.cpp
*
* Created on: Sep 16, 2012
* Author: Mitchell Wills
*/
#include "networktables2/type/NetworkTableEntryTypeManager.h"
#include "networktables2/type/DefaultEntryTypes.h"
NetworkTableEntryType* NetworkTableEntryTypeManager::GetType(TypeId id){
return entryTypes[id];
}
void NetworkTableEntryTypeManager::RegisterType(NetworkTableEntryType& type){
entryTypes[type.id] = &type;
}
NetworkTableEntryTypeManager::NetworkTableEntryTypeManager(){
for(int i = 0; i<MAX_NUM_TABLE_ENTRY_TYPES; ++i)
entryTypes[i] = NULL;
DefaultEntryTypes::registerTypes(this);
}

View File

@@ -0,0 +1,32 @@
/*
* NumberArray.cpp
*
* Created on: Nov 16, 2012
* Author: Mitchell Wills
*/
#include "networktables2/type/NumberArray.h"
#include "networktables2/type/DefaultEntryTypes.h"
const TypeId NumberArray::NUMBER_ARRAY_RAW_ID = 0x11;
ArrayEntryType NumberArray::TYPE(NUMBER_ARRAY_RAW_ID, DefaultEntryTypes::DOUBLE);
NumberArray::NumberArray() : ArrayData(TYPE) {
}
double NumberArray::get(int index){
return _get(index).f;
}
void NumberArray::set(int index, double value){
EntryValue eValue;
eValue.f = value;
_set(index, eValue);
}
void NumberArray::add(double value){
EntryValue eValue;
eValue.f = value;
_add(eValue);
}

View File

@@ -0,0 +1,32 @@
/*
* StringArray
*
* Created on: Nov 16, 2012
* Author: Mitchell Wills
*/
#include "networktables2/type/StringArray.h"
#include "networktables2/type/DefaultEntryTypes.h"
const TypeId StringArray::STRING_ARRAY_RAW_ID = 0x12;
ArrayEntryType StringArray::TYPE(STRING_ARRAY_RAW_ID, DefaultEntryTypes::STRING);
StringArray::StringArray() : ArrayData(TYPE) {
}
std::string StringArray::get(int index){
return *(std::string*)_get(index).ptr;
}
void StringArray::set(int index, std::string value){
EntryValue eValue;
eValue.ptr = &value;
_set(index, eValue);
}
void StringArray::add(std::string value){
EntryValue eValue;
eValue.ptr = &value;
_add(eValue);
}

View File

@@ -0,0 +1,14 @@
/*
* EOFException.cpp
*
* Created on: Oct 1, 2012
* Author: Mitchell Wills
*/
#include "networktables2/util/EOFException.h"
EOFException::EOFException() : IOException("End of File"){}
EOFException::~EOFException() throw(){}
bool EOFException::isEOF(){
return true;
}

View File

@@ -0,0 +1,24 @@
/*
* IOException.cpp
*
* Created on: Oct 1, 2012
* Author: Mitchell Wills
*/
#include "networktables2/util/IOException.h"
#include <stdlib.h>
#include <string.h>
IOException::IOException(const char* msg) : message(strdup(msg)), errorValue(0){}
IOException::IOException(const char* msg, int _errorValue) : message(strdup(msg)), errorValue(_errorValue){}
const char* IOException::what(){
return message;
}
bool IOException::isEOF(){return false;}
IOException::~IOException() throw (){
free((void*)message);
}

View File

@@ -0,0 +1,16 @@
/*
* IllegalStateException.cpp
*
* Created on: Sep 16, 2012
* Author: Mitchell Wills
*/
#include "networktables2/util/IllegalStateException.h"
IllegalStateException::IllegalStateException(const char* msg)
: message(msg)
{
}
IllegalStateException::~IllegalStateException() throw ()
{
}

View File

@@ -0,0 +1,27 @@
/*
* StringCache.cpp
*
* Created on: Oct 16, 2012
* Author: Mitchell Wills
*/
#include <map>
#include "networktables2/util/StringCache.h"
using namespace std;
StringCache::StringCache(){
}
StringCache::~StringCache(){
}
std::string& StringCache::Get(const std::string& input){
map<std::string, std::string>::iterator itr = cache.find(input);
if(itr != cache.end()){
return itr->second;
}
else{
cache[input] = Calc(input);
return cache[input];
}
}

View File

@@ -0,0 +1,24 @@
/*
* TableKeyNotDefinedException.cpp
*
* Created on: Sep 22, 2012
* Author: Mitchell Wills
*/
#include "tables/TableKeyNotDefinedException.h"
#include <stdlib.h>
#include <cstring>
#define MESSAGE_PREFIX "Unkown Table Key: "
TableKeyNotDefinedException::TableKeyNotDefinedException(const std::string key):
msg(MESSAGE_PREFIX + key){
}
const char* TableKeyNotDefinedException::what(){
return msg.c_str();
}
TableKeyNotDefinedException::~TableKeyNotDefinedException() throw(){
}