Skip to content

Commit 11db640

Browse files
committed
Start and stop GRIP using NetworkTables
This lets robot programs start and stop GRIP to save CPU without having to wait for the process to start up again. For example, NetworkTable table = NetworkTable.getTable("GRIP"); // Stop GRIP without killing the process table.putBoolean("run", false); doSomeStuffThatDoesntNeedVision(); // Start GRIP again table.putBoolean("run", true);
1 parent c7cbbb7 commit 11db640

File tree

8 files changed

+66
-18
lines changed

8 files changed

+66
-18
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
import com.google.inject.spi.TypeEncounter;
1111
import com.google.inject.spi.TypeListener;
1212
import edu.wpi.grip.core.events.UnexpectedThrowableEvent;
13+
import edu.wpi.grip.core.operations.networktables.NTManager;
1314
import edu.wpi.grip.core.serialization.Project;
1415
import edu.wpi.grip.core.sources.CameraSource;
1516
import edu.wpi.grip.core.sources.ImageFileSource;
1617
import edu.wpi.grip.core.sources.MultiImageFileSource;
1718
import edu.wpi.grip.core.util.ExceptionWitness;
19+
import edu.wpi.grip.core.util.GRIPMode;
1820

1921
import javax.annotation.Nullable;
2022
import java.io.IOException;
@@ -87,6 +89,8 @@ public GRIPCoreModule() {
8789

8890
@Override
8991
protected void configure() {
92+
bind(GRIPMode.class).toInstance(GRIPMode.HEADLESS);
93+
9094
// Register any injected object on the event bus
9195
bindListener(Matchers.any(), new TypeListener() {
9296
@Override
@@ -97,6 +101,8 @@ public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
97101

98102
bind(EventBus.class).toInstance(eventBus);
99103

104+
bind(NTManager.class).asEagerSingleton();
105+
100106
install(new FactoryModuleBuilder().build(new TypeLiteral<Connection.Factory<Object>>() {
101107
}));
102108

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import com.thoughtworks.xstream.annotations.XStreamAlias;
88
import com.thoughtworks.xstream.annotations.XStreamOmitField;
99
import edu.wpi.grip.core.events.*;
10-
import edu.wpi.grip.core.operations.networktables.NTManager;
1110
import edu.wpi.grip.core.settings.ProjectSettings;
1211

1312
import javax.inject.Inject;
@@ -37,10 +36,6 @@ public class Pipeline {
3736
@XStreamOmitField
3837
private EventBus eventBus;
3938

40-
@Inject
41-
@XStreamOmitField
42-
private NTManager ntManager;
43-
4439
/*
4540
* We have separate locks for sources and steps because we don't want to
4641
* block access to both resources when only one is in use.

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

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
import edu.wpi.first.wpilibj.networktables.NetworkTable;
66
import edu.wpi.first.wpilibj.networktables.NetworkTablesJNI;
77
import edu.wpi.grip.core.Pipeline;
8+
import edu.wpi.grip.core.PipelineRunner;
89
import edu.wpi.grip.core.events.ProjectSettingsChangedEvent;
910
import edu.wpi.grip.core.events.StepRemovedEvent;
1011
import edu.wpi.grip.core.settings.ProjectSettings;
12+
import edu.wpi.grip.core.util.GRIPMode;
1113

1214
import javax.inject.Inject;
1315
import java.io.File;
@@ -41,13 +43,16 @@ public class NTManager {
4143
put(6, Level.FINEST);
4244
}};
4345

44-
@Inject Pipeline pipeline;
46+
@Inject private Pipeline pipeline;
47+
@Inject private PipelineRunner pipelineRunner;
48+
@Inject private GRIPMode gripMode;
4549

4650
@Inject
4751
public NTManager(Logger logger) {
4852
checkNotNull(logger, "Logger cannot be null");
4953
// We may have another instance of this method lying around
5054
NetworkTable.shutdown();
55+
5156
// Redirect NetworkTables log messages to our own log files. This gets rid of console spam, and it also lets
5257
// us grep through NetworkTables messages just like any other messages.
5358
NetworkTablesJNI.setLogger((level, file, line, msg) -> {
@@ -56,6 +61,30 @@ public NTManager(Logger logger) {
5661
}, 0);
5762

5863
NetworkTable.setClientMode();
64+
65+
// When in headless mode, start and stop the pipeline based on the "GRIP/run" key. This allows robot programs
66+
// to control GRIP without actually restarting the process.
67+
NetworkTable.getTable("GRIP").addTableListener("run", (source, key, value, isNew) -> {
68+
if (gripMode == GRIPMode.HEADLESS) {
69+
if (!(value instanceof Boolean)) {
70+
logger.warning("NetworkTables value GRIP/run should be a boolean!");
71+
return;
72+
}
73+
74+
if ((Boolean) value) {
75+
if (!pipelineRunner.isRunning()) {
76+
logger.info("Starting GRIP from NetworkTables");
77+
pipelineRunner.startAsync();
78+
}
79+
} else if (pipelineRunner.isRunning()) {
80+
logger.info("Stopping GRIP from NetworkTables");
81+
pipelineRunner.stopAsync();
82+
83+
}
84+
}
85+
}, true);
86+
87+
NetworkTable.shutdown();
5988
}
6089

6190
/**
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package edu.wpi.grip.core.util;
2+
3+
/**
4+
* An enum that indicates if GRIP is running in GUI mode with JavaFX or as a headless command line application.
5+
* <p>
6+
* To the get the mode, this can be injected into a class (ie: @Inject private GRIPMode mode;)
7+
*/
8+
public enum GRIPMode {
9+
GUI, HEADLESS
10+
}

ui/src/main/java/edu/wpi/grip/ui/GRIPUIModule.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.google.inject.spi.TypeEncounter;
99
import com.google.inject.spi.TypeListener;
1010
import edu.wpi.grip.core.Source;
11+
import edu.wpi.grip.core.util.GRIPMode;
1112
import edu.wpi.grip.ui.annotations.ParametrizedController;
1213
import edu.wpi.grip.ui.components.ExceptionWitnessResponderButton;
1314
import edu.wpi.grip.ui.components.StartStoppableButton;
@@ -28,6 +29,7 @@
2829
public class GRIPUIModule extends AbstractModule {
2930
@Override
3031
protected void configure() {
32+
bind(GRIPMode.class).toInstance(GRIPMode.GUI);
3133

3234
bindListener(Matchers.any(), new TypeListener() {
3335
@Override

ui/src/main/java/edu/wpi/grip/ui/Main.java

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.google.common.eventbus.Subscribe;
66
import com.google.inject.Guice;
77
import com.google.inject.Injector;
8+
import com.google.inject.util.Modules;
89
import com.sun.javafx.application.PlatformImpl;
910
import edu.wpi.grip.core.GRIPCoreModule;
1011
import edu.wpi.grip.core.PipelineRunner;
@@ -37,8 +38,12 @@ public class Main extends Application {
3738
@Inject private Project project;
3839
@Inject private Logger logger;
3940

41+
/**
42+
* JavaFX insists on creating the main application with its own reflection code, so we can't create with the
43+
* Guice and do automatic field injection. However, we can inject it after the fact.
44+
*/
4045
@VisibleForTesting
41-
protected final Injector injector = Guice.createInjector(new GRIPCoreModule(), new GRIPUIModule());
46+
protected Injector injector;
4247

4348
private final Object dialogLock = new Object();
4449
private Parent root;
@@ -47,23 +52,22 @@ public static void main(String[] args) {
4752
launch(args);
4853
}
4954

50-
/**
51-
* JavaFX insists on creating the main application with its own reflection code, so we can't create with the
52-
* Guice and do automatic field injection. However, we can inject it after the fact.
53-
*/
54-
public Main() {
55-
injector.injectMembers(this);
56-
}
57-
5855
@Override
5956
@SuppressWarnings("PMD.SignatureDeclareThrowsException")
6057
public void start(Stage stage) throws Exception {
61-
// If --headless was specified on the command line, run in headless mode
6258
List<String> parameters = new ArrayList<>(getParameters().getRaw());
6359

6460
if (parameters.contains("--headless")) {
61+
// If --headless was specified on the command line, run in headless mode (only use the core module)
62+
injector = Guice.createInjector(new GRIPCoreModule());
63+
injector.injectMembers(this);
64+
6565
parameters.remove("--headless");
6666
} else {
67+
// Otherwise, run with both the core and UI modules, and show the JavaFX stage
68+
injector = Guice.createInjector(Modules.override(new GRIPCoreModule()).with(new GRIPUIModule()));
69+
injector.injectMembers(this);
70+
6771
root = FXMLLoader.load(Main.class.getResource("MainWindow.fxml"), null, null, injector::getInstance);
6872
root.setStyle("-fx-font-size: " + DPIUtility.FONT_SIZE + "px");
6973

ui/src/test/java/edu/wpi/grip/ui/pipeline/PipelineUITest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.google.common.eventbus.EventBus;
55
import com.google.inject.Guice;
66
import com.google.inject.Injector;
7+
import com.google.inject.util.Modules;
78
import edu.wpi.grip.core.*;
89
import edu.wpi.grip.core.util.MockExceptionWitness;
910
import edu.wpi.grip.ui.GRIPUIModule;
@@ -44,7 +45,7 @@ public class PipelineUITest extends ApplicationTest {
4445
public void start(Stage stage) {
4546
testModule = new GRIPCoreTestModule();
4647
testModule.setUp();
47-
final Injector injector = Guice.createInjector(testModule, new GRIPUIModule());
48+
final Injector injector = Guice.createInjector(Modules.override(testModule).with(new GRIPUIModule()));
4849
eventBus = injector.getInstance(EventBus.class);
4950
pipeline = injector.getInstance(Pipeline.class);
5051
additionOperation = new AdditionOperation();

ui/src/test/java/edu/wpi/grip/ui/pipeline/input/InputSocketControllerFactoryTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.google.common.eventbus.EventBus;
44
import com.google.inject.Guice;
55
import com.google.inject.Injector;
6+
import com.google.inject.util.Modules;
67
import edu.wpi.grip.core.InputSocket;
78
import edu.wpi.grip.core.Operation;
89
import edu.wpi.grip.core.Palette;
@@ -77,7 +78,7 @@ public InputSocketControllerFactoryTest(Operation operation, String name) {
7778
public void start(Stage stage) {
7879
testModule = new GRIPCoreTestModule();
7980
testModule.setUp();
80-
Injector injector = Guice.createInjector(testModule, new GRIPUIModule());
81+
Injector injector = Guice.createInjector(Modules.override(testModule).with(new GRIPUIModule()));
8182
inputSocketControllerFactory = injector.getInstance(InputSocketControllerFactory.class);
8283
stepFactory = injector.getInstance(Step.Factory.class);
8384
gridPane = new GridPane();

0 commit comments

Comments
 (0)