Skip to content

Commit f3a0f53

Browse files
committed
Add versioning
If a save file cannot be loaded, no changes happen to the pipeline and the user is notified. This will happen for any save file, pre-versioning or otherwise. When saving a loaded project, the original version string will be changed to the version of GRIP that saved it. Pre-versioning save files will also be upgraded to the versioned format and have the current version of GRIP
1 parent 54d98ea commit f3a0f53

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+477
-151
lines changed

build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ project(":core") {
252252
compile group: 'org.bytedeco.javacpp-presets', name: 'videoinput', version: '0.200-1.1', classifier: os
253253
compile group: 'org.python', name: 'jython', version: '2.7.0'
254254
compile group: 'com.thoughtworks.xstream', name: 'xstream', version: '1.4.9'
255+
compile group: 'com.github.zafarkhaja', name: 'java-semver', version: '0.9.0'
255256
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.4'
256257
compile group: 'com.google.guava', name: 'guava', version: '19.0'
257258
compile group: 'com.google.auto.value', name: 'auto-value', version: '1.2'

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

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import edu.wpi.grip.core.util.ExceptionWitness;
1616
import edu.wpi.grip.core.util.GripMode;
1717

18+
import com.github.zafarkhaja.semver.Version;
1819
import com.google.common.eventbus.EventBus;
1920
import com.google.common.eventbus.SubscriberExceptionContext;
2021
import com.google.inject.AbstractModule;
@@ -125,6 +126,9 @@ public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
125126
// Allow for just injecting the settings provider, instead of the whole pipeline
126127
bind(SettingsProvider.class).to(Pipeline.class);
127128

129+
// Bind the current GRIP version.
130+
bind(Version.class).toInstance(VersionManager.CURRENT_VERSION);
131+
128132
install(new FactoryModuleBuilder().build(new TypeLiteral<Connection.Factory<Object>>() {
129133
}));
130134

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

+54-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
package edu.wpi.grip.core;
22

33
import edu.wpi.grip.core.events.OperationAddedEvent;
4+
import edu.wpi.grip.core.sockets.Socket;
45

6+
import com.google.common.collect.ImmutableList;
57
import com.google.common.eventbus.Subscribe;
68

79
import java.util.Collection;
810
import java.util.LinkedHashMap;
11+
import java.util.List;
912
import java.util.Map;
1013
import java.util.Optional;
14+
import java.util.function.Function;
15+
import java.util.stream.Collectors;
1116

1217
import javax.inject.Singleton;
1318

@@ -24,10 +29,54 @@ public class Palette {
2429

2530
@Subscribe
2631
public void onOperationAdded(OperationAddedEvent event) {
27-
final OperationMetaData operation = event.getOperation();
28-
map(operation.getDescription().name(), operation);
29-
for (String alias : operation.getDescription().aliases()) {
30-
map(alias, operation);
32+
final OperationMetaData operationData = event.getOperation();
33+
map(operationData.getDescription().name(), operationData);
34+
for (String alias : operationData.getDescription().aliases()) {
35+
map(alias, operationData);
36+
}
37+
// Validate that every input and output socket has a unique name and UID
38+
Operation operation = operationData.getOperationSupplier().get();
39+
try {
40+
final List<? extends Socket> sockets = new ImmutableList.Builder<Socket>()
41+
.addAll(operation.getInputSockets())
42+
.addAll(operation.getOutputSockets())
43+
.build();
44+
checkDuplicates(
45+
operationData,
46+
"input socket names",
47+
operation.getInputSockets(), s -> s.getSocketHint().getIdentifier()
48+
);
49+
checkDuplicates(
50+
operationData,
51+
"output socket names",
52+
operation.getOutputSockets(), s -> s.getSocketHint().getIdentifier()
53+
);
54+
checkDuplicates(operationData, "socket IDs", sockets, Socket::getUid);
55+
} finally {
56+
operation.cleanUp();
57+
}
58+
}
59+
60+
private static <T, U> void checkDuplicates(OperationMetaData operationMetaData,
61+
String type,
62+
List<T> list,
63+
Function<T, U> extractionFunction) {
64+
List<U> duplicates = list.stream()
65+
.map(extractionFunction)
66+
.collect(Collectors.toList());
67+
list.stream()
68+
.map(extractionFunction)
69+
.distinct()
70+
.forEach(duplicates::remove);
71+
if (!duplicates.isEmpty()) {
72+
throw new IllegalArgumentException(
73+
String.format(
74+
"Duplicate %s found in operation %s: %s",
75+
type,
76+
operationMetaData.getDescription().name(),
77+
duplicates
78+
)
79+
);
3180
}
3281
}
3382

@@ -36,6 +85,7 @@ public void onOperationAdded(OperationAddedEvent event) {
3685
*
3786
* @param key The key the operation should be mapped to
3887
* @param operation The operation to map the key to
88+
*
3989
* @throws IllegalArgumentException if the key is already in the {@link #operations} map.
4090
*/
4191
private void map(String key, OperationMetaData operation) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package edu.wpi.grip.core;
2+
3+
import edu.wpi.grip.core.exception.IncompatibleVersionException;
4+
5+
import com.github.zafarkhaja.semver.Version;
6+
7+
/**
8+
* Manager for GRIP versions.
9+
*/
10+
public final class VersionManager {
11+
12+
/**
13+
* The final release of GRIP without versioned saves.
14+
*/
15+
public static final Version LAST_UNVERSIONED_RELEASE = Version.valueOf("1.5.0");
16+
17+
/**
18+
* The current version of GRIP.
19+
*/
20+
public static final Version CURRENT_VERSION = Version.valueOf("2.0.0-beta");
21+
22+
/**
23+
* Checks compatibility between two versions of GRIP.
24+
*
25+
* @param current the current version of GRIP
26+
* @param check the version to check
27+
*
28+
* @throws IncompatibleVersionException if the versions are incompatible
29+
*/
30+
public static void checkVersionCompatibility(Version current, Version check)
31+
throws IncompatibleVersionException {
32+
if (check.getMajorVersion() > current.getMajorVersion()
33+
|| check.getMinorVersion() > current.getMinorVersion()) {
34+
throw new IncompatibleVersionException("Incompatible future version: " + check);
35+
}
36+
}
37+
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package edu.wpi.grip.core.exception;
2+
3+
/**
4+
* An exception thrown when trying to load a saved project created in an incompatible version
5+
* of GRIP.
6+
*/
7+
public class IncompatibleVersionException extends InvalidSaveException {
8+
9+
public IncompatibleVersionException(String message) {
10+
super(message);
11+
}
12+
13+
public IncompatibleVersionException(String message, Throwable cause) {
14+
super(message, cause);
15+
}
16+
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package edu.wpi.grip.core.exception;
2+
3+
/**
4+
* An exception thrown when trying to load an invalid saved project.
5+
*/
6+
public class InvalidSaveException extends GripException {
7+
8+
public InvalidSaveException(String message) {
9+
super(message);
10+
}
11+
12+
public InvalidSaveException(String message, Throwable cause) {
13+
super(message, cause);
14+
}
15+
16+
}

core/src/main/java/edu/wpi/grip/core/http/HttpPipelineSwitcher.java

+10-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package edu.wpi.grip.core.http;
22

3+
import edu.wpi.grip.core.exception.InvalidSaveException;
34
import edu.wpi.grip.core.serialization.Project;
45
import edu.wpi.grip.core.util.GripMode;
56

@@ -43,9 +44,15 @@ protected void handleIfPassed(String target,
4344
}
4445
switch (mode) {
4546
case HEADLESS:
46-
project.open(new String(IOUtils.toByteArray(request.getInputStream()), "UTF-8"));
47-
response.setStatus(HttpServletResponse.SC_CREATED);
48-
baseRequest.setHandled(true);
47+
try {
48+
project.open(new String(IOUtils.toByteArray(request.getInputStream()), "UTF-8"));
49+
response.setStatus(HttpServletResponse.SC_CREATED);
50+
baseRequest.setHandled(true);
51+
} catch (InvalidSaveException e) {
52+
// 403 - Forbidden if given an invalid save
53+
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
54+
baseRequest.setHandled(true);
55+
}
4956
break;
5057
case GUI:
5158
// Don't run in GUI mode, it doesn't make much sense and can easily deadlock if pipelines

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ public class BlurOperation implements Operation {
4747
@SuppressWarnings("JavadocMethod")
4848
public BlurOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
4949
outputSocketFactory) {
50-
this.inputSocket = inputSocketFactory.create(inputHint);
51-
this.typeSocket = inputSocketFactory.create(typeHint);
52-
this.radiusSocket = inputSocketFactory.create(radiusHint);
50+
this.inputSocket = inputSocketFactory.create(inputHint, "source-image");
51+
this.typeSocket = inputSocketFactory.create(typeHint, "blur-type");
52+
this.radiusSocket = inputSocketFactory.create(radiusHint, "blur-radius");
5353

54-
this.outputSocket = outputSocketFactory.create(outputHint);
54+
this.outputSocket = outputSocketFactory.create(outputHint, "result");
5555
}
5656

5757
@Override

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

+7-5
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,21 @@ public class ConvexHullsOperation implements Operation {
2626
.category(OperationDescription.Category.FEATURE_DETECTION)
2727
.build();
2828

29-
private final SocketHint<ContoursReport> contoursHint = new SocketHint.Builder<>(ContoursReport
30-
.class)
31-
.identifier("Contours").initialValueSupplier(ContoursReport::new).build();
29+
private final SocketHint<ContoursReport> contoursHint =
30+
new SocketHint.Builder<>(ContoursReport.class)
31+
.identifier("Contours")
32+
.initialValueSupplier(ContoursReport::new)
33+
.build();
3234

3335
private final InputSocket<ContoursReport> inputSocket;
3436
private final OutputSocket<ContoursReport> outputSocket;
3537

3638
@SuppressWarnings("JavadocMethod")
3739
public ConvexHullsOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
3840
outputSocketFactory) {
39-
this.inputSocket = inputSocketFactory.create(contoursHint);
41+
this.inputSocket = inputSocketFactory.create(contoursHint, "contours");
4042

41-
this.outputSocket = outputSocketFactory.create(contoursHint);
43+
this.outputSocket = outputSocketFactory.create(contoursHint, "hulls");
4244
}
4345

4446
@Override

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ public class DesaturateOperation implements Operation {
3939
@SuppressWarnings("JavadocMethod")
4040
public DesaturateOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
4141
outputSocketFactory) {
42-
this.inputSocket = inputSocketFactory.create(inputHint);
43-
this.outputSocket = outputSocketFactory.create(outputHint);
42+
this.inputSocket = inputSocketFactory.create(inputHint, "source-image");
43+
this.outputSocket = outputSocketFactory.create(outputHint, "result");
4444
}
4545

4646

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ public class DistanceTransformOperation implements Operation {
4646
@SuppressWarnings("JavadocMethod")
4747
public DistanceTransformOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
4848
outputSocketFactory) {
49-
this.srcSocket = inputSocketFactory.create(srcHint);
50-
this.typeSocket = inputSocketFactory.create(typeHint);
51-
this.maskSizeSocket = inputSocketFactory.create(maskSizeHint);
49+
this.srcSocket = inputSocketFactory.create(srcHint, "source-image");
50+
this.typeSocket = inputSocketFactory.create(typeHint, "transform-type");
51+
this.maskSizeSocket = inputSocketFactory.create(maskSizeHint, "mask-size");
5252

53-
this.outputSocket = outputSocketFactory.create(outputHint);
53+
this.outputSocket = outputSocketFactory.create(outputHint, "result");
5454
}
5555

5656
@Override

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

+14-14
Original file line numberDiff line numberDiff line change
@@ -95,20 +95,20 @@ public class FilterContoursOperation implements Operation {
9595
@SuppressWarnings("JavadocMethod")
9696
public FilterContoursOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
9797
outputSocketFactory) {
98-
this.contoursSocket = inputSocketFactory.create(contoursHint);
99-
this.minAreaSocket = inputSocketFactory.create(minAreaHint);
100-
this.minPerimeterSocket = inputSocketFactory.create(minPerimeterHint);
101-
this.minWidthSocket = inputSocketFactory.create(minWidthHint);
102-
this.maxWidthSocket = inputSocketFactory.create(maxWidthHint);
103-
this.minHeightSocket = inputSocketFactory.create(minHeightHint);
104-
this.maxHeightSocket = inputSocketFactory.create(maxHeightHint);
105-
this.soliditySocket = inputSocketFactory.create(solidityHint);
106-
this.minVertexSocket = inputSocketFactory.create(minVertexHint);
107-
this.maxVertexSocket = inputSocketFactory.create(maxVertexHint);
108-
this.minRatioSocket = inputSocketFactory.create(minRatioHint);
109-
this.maxRatioSocket = inputSocketFactory.create(maxRatioHint);
110-
111-
this.outputSocket = outputSocketFactory.create(contoursHint);
98+
this.contoursSocket = inputSocketFactory.create(contoursHint, "contours");
99+
this.minAreaSocket = inputSocketFactory.create(minAreaHint, "min-area");
100+
this.minPerimeterSocket = inputSocketFactory.create(minPerimeterHint, "min-perimeter");
101+
this.minWidthSocket = inputSocketFactory.create(minWidthHint, "min-width");
102+
this.maxWidthSocket = inputSocketFactory.create(maxWidthHint, "max-width");
103+
this.minHeightSocket = inputSocketFactory.create(minHeightHint, "min-height");
104+
this.maxHeightSocket = inputSocketFactory.create(maxHeightHint, "max-height");
105+
this.soliditySocket = inputSocketFactory.create(solidityHint, "solidity");
106+
this.minVertexSocket = inputSocketFactory.create(minVertexHint, "min-vertices");
107+
this.maxVertexSocket = inputSocketFactory.create(maxVertexHint, "max-vertices");
108+
this.minRatioSocket = inputSocketFactory.create(minRatioHint, "min-ratio");
109+
this.maxRatioSocket = inputSocketFactory.create(maxRatioHint, "max-ratio");
110+
111+
this.outputSocket = outputSocketFactory.create(contoursHint, "filtered-contours");
112112
}
113113

114114
@Override

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@ public class FilterLinesOperation implements Operation {
5050
@SuppressWarnings("JavadocMethod")
5151
public FilterLinesOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
5252
outputSocketFactory) {
53-
this.inputSocket = inputSocketFactory.create(inputHint);
54-
this.minLengthSocket = inputSocketFactory.create(minLengthHint);
55-
this.angleSocket = inputSocketFactory.create(angleHint);
53+
this.inputSocket = inputSocketFactory.create(inputHint, "lines");
54+
this.minLengthSocket = inputSocketFactory.create(minLengthHint, "min-length");
55+
this.angleSocket = inputSocketFactory.create(angleHint, "angle");
5656

57-
this.linesOutputSocket = outputSocketFactory.create(outputHint);
57+
this.linesOutputSocket = outputSocketFactory.create(outputHint, "filtered-lines");
5858
}
5959

6060
@Override

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

+5-5
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ public class FindBlobsOperation implements Operation {
5454
@SuppressWarnings("JavadocMethod")
5555
public FindBlobsOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
5656
outputSocketFactory) {
57-
this.inputSocket = inputSocketFactory.create(inputHint);
58-
this.minAreaSocket = inputSocketFactory.create(minAreaHint);
59-
this.circularitySocket = inputSocketFactory.create(circularityHint);
60-
this.colorSocket = inputSocketFactory.create(colorHint);
57+
this.inputSocket = inputSocketFactory.create(inputHint, "source-image");
58+
this.minAreaSocket = inputSocketFactory.create(minAreaHint, "min-area");
59+
this.circularitySocket = inputSocketFactory.create(circularityHint, "circularity");
60+
this.colorSocket = inputSocketFactory.create(colorHint, "find-dark-blobs");
6161

62-
this.outputSocket = outputSocketFactory.create(blobsHint);
62+
this.outputSocket = outputSocketFactory.create(blobsHint, "found-blobs");
6363
}
6464

6565
@Override

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ public class FindContoursOperation implements Operation {
5252
@SuppressWarnings("JavadocMethod")
5353
public FindContoursOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
5454
outputSocketFactory) {
55-
this.inputSocket = inputSocketFactory.create(inputHint);
56-
this.externalSocket = inputSocketFactory.create(externalHint);
55+
this.inputSocket = inputSocketFactory.create(inputHint, "source-image");
56+
this.externalSocket = inputSocketFactory.create(externalHint, "find-external-only");
5757

58-
this.contoursSocket = outputSocketFactory.create(contoursHint);
58+
this.contoursSocket = outputSocketFactory.create(contoursHint, "found-contours");
5959
}
6060

6161
@Override

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ public class FindLinesOperation implements Operation {
4444

4545
public FindLinesOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
4646
outputSocketFactory) {
47-
this.inputSocket = inputSocketFactory.create(inputHint);
48-
this.linesReportSocket = outputSocketFactory.create(linesHint);
47+
this.inputSocket = inputSocketFactory.create(inputHint, "source-image");
48+
this.linesReportSocket = outputSocketFactory.create(linesHint, "found-lines");
4949
}
5050

5151
@Override

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

+5-5
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ public class HSLThresholdOperation extends ThresholdOperation {
5656
@SuppressWarnings("JavadocMethod")
5757
public HSLThresholdOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
5858
outputSocketFactory) {
59-
this.inputSocket = inputSocketFactory.create(inputHint);
60-
this.hueSocket = inputSocketFactory.create(hueHint);
61-
this.saturationSocket = inputSocketFactory.create(saturationHint);
62-
this.luminanceSocket = inputSocketFactory.create(luminanceHint);
59+
this.inputSocket = inputSocketFactory.create(inputHint, "source-image");
60+
this.hueSocket = inputSocketFactory.create(hueHint, "hue");
61+
this.saturationSocket = inputSocketFactory.create(saturationHint, "saturation");
62+
this.luminanceSocket = inputSocketFactory.create(luminanceHint, "luminance");
6363

64-
this.outputSocket = outputSocketFactory.create(outputHint);
64+
this.outputSocket = outputSocketFactory.create(outputHint, "result");
6565
}
6666

6767
@Override

0 commit comments

Comments
 (0)