Skip to content

Commit 089ec41

Browse files
committed
Merge pull request #468 from ThomasJClark/nt
Fix NTPublishOperation problems
2 parents 9fff323 + 344b453 commit 089ec41

File tree

8 files changed

+132
-33
lines changed

8 files changed

+132
-33
lines changed

core/src/main/java/edu/wpi/grip/core/operations/composite/BlobsReport.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public Mat getInput() {
6060
return this.input;
6161
}
6262

63-
@NTValue(key = "x")
63+
@NTValue(key = "x", weight = 0)
6464
public double[] getX() {
6565
final double[] x = new double[blobs.size()];
6666
for (int i = 0; i < blobs.size(); i++) {
@@ -69,7 +69,7 @@ public double[] getX() {
6969
return x;
7070
}
7171

72-
@NTValue(key = "y")
72+
@NTValue(key = "y", weight = 1)
7373
public double[] getY() {
7474
final double[] y = new double[blobs.size()];
7575
for (int i = 0; i < blobs.size(); i++) {
@@ -78,7 +78,7 @@ public double[] getY() {
7878
return y;
7979
}
8080

81-
@NTValue(key = "size")
81+
@NTValue(key = "size", weight = 2)
8282
public double[] getSize() {
8383
final double[] sizes = new double[blobs.size()];
8484
for (int i = 0; i < blobs.size(); i++) {

core/src/main/java/edu/wpi/grip/core/operations/composite/ContoursReport.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ private synchronized Rect[] computeBoundingBoxes() {
6363
return boundingBoxes.get();
6464
}
6565

66-
@NTValue(key = "area")
66+
@NTValue(key = "area", weight = 0)
6767
public double[] getArea() {
6868
final double[] areas = new double[(int) contours.size()];
6969
for (int i = 0; i < contours.size(); i++) {
@@ -72,7 +72,7 @@ public double[] getArea() {
7272
return areas;
7373
}
7474

75-
@NTValue(key = "centerX")
75+
@NTValue(key = "centerX", weight = 1)
7676
public double[] getCenterX() {
7777
final double[] centers = new double[(int) contours.size()];
7878
final Rect[] boundingBoxes = computeBoundingBoxes();
@@ -82,7 +82,7 @@ public double[] getCenterX() {
8282
return centers;
8383
}
8484

85-
@NTValue(key = "centerY")
85+
@NTValue(key = "centerY", weight = 2)
8686
public double[] getCenterY() {
8787
final double[] centers = new double[(int) contours.size()];
8888
final Rect[] boundingBoxes = computeBoundingBoxes();
@@ -92,7 +92,7 @@ public double[] getCenterY() {
9292
return centers;
9393
}
9494

95-
@NTValue(key = "width")
95+
@NTValue(key = "width", weight = 3)
9696
public synchronized double[] getWidth() {
9797
final double[] widths = new double[(int) contours.size()];
9898
final Rect[] boundingBoxes = computeBoundingBoxes();
@@ -102,7 +102,7 @@ public synchronized double[] getWidth() {
102102
return widths;
103103
}
104104

105-
@NTValue(key = "height")
105+
@NTValue(key = "height", weight = 4)
106106
public synchronized double[] getHeights() {
107107
final double[] heights = new double[(int) contours.size()];
108108
final Rect[] boundingBoxes = computeBoundingBoxes();
@@ -112,7 +112,7 @@ public synchronized double[] getHeights() {
112112
return heights;
113113
}
114114

115-
@NTValue(key = "solidity")
115+
@NTValue(key = "solidity", weight = 5)
116116
public synchronized double[] getSolidity() {
117117
final double[] solidities = new double[(int) contours.size()];
118118
Mat hull = new Mat();

core/src/main/java/edu/wpi/grip/core/operations/composite/LinesReport.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public List<Line> getLines() {
7777
return this.lines;
7878
}
7979

80-
@NTValue(key = "x1")
80+
@NTValue(key = "x1", weight = 0)
8181
public double[] getX1() {
8282
final double[] x1 = new double[lines.size()];
8383
for (int i = 0; i < lines.size(); i++) {
@@ -86,7 +86,7 @@ public double[] getX1() {
8686
return x1;
8787
}
8888

89-
@NTValue(key = "y1")
89+
@NTValue(key = "y1", weight = 1)
9090
public double[] getY1() {
9191
final double[] y1 = new double[lines.size()];
9292
for (int i = 0; i < lines.size(); i++) {
@@ -95,7 +95,7 @@ public double[] getY1() {
9595
return y1;
9696
}
9797

98-
@NTValue(key = "x2")
98+
@NTValue(key = "x2", weight = 2)
9999
public double[] getX2() {
100100
final double[] x2 = new double[lines.size()];
101101
for (int i = 0; i < lines.size(); i++) {
@@ -104,7 +104,7 @@ public double[] getX2() {
104104
return x2;
105105
}
106106

107-
@NTValue(key = "y2")
107+
@NTValue(key = "y2", weight = 3)
108108
public double[] getY2() {
109109
final double[] y2 = new double[lines.size()];
110110
for (int i = 0; i < lines.size(); i++) {
@@ -113,7 +113,7 @@ public double[] getY2() {
113113
return y2;
114114
}
115115

116-
@NTValue(key = "length")
116+
@NTValue(key = "length", weight = 4)
117117
public double[] getLength() {
118118
final double[] length = new double[lines.size()];
119119
for (int i = 0; i < lines.size(); i++) {
@@ -122,7 +122,7 @@ public double[] getLength() {
122122
return length;
123123
}
124124

125-
@NTValue(key = "angle")
125+
@NTValue(key = "angle", weight = 5)
126126
public double[] getAngle() {
127127
final double[] angle = new double[lines.size()];
128128
for (int i = 0; i < lines.size(); i++) {

core/src/main/java/edu/wpi/grip/core/operations/networktables/NTNumber.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public NTNumber(Number number) {
1515
this.number = number.doubleValue();
1616
}
1717

18-
@NTValue
18+
@NTValue(weight = 0)
1919
public double getValue() {
2020
return number;
2121
}

core/src/main/java/edu/wpi/grip/core/operations/networktables/NTPublishOperation.java

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package edu.wpi.grip.core.operations.networktables;
22

33
import com.google.common.base.Throwables;
4+
import com.google.common.collect.ImmutableList;
5+
import com.google.common.collect.Ordering;
46
import com.google.common.eventbus.EventBus;
57
import edu.wpi.first.wpilibj.networktables.NetworkTable;
68
import edu.wpi.first.wpilibj.tables.ITable;
@@ -9,8 +11,8 @@
911
import java.io.InputStream;
1012
import java.lang.reflect.InvocationTargetException;
1113
import java.lang.reflect.Method;
12-
import java.util.ArrayList;
13-
import java.util.List;
14+
import java.util.Arrays;
15+
import java.util.Comparator;
1416
import java.util.Optional;
1517
import java.util.function.Function;
1618

@@ -26,7 +28,7 @@ public class NTPublishOperation<S, T extends NTPublishable> implements Operation
2628

2729
private final Class<S> type;
2830
private final Function<S, T> converter;
29-
private final List<Method> ntValueMethods = new ArrayList<>();
31+
private final ImmutableList<Method> ntValueMethods;
3032

3133
/**
3234
* Create a new publish operation for a socket type that implements {@link NTPublishable} directly
@@ -49,15 +51,27 @@ public NTPublishOperation(Class<S> socketType, Class<T> reportType, Function<S,
4951
this.type = checkNotNull(socketType, "Type was null");
5052
this.converter = checkNotNull(converter, "Converter was null");
5153

52-
// Any accessor method with an @NTValue annotation can be published to NetworkTables.
53-
for (Method method : reportType.getDeclaredMethods()) {
54-
if (method.getAnnotation(NTValue.class) != null) {
55-
if (method.getParameters().length > 0) {
54+
Comparator<Method> byWeight = Comparator.comparing(method -> method.getAnnotation(NTValue.class).weight());
55+
56+
// Any accessor method with an @NTValue annotation can be published to NetworkTables. We sort them by their
57+
// "weights" in order to avoid the issue of different JVM versions returning methods in a different order.
58+
this.ntValueMethods = ImmutableList.copyOf(Arrays.asList(reportType.getDeclaredMethods()).stream()
59+
.filter(method -> method.getAnnotation(NTValue.class) != null)
60+
.sorted(byWeight)
61+
.iterator());
62+
63+
// In order for NTPublishOperation to call the accessor methods, they must all have no parameters
64+
this.ntValueMethods.stream()
65+
.filter(method -> method.getParameterCount() > 0)
66+
.findAny()
67+
.ifPresent(method -> {
5668
throw new IllegalArgumentException("@NTValue method must have 0 parameters: " + method);
57-
}
69+
});
5870

59-
ntValueMethods.add(method);
60-
}
71+
// The weight thing doesn't help us if two methods have the same weight, since the JVM could put them in either
72+
// order.
73+
if (!Ordering.from(byWeight).isStrictlyOrdered(this.ntValueMethods)) {
74+
throw new IllegalArgumentException("@NTValue methods must have distinct weights: " + reportType);
6175
}
6276
}
6377

@@ -117,21 +131,36 @@ public void perform(InputSocket<?>[] inputs, OutputSocket<?>[] outputs) {
117131

118132
// Get a subtable to put the values in. Each NTPublishable has multiple properties that are published (such as
119133
// x, y, width, height, etc...), so they're grouped together in a subtable.
120-
final ITable subtable;
134+
final ITable table, subtable;
121135
synchronized (NetworkTable.class) {
122-
subtable = NetworkTable.getTable("GRIP").getSubTable(subtableName);
136+
table = NetworkTable.getTable("GRIP");
137+
subtable = table.getSubTable(subtableName);
123138
}
124139

125140
// For each NTValue method in the object being published, put it in the table if the the corresponding
126141
// checkbox is selected.
127142
try {
128143
for (Method method : ntValueMethods) {
129144
String key = method.getAnnotation(NTValue.class).key();
130-
if ((Boolean) inputs[i++].getValue().get()) {
131-
subtable.putValue(key, method.invoke(value));
145+
boolean publish = (Boolean) inputs[i++].getValue().get();
146+
147+
if (key.isEmpty()) {
148+
// If there is no key specified, put the value directly in the key of this report instead of a
149+
// subtable
150+
if (publish) {
151+
table.putValue(subtableName, method.invoke(value));
152+
} else {
153+
table.delete(subtableName);
154+
}
132155
} else {
133-
subtable.delete(key);
156+
// Otherwise, put a value in the subtable specified by the input to this operation
157+
if (publish) {
158+
subtable.putValue(key, method.invoke(value));
159+
} else {
160+
subtable.delete(key);
161+
}
134162
}
163+
135164
}
136165
} catch (IllegalAccessException | InvocationTargetException e) {
137166
Throwables.propagate(e);

core/src/main/java/edu/wpi/grip/core/operations/networktables/NTValue.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,15 @@
1717
* One potential way of having multiple values without using annotations might be to make the interface method return
1818
* a list, but this would prevent us from knowing how many values there are and what their names are without having
1919
* an instance of the object being published.
20+
* <p>
21+
* The weight of each accessor must be specified. This determines order of the inputs to {@link NTPublishOperation}.
22+
* It's important to specify weights if there are multiple keys because otherwise, different JVMs will return them in
23+
* different orders, leading to projects that are interpreted differently on different machines.
2024
*/
2125
@Retention(RetentionPolicy.RUNTIME)
2226
@Target(ElementType.METHOD)
2327
public @interface NTValue {
2428
String key() default "";
29+
30+
int weight();
2531
}

core/src/main/java/edu/wpi/grip/core/operations/networktables/NTVector2D.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ public NTVector2D(Size size) {
2424
this.y = size.height();
2525
}
2626

27-
@NTValue(key = "x")
27+
@NTValue(key = "x", weight = 0)
2828
public double getX() {
2929
return x;
3030
}
3131

32-
@NTValue(key = "y")
32+
@NTValue(key = "y", weight = 1)
3333
public double getY() {
3434
return y;
3535
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package edu.wpi.grip.core.operations.networktables;
2+
3+
import com.google.common.eventbus.EventBus;
4+
import edu.wpi.grip.core.InputSocket;
5+
import org.junit.Test;
6+
7+
import static org.junit.Assert.assertEquals;
8+
9+
/**
10+
* Tests for error handling in the publish operations' reflection stuff.
11+
*/
12+
public class NTPublishOperationTest {
13+
14+
private class Report implements NTPublishable {
15+
@NTValue(key = "foo", weight = 2)
16+
public double getFoo() {
17+
return 0.0;
18+
}
19+
20+
@NTValue(key = "bar", weight = 1)
21+
public double getBar() {
22+
return 1.0;
23+
}
24+
}
25+
26+
private class ReportWithNonDistinctWeights implements NTPublishable {
27+
@NTValue(key = "foo", weight = 1)
28+
public double getFoo() {
29+
return 0.0;
30+
}
31+
32+
@NTValue(key = "bar", weight = 1)
33+
public double getBar() {
34+
return 0.0;
35+
}
36+
}
37+
38+
private class ReportWithParameters implements NTPublishable {
39+
@NTValue(key = "foo", weight = 0)
40+
public double getFoo(boolean b) {
41+
return b ? 1.0 : 0.0;
42+
}
43+
}
44+
45+
@Test
46+
public void testNTValueOrder() {
47+
NTPublishOperation<Report, Report> ntPublishOperation = new NTPublishOperation<>(Report.class);
48+
InputSocket<?>[] sockets = ntPublishOperation.createInputSockets(new EventBus());
49+
50+
assertEquals(4, sockets.length);
51+
assertEquals("Publish bar", sockets[2].getSocketHint().getIdentifier());
52+
assertEquals("Publish foo", sockets[3].getSocketHint().getIdentifier());
53+
}
54+
55+
@Test(expected = IllegalArgumentException.class)
56+
public void testNonDistinctWeights() {
57+
new NTPublishOperation<>(ReportWithNonDistinctWeights.class);
58+
}
59+
60+
@Test(expected = IllegalArgumentException.class)
61+
public void testInvalidParameters() {
62+
new NTPublishOperation<>(ReportWithParameters.class);
63+
}
64+
}

0 commit comments

Comments
 (0)