Skip to content

Commit d409cc5

Browse files
committed
Add NetworkTableSource
1 parent 58635d3 commit d409cc5

File tree

13 files changed

+353
-13
lines changed

13 files changed

+353
-13
lines changed

core/src/main/java/edu/wpi/grip/core/GripCoreModule.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import edu.wpi.grip.core.sources.HttpSource;
1212
import edu.wpi.grip.core.sources.ImageFileSource;
1313
import edu.wpi.grip.core.sources.MultiImageFileSource;
14+
import edu.wpi.grip.core.sources.NetworkTableEntrySource;
1415
import edu.wpi.grip.core.util.ExceptionWitness;
1516
import edu.wpi.grip.core.util.GripMode;
1617

@@ -144,6 +145,9 @@ public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
144145
install(new FactoryModuleBuilder()
145146
.implement(HttpSource.class, HttpSource.class)
146147
.build(HttpSource.Factory.class));
148+
install(new FactoryModuleBuilder()
149+
.implement(NetworkTableEntrySource.class, NetworkTableEntrySource.class)
150+
.build(NetworkTableEntrySource.Factory.class));
147151

148152
install(new FactoryModuleBuilder().build(ExceptionWitness.Factory.class));
149153
}

core/src/main/java/edu/wpi/grip/core/Source.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import edu.wpi.grip.core.sources.HttpSource;
66
import edu.wpi.grip.core.sources.ImageFileSource;
77
import edu.wpi.grip.core.sources.MultiImageFileSource;
8+
import edu.wpi.grip.core.sources.NetworkTableEntrySource;
89
import edu.wpi.grip.core.util.ExceptionWitness;
910

1011
import com.google.common.collect.ImmutableList;
@@ -111,6 +112,8 @@ public static class SourceFactoryImpl implements SourceFactory {
111112
MultiImageFileSource.Factory multiImageFactory;
112113
@Inject
113114
HttpSource.Factory httpFactory;
115+
@Inject
116+
NetworkTableEntrySource.Factory networkTableEntryFactory;
114117

115118
@Override
116119
public Source create(Class<?> type, Properties properties) throws IOException {
@@ -122,6 +125,8 @@ public Source create(Class<?> type, Properties properties) throws IOException {
122125
return multiImageFactory.create(properties);
123126
} else if (type.isAssignableFrom(HttpSource.class)) {
124127
return httpFactory.create(properties);
128+
} else if (type.isAssignableFrom(NetworkTableEntrySource.class)) {
129+
return networkTableEntryFactory.create(properties);
125130
} else {
126131
throw new IllegalArgumentException(type + " was not a valid type");
127132
}

core/src/main/java/edu/wpi/grip/core/operations/network/GripNetworkModule.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,10 @@ protected void configure() {
3434
bind(ROSNetworkPublisherFactory.class)
3535
.annotatedWith(Names.named("rosManager"))
3636
.to(ROSManager.class);
37+
38+
// Network receiver bindings
39+
bind(MapNetworkReceiverFactory.class)
40+
.annotatedWith(Names.named("ntManager"))
41+
.to(NTManager.class);
3742
}
3843
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package edu.wpi.grip.core.operations.network;
2+
3+
4+
/**
5+
* A factory to create {@link NetworkReceiver NetworkRecievers}.
6+
*/
7+
@FunctionalInterface
8+
public interface MapNetworkReceiverFactory {
9+
NetworkReceiver create(String path);
10+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package edu.wpi.grip.core.operations.network;
2+
3+
import static com.google.common.base.Preconditions.checkArgument;
4+
5+
/**
6+
* Manages the interface between the {@link PublishAnnotatedOperation} and the actual network
7+
* protocol implemented by a specific {@link Manager}.
8+
*/
9+
public abstract class NetworkReceiver implements AutoCloseable {
10+
11+
protected final String path;
12+
13+
/**
14+
* Create a new NetworkReceiver with the specified path.
15+
*
16+
* @param path The path of the object to get
17+
*/
18+
public NetworkReceiver(String path) {
19+
checkArgument(!path.isEmpty(), "Name cannot be an empty string");
20+
this.path = path;
21+
}
22+
23+
/**
24+
* Get the value of the object.
25+
*
26+
* @return The value of this NetworkReceiver
27+
*/
28+
public abstract Object getValue();
29+
30+
/**
31+
* Close the network reciever. This should not throw an exception.
32+
*/
33+
public abstract void close();
34+
}

core/src/main/java/edu/wpi/grip/core/operations/network/networktables/NTManager.java

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import edu.wpi.grip.core.operations.network.Manager;
66
import edu.wpi.grip.core.operations.network.MapNetworkPublisher;
77
import edu.wpi.grip.core.operations.network.MapNetworkPublisherFactory;
8+
import edu.wpi.grip.core.operations.network.MapNetworkReceiverFactory;
9+
import edu.wpi.grip.core.operations.network.NetworkReceiver;
810
import edu.wpi.grip.core.settings.ProjectSettings;
911
import edu.wpi.grip.core.util.GripMode;
1012

@@ -25,7 +27,7 @@
2527
import java.util.concurrent.atomic.AtomicInteger;
2628
import java.util.logging.Level;
2729
import java.util.logging.Logger;
28-
30+
import javafx.beans.property.SimpleObjectProperty;
2931
import javax.inject.Inject;
3032

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

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

121123
@Override
122124
public <P> MapNetworkPublisher<P> create(Set<String> keys) {
123-
// Keep track of ever publisher created.
124-
publisherCount.getAndAdd(1);
125+
// Keep track of every publisher created.
126+
count.getAndAdd(1);
125127
return new NTPublisher<>(keys);
126128
}
127129

130+
@Override
131+
public NetworkReceiver create(String path) {
132+
count.getAndAdd(1);
133+
return new NTReceiver(path);
134+
}
135+
136+
private static final class NTReceiver extends NetworkReceiver {
137+
138+
private int entryListenerFunctionUid;
139+
private final SimpleObjectProperty objectProperty = new SimpleObjectProperty();
140+
141+
protected NTReceiver(String path) {
142+
super(path);
143+
NetworkTablesJNI.addConnectionListener((uid, connected, conn) -> {
144+
System.out.println("\t" + uid + "\t" + connected + "\t" + conn);
145+
if (connected) {
146+
addListener();
147+
NetworkTablesJNI.removeConnectionListener(uid);
148+
}
149+
}, true);
150+
synchronized (NetworkTable.class) {
151+
NetworkTable.initialize();
152+
}
153+
}
154+
155+
private void addListener() {
156+
entryListenerFunctionUid = NetworkTablesJNI.addEntryListener(path, (uid, key, value, flags)
157+
-> objectProperty.set(value),
158+
ITable.NOTIFY_IMMEDIATE
159+
| ITable.NOTIFY_NEW
160+
| ITable.NOTIFY_UPDATE
161+
| ITable.NOTIFY_DELETE
162+
| ITable.NOTIFY_LOCAL);
163+
}
164+
165+
@Override
166+
public Object getValue() {
167+
return objectProperty.getValue();
168+
}
169+
170+
@Override
171+
public void close() {
172+
NetworkTablesJNI.removeEntryListener(entryListenerFunctionUid);
173+
174+
synchronized (NetworkTable.class) {
175+
// This publisher is no longer used.
176+
if (NTManager.count.addAndGet(-1) == 0) {
177+
// We are the last publisher so shut it down
178+
NetworkTable.shutdown();
179+
}
180+
}
181+
}
182+
}
183+
128184
private static final class NTPublisher<P> extends MapNetworkPublisher<P> {
129185
private final ImmutableSet<String> keys;
130186
private Optional<String> name = Optional.empty();
@@ -184,7 +240,7 @@ public void close() {
184240
}
185241
synchronized (NetworkTable.class) {
186242
// This publisher is no longer used.
187-
if (NTManager.publisherCount.addAndGet(-1) == 0) {
243+
if (NTManager.count.addAndGet(-1) == 0) {
188244
// We are the last publisher so shut it down
189245
NetworkTable.shutdown();
190246
}

core/src/main/java/edu/wpi/grip/core/sockets/SocketHints.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,5 +193,13 @@ public static SocketHint<Number> createNumberSocketHint(final String identifier,
193193
defaultValue) {
194194
return createNumberSocketHintBuilder(identifier, defaultValue).build();
195195
}
196+
197+
public static SocketHint<String> createStringSocketHint(final String identifier,
198+
String defaultValue) {
199+
return new SocketHint.Builder<String>(String.class)
200+
.identifier(identifier)
201+
.initialValue(defaultValue)
202+
.build();
203+
}
196204
}
197205
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package edu.wpi.grip.core.sources;
2+
3+
import edu.wpi.grip.core.Source;
4+
import edu.wpi.grip.core.events.SourceRemovedEvent;
5+
import edu.wpi.grip.core.operations.network.MapNetworkReceiverFactory;
6+
import edu.wpi.grip.core.operations.network.NetworkReceiver;
7+
import edu.wpi.grip.core.sockets.OutputSocket;
8+
import edu.wpi.grip.core.sockets.SocketHints;
9+
import edu.wpi.grip.core.util.ExceptionWitness;
10+
11+
import com.google.common.collect.ImmutableList;
12+
import com.google.common.eventbus.Subscribe;
13+
import com.google.inject.assistedinject.Assisted;
14+
import com.google.inject.assistedinject.AssistedInject;
15+
import com.google.inject.name.Named;
16+
import com.thoughtworks.xstream.annotations.XStreamAlias;
17+
18+
import java.util.List;
19+
import java.util.Properties;
20+
21+
/**
22+
* Provides a way to get a {@link Types Type} from a NetworkTable that GRIP is connected to.
23+
*/
24+
@XStreamAlias("grip:NetworkValue")
25+
public class NetworkTableEntrySource extends Source {
26+
27+
private static final String PATH_PROPERTY = "networktable_path";
28+
private static final String TYPE_PROPERTY = "BOOLEAN";
29+
30+
private final OutputSocket output;
31+
private final String path;
32+
private final Types type;
33+
private NetworkReceiver networkReceiver;
34+
35+
public interface Factory {
36+
NetworkTableEntrySource create(Properties properties);
37+
38+
NetworkTableEntrySource create(String path, Types type);
39+
}
40+
41+
public enum Types {
42+
BOOLEAN, NUMBER, STRING;
43+
44+
@Override
45+
public String toString() {
46+
return super.toString().charAt(0) + super.toString().substring(1).toLowerCase();
47+
}
48+
49+
}
50+
51+
@AssistedInject
52+
NetworkTableEntrySource(
53+
ExceptionWitness.Factory exceptionWitnessFactory,
54+
OutputSocket.Factory osf,
55+
@Named("ntManager") MapNetworkReceiverFactory ntManager,
56+
@Assisted Properties properties) {
57+
this(exceptionWitnessFactory,
58+
osf,
59+
ntManager,
60+
properties.getProperty(PATH_PROPERTY),
61+
Types.valueOf(properties.getProperty(TYPE_PROPERTY)));
62+
}
63+
64+
@AssistedInject
65+
NetworkTableEntrySource(
66+
ExceptionWitness.Factory exceptionWitnessFactory,
67+
OutputSocket.Factory osf,
68+
@Named("ntManager") MapNetworkReceiverFactory ntManager,
69+
@Assisted String path,
70+
@Assisted Types type) {
71+
super(exceptionWitnessFactory);
72+
this.path = path;
73+
this.type = type;
74+
networkReceiver = ntManager.create(path);
75+
76+
switch (type) {
77+
case BOOLEAN:
78+
output = osf.create(
79+
SocketHints.Outputs.createBooleanSocketHint(Types.BOOLEAN.toString(), false));
80+
break;
81+
case NUMBER:
82+
output = osf.create(
83+
SocketHints.Outputs.createNumberSocketHint(Types.NUMBER.toString(), 0.0));
84+
break;
85+
case STRING:
86+
output = osf.create(
87+
SocketHints.Outputs.createStringSocketHint(Types.STRING.toString(), ""));
88+
break;
89+
default:
90+
throw new IllegalArgumentException("Invalid NetworkTable source type");
91+
}
92+
}
93+
94+
@Override
95+
public String getName() {
96+
return path;
97+
}
98+
99+
@Override
100+
protected List<OutputSocket> createOutputSockets() {
101+
return ImmutableList.of(
102+
output
103+
);
104+
}
105+
106+
@Override
107+
protected boolean updateOutputSockets() {
108+
try {
109+
output.setValue(networkReceiver.getValue());
110+
} catch (ClassCastException ex) {
111+
getExceptionWitness().flagException(ex, getName() + " is not of type "
112+
+ output.getSocketHint().getTypeLabel());
113+
return false;
114+
}
115+
return true;
116+
}
117+
118+
@Override
119+
public Properties getProperties() {
120+
Properties properties = new Properties();
121+
properties.setProperty(PATH_PROPERTY, path);
122+
properties.setProperty(TYPE_PROPERTY, type.toString().toUpperCase());
123+
return properties;
124+
}
125+
126+
@Override
127+
public void initialize() {
128+
updateOutputSockets();
129+
}
130+
131+
@Subscribe
132+
public void onSourceRemovedEvent(SourceRemovedEvent event) {
133+
if (event.getSource() == this) {
134+
networkReceiver.close();
135+
}
136+
}
137+
138+
}

core/src/test/java/edu/wpi/grip/core/operations/network/MockGripNetworkModule.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,9 @@ protected void configure() {
2121
bind(ROSNetworkPublisherFactory.class)
2222
.annotatedWith(Names.named("rosManager"))
2323
.to(MockROSManager.class);
24+
25+
bind(MapNetworkReceiverFactory.class)
26+
.annotatedWith(Names.named("ntManager"))
27+
.to(MockNetworkReceiver.class);
2428
}
2529
}

core/src/test/java/edu/wpi/grip/core/operations/network/MockMapNetworkPublisher.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
@SuppressWarnings("PMD.UncommentedEmptyMethodBody")
99
public class MockMapNetworkPublisher<T> extends MapNetworkPublisher<T> implements
10-
MapNetworkPublisherFactory {
10+
MapNetworkPublisherFactory, MapNetworkReceiverFactory {
1111

1212
public MockMapNetworkPublisher() {
1313
this(Collections.emptySet());
@@ -46,4 +46,9 @@ public void close() {
4646
public <T> MapNetworkPublisher<T> create(Set<String> keys) {
4747
return new MockMapNetworkPublisher<>(keys);
4848
}
49+
50+
@Override
51+
public NetworkReceiver create(String path) {
52+
return new MockNetworkReceiver(path);
53+
}
4954
}

0 commit comments

Comments
 (0)