Skip to content

Commit

Permalink
Add NetworkTableSource
Browse files Browse the repository at this point in the history
  • Loading branch information
AustinShalit committed Jul 30, 2016
1 parent 58635d3 commit d409cc5
Show file tree
Hide file tree
Showing 13 changed files with 353 additions and 13 deletions.
4 changes: 4 additions & 0 deletions core/src/main/java/edu/wpi/grip/core/GripCoreModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import edu.wpi.grip.core.sources.HttpSource;
import edu.wpi.grip.core.sources.ImageFileSource;
import edu.wpi.grip.core.sources.MultiImageFileSource;
import edu.wpi.grip.core.sources.NetworkTableEntrySource;
import edu.wpi.grip.core.util.ExceptionWitness;
import edu.wpi.grip.core.util.GripMode;

Expand Down Expand Up @@ -144,6 +145,9 @@ public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
install(new FactoryModuleBuilder()
.implement(HttpSource.class, HttpSource.class)
.build(HttpSource.Factory.class));
install(new FactoryModuleBuilder()
.implement(NetworkTableEntrySource.class, NetworkTableEntrySource.class)
.build(NetworkTableEntrySource.Factory.class));

install(new FactoryModuleBuilder().build(ExceptionWitness.Factory.class));
}
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/java/edu/wpi/grip/core/Source.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import edu.wpi.grip.core.sources.HttpSource;
import edu.wpi.grip.core.sources.ImageFileSource;
import edu.wpi.grip.core.sources.MultiImageFileSource;
import edu.wpi.grip.core.sources.NetworkTableEntrySource;
import edu.wpi.grip.core.util.ExceptionWitness;

import com.google.common.collect.ImmutableList;
Expand Down Expand Up @@ -111,6 +112,8 @@ public static class SourceFactoryImpl implements SourceFactory {
MultiImageFileSource.Factory multiImageFactory;
@Inject
HttpSource.Factory httpFactory;
@Inject
NetworkTableEntrySource.Factory networkTableEntryFactory;

@Override
public Source create(Class<?> type, Properties properties) throws IOException {
Expand All @@ -122,6 +125,8 @@ public Source create(Class<?> type, Properties properties) throws IOException {
return multiImageFactory.create(properties);
} else if (type.isAssignableFrom(HttpSource.class)) {
return httpFactory.create(properties);
} else if (type.isAssignableFrom(NetworkTableEntrySource.class)) {
return networkTableEntryFactory.create(properties);
} else {
throw new IllegalArgumentException(type + " was not a valid type");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,10 @@ protected void configure() {
bind(ROSNetworkPublisherFactory.class)
.annotatedWith(Names.named("rosManager"))
.to(ROSManager.class);

// Network receiver bindings
bind(MapNetworkReceiverFactory.class)
.annotatedWith(Names.named("ntManager"))
.to(NTManager.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package edu.wpi.grip.core.operations.network;


/**
* A factory to create {@link NetworkReceiver NetworkRecievers}.
*/
@FunctionalInterface
public interface MapNetworkReceiverFactory {
NetworkReceiver create(String path);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package edu.wpi.grip.core.operations.network;

import static com.google.common.base.Preconditions.checkArgument;

/**
* Manages the interface between the {@link PublishAnnotatedOperation} and the actual network
* protocol implemented by a specific {@link Manager}.
*/
public abstract class NetworkReceiver implements AutoCloseable {

protected final String path;

/**
* Create a new NetworkReceiver with the specified path.
*
* @param path The path of the object to get
*/
public NetworkReceiver(String path) {
checkArgument(!path.isEmpty(), "Name cannot be an empty string");
this.path = path;
}

/**
* Get the value of the object.
*
* @return The value of this NetworkReceiver
*/
public abstract Object getValue();

/**
* Close the network reciever. This should not throw an exception.
*/
public abstract void close();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import edu.wpi.grip.core.operations.network.Manager;
import edu.wpi.grip.core.operations.network.MapNetworkPublisher;
import edu.wpi.grip.core.operations.network.MapNetworkPublisherFactory;
import edu.wpi.grip.core.operations.network.MapNetworkReceiverFactory;
import edu.wpi.grip.core.operations.network.NetworkReceiver;
import edu.wpi.grip.core.settings.ProjectSettings;
import edu.wpi.grip.core.util.GripMode;

Expand All @@ -25,7 +27,7 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

import javafx.beans.property.SimpleObjectProperty;
import javax.inject.Inject;

import static com.google.common.base.Preconditions.checkNotNull;
Expand All @@ -34,11 +36,11 @@
* This class encapsulates the way we map various settings to the global NetworkTables state.
*/
@Singleton
public class NTManager implements Manager, MapNetworkPublisherFactory {
public class NTManager implements Manager, MapNetworkPublisherFactory, MapNetworkReceiverFactory {
/*
* Nasty hack that is unavoidable because of how NetworkTables works.
*/
private static final AtomicInteger publisherCount = new AtomicInteger(0);
private static final AtomicInteger count = new AtomicInteger(0);

/**
* Information from: https://github.com/PeterJohnson/ntcore/blob/master/src/Log.h and
Expand Down Expand Up @@ -120,11 +122,65 @@ public void updateSettings(ProjectSettingsChangedEvent event) {

@Override
public <P> MapNetworkPublisher<P> create(Set<String> keys) {
// Keep track of ever publisher created.
publisherCount.getAndAdd(1);
// Keep track of every publisher created.
count.getAndAdd(1);
return new NTPublisher<>(keys);
}

@Override
public NetworkReceiver create(String path) {
count.getAndAdd(1);
return new NTReceiver(path);
}

private static final class NTReceiver extends NetworkReceiver {

private int entryListenerFunctionUid;
private final SimpleObjectProperty objectProperty = new SimpleObjectProperty();

protected NTReceiver(String path) {
super(path);
NetworkTablesJNI.addConnectionListener((uid, connected, conn) -> {
System.out.println("\t" + uid + "\t" + connected + "\t" + conn);
if (connected) {
addListener();
NetworkTablesJNI.removeConnectionListener(uid);
}
}, true);
synchronized (NetworkTable.class) {
NetworkTable.initialize();
}
}

private void addListener() {
entryListenerFunctionUid = NetworkTablesJNI.addEntryListener(path, (uid, key, value, flags)
-> objectProperty.set(value),
ITable.NOTIFY_IMMEDIATE
| ITable.NOTIFY_NEW
| ITable.NOTIFY_UPDATE
| ITable.NOTIFY_DELETE
| ITable.NOTIFY_LOCAL);
}

@Override
public Object getValue() {
return objectProperty.getValue();
}

@Override
public void close() {
NetworkTablesJNI.removeEntryListener(entryListenerFunctionUid);

synchronized (NetworkTable.class) {
// This publisher is no longer used.
if (NTManager.count.addAndGet(-1) == 0) {
// We are the last publisher so shut it down
NetworkTable.shutdown();
}
}
}
}

private static final class NTPublisher<P> extends MapNetworkPublisher<P> {
private final ImmutableSet<String> keys;
private Optional<String> name = Optional.empty();
Expand Down Expand Up @@ -184,7 +240,7 @@ public void close() {
}
synchronized (NetworkTable.class) {
// This publisher is no longer used.
if (NTManager.publisherCount.addAndGet(-1) == 0) {
if (NTManager.count.addAndGet(-1) == 0) {
// We are the last publisher so shut it down
NetworkTable.shutdown();
}
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/java/edu/wpi/grip/core/sockets/SocketHints.java
Original file line number Diff line number Diff line change
Expand Up @@ -193,5 +193,13 @@ public static SocketHint<Number> createNumberSocketHint(final String identifier,
defaultValue) {
return createNumberSocketHintBuilder(identifier, defaultValue).build();
}

public static SocketHint<String> createStringSocketHint(final String identifier,
String defaultValue) {
return new SocketHint.Builder<String>(String.class)
.identifier(identifier)
.initialValue(defaultValue)
.build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package edu.wpi.grip.core.sources;

import edu.wpi.grip.core.Source;
import edu.wpi.grip.core.events.SourceRemovedEvent;
import edu.wpi.grip.core.operations.network.MapNetworkReceiverFactory;
import edu.wpi.grip.core.operations.network.NetworkReceiver;
import edu.wpi.grip.core.sockets.OutputSocket;
import edu.wpi.grip.core.sockets.SocketHints;
import edu.wpi.grip.core.util.ExceptionWitness;

import com.google.common.collect.ImmutableList;
import com.google.common.eventbus.Subscribe;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import com.google.inject.name.Named;
import com.thoughtworks.xstream.annotations.XStreamAlias;

import java.util.List;
import java.util.Properties;

/**
* Provides a way to get a {@link Types Type} from a NetworkTable that GRIP is connected to.
*/
@XStreamAlias("grip:NetworkValue")
public class NetworkTableEntrySource extends Source {

private static final String PATH_PROPERTY = "networktable_path";
private static final String TYPE_PROPERTY = "BOOLEAN";

private final OutputSocket output;
private final String path;
private final Types type;
private NetworkReceiver networkReceiver;

public interface Factory {
NetworkTableEntrySource create(Properties properties);

NetworkTableEntrySource create(String path, Types type);
}

public enum Types {
BOOLEAN, NUMBER, STRING;

@Override
public String toString() {
return super.toString().charAt(0) + super.toString().substring(1).toLowerCase();
}

}

@AssistedInject
NetworkTableEntrySource(
ExceptionWitness.Factory exceptionWitnessFactory,
OutputSocket.Factory osf,
@Named("ntManager") MapNetworkReceiverFactory ntManager,
@Assisted Properties properties) {
this(exceptionWitnessFactory,
osf,
ntManager,
properties.getProperty(PATH_PROPERTY),
Types.valueOf(properties.getProperty(TYPE_PROPERTY)));
}

@AssistedInject
NetworkTableEntrySource(
ExceptionWitness.Factory exceptionWitnessFactory,
OutputSocket.Factory osf,
@Named("ntManager") MapNetworkReceiverFactory ntManager,
@Assisted String path,
@Assisted Types type) {
super(exceptionWitnessFactory);
this.path = path;
this.type = type;
networkReceiver = ntManager.create(path);

switch (type) {
case BOOLEAN:
output = osf.create(
SocketHints.Outputs.createBooleanSocketHint(Types.BOOLEAN.toString(), false));
break;
case NUMBER:
output = osf.create(
SocketHints.Outputs.createNumberSocketHint(Types.NUMBER.toString(), 0.0));
break;
case STRING:
output = osf.create(
SocketHints.Outputs.createStringSocketHint(Types.STRING.toString(), ""));
break;
default:
throw new IllegalArgumentException("Invalid NetworkTable source type");
}
}

@Override
public String getName() {
return path;
}

@Override
protected List<OutputSocket> createOutputSockets() {
return ImmutableList.of(
output
);
}

@Override
protected boolean updateOutputSockets() {
try {
output.setValue(networkReceiver.getValue());
} catch (ClassCastException ex) {
getExceptionWitness().flagException(ex, getName() + " is not of type "
+ output.getSocketHint().getTypeLabel());
return false;
}
return true;
}

@Override
public Properties getProperties() {
Properties properties = new Properties();
properties.setProperty(PATH_PROPERTY, path);
properties.setProperty(TYPE_PROPERTY, type.toString().toUpperCase());
return properties;
}

@Override
public void initialize() {
updateOutputSockets();
}

@Subscribe
public void onSourceRemovedEvent(SourceRemovedEvent event) {
if (event.getSource() == this) {
networkReceiver.close();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,9 @@ protected void configure() {
bind(ROSNetworkPublisherFactory.class)
.annotatedWith(Names.named("rosManager"))
.to(MockROSManager.class);

bind(MapNetworkReceiverFactory.class)
.annotatedWith(Names.named("ntManager"))
.to(MockNetworkReceiver.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

@SuppressWarnings("PMD.UncommentedEmptyMethodBody")
public class MockMapNetworkPublisher<T> extends MapNetworkPublisher<T> implements
MapNetworkPublisherFactory {
MapNetworkPublisherFactory, MapNetworkReceiverFactory {

public MockMapNetworkPublisher() {
this(Collections.emptySet());
Expand Down Expand Up @@ -46,4 +46,9 @@ public void close() {
public <T> MapNetworkPublisher<T> create(Set<String> keys) {
return new MockMapNetworkPublisher<>(keys);
}

@Override
public NetworkReceiver create(String path) {
return new MockNetworkReceiver(path);
}
}
Loading

0 comments on commit d409cc5

Please sign in to comment.