Skip to content

Commit 764b534

Browse files
committed
Adds UI for deploying to remote device
1 parent 53e156a commit 764b534

12 files changed

Lines changed: 533 additions & 7 deletions

File tree

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ public class Main {
2626
@Inject
2727
private Logger logger;
2828

29-
@SuppressWarnings("PMD.SignatureDeclareThrowsException")
30-
public static void main(String[] args) throws Exception {
29+
public static void main(String[] args) throws IOException, InterruptedException {
3130
final Injector injector = Guice.createInjector(new GRIPCoreModule());
3231
injector.getInstance(Main.class).start(args);
3332
}
3433

34+
@SuppressWarnings("PMD.SystemPrintln")
3535
public void start(String[] args) throws IOException, InterruptedException {
3636
if (args.length != 1) {
3737
System.err.println("Usage: GRIP.jar project.grip");
@@ -69,6 +69,9 @@ public void start(String[] args) throws IOException, InterruptedException {
6969
// Open a project from a .grip file specified on the command line
7070
project.open(new File(projectPath));
7171

72+
73+
// This is done in order to indicate to the user using the deployment UI that this is running
74+
System.out.println("SUCCESS! The project is running in headless mode!");
7275
// There's nothing more to do in the main thread since we're in headless mode - sleep forever
7376
for (; ; ) {
7477
Thread.sleep(Integer.MAX_VALUE);
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package edu.wpi.grip.ui;
2+
3+
4+
import com.google.common.base.Throwables;
5+
import com.google.common.eventbus.EventBus;
6+
import com.google.inject.Inject;
7+
import com.google.inject.Singleton;
8+
import edu.wpi.grip.ui.annotations.ParametrizedController;
9+
import edu.wpi.grip.ui.components.StartStoppableButton;
10+
import edu.wpi.grip.ui.deployment.DeploymentOptionsController;
11+
import edu.wpi.grip.ui.deployment.DeploymentOptionsControllersFactory;
12+
import edu.wpi.grip.ui.util.deployment.DeployedInstanceManager;
13+
import javafx.application.Platform;
14+
import javafx.fxml.FXML;
15+
import javafx.scene.control.Accordion;
16+
import javafx.scene.control.DialogPane;
17+
import javafx.scene.control.ProgressBar;
18+
import javafx.scene.control.TextArea;
19+
import javafx.scene.layout.HBox;
20+
21+
import java.io.IOException;
22+
import java.io.OutputStream;
23+
import java.io.PrintStream;
24+
import java.util.function.Supplier;
25+
import java.util.stream.Collectors;
26+
27+
@Singleton
28+
@ParametrizedController(url = "DeployerPane.fxml")
29+
public class DeployerController {
30+
31+
@FXML
32+
private DialogPane root;
33+
34+
@FXML
35+
private HBox controlsBox;
36+
37+
@FXML
38+
private Accordion deploymentMethods;
39+
40+
@FXML
41+
private TextArea stdOutStreamTextArea;
42+
43+
@FXML
44+
private TextArea stdErrStreamTextArea;
45+
46+
@FXML
47+
private ProgressBar progressIndicator;
48+
49+
private final EventBus eventBus;
50+
private final StartStoppableButton.Factory startStopButtonFactory;
51+
private final DeploymentOptionsControllersFactory optionsControllersFactory;
52+
53+
private class StreamToTextArea extends OutputStream {
54+
private final TextArea outputArea;
55+
56+
public StreamToTextArea(TextArea outputArea) {
57+
this.outputArea = outputArea;
58+
}
59+
60+
public StreamToTextArea reset() {
61+
outputArea.clear();
62+
return this;
63+
}
64+
65+
@Override
66+
public void write(int i) throws IOException {
67+
outputArea.appendText(String.valueOf((char) i));
68+
}
69+
}
70+
71+
72+
public interface Factory {
73+
DeployerController create();
74+
}
75+
76+
@Inject
77+
DeployerController(EventBus eventBus, StartStoppableButton.Factory startStopButtonFactory, DeploymentOptionsControllersFactory optionsControllersFactory) {
78+
this.eventBus = eventBus;
79+
this.startStopButtonFactory = startStopButtonFactory;
80+
this.optionsControllersFactory = optionsControllersFactory;
81+
}
82+
83+
@FXML
84+
private void initialize() {
85+
final Supplier<OutputStream> out = () ->
86+
new PrintStream(new StreamToTextArea(stdOutStreamTextArea).reset(), false);
87+
final Supplier<OutputStream> err = () ->
88+
new PrintStream(new StreamToTextArea(stdErrStreamTextArea).reset(), false);
89+
deploymentMethods.getPanes().addAll(
90+
optionsControllersFactory
91+
.createControllers(this::onDeploy, out, err)
92+
.stream()
93+
.map(DeploymentOptionsController::getRoot)
94+
.collect(Collectors.toList()));
95+
}
96+
97+
private void onDeploy(DeployedInstanceManager manager) {
98+
Platform.runLater(() -> {
99+
progressIndicator.setProgress(0);
100+
deploymentMethods.setDisable(true);
101+
});
102+
manager.deploy()
103+
.fail(throwable -> {
104+
Platform.runLater(() -> {
105+
stdErrStreamTextArea.setText("Failed to deploy\n" +
106+
Throwables.getStackTraceAsString(throwable)
107+
);
108+
deploymentMethods.setDisable(false);
109+
});
110+
})
111+
.progress(percent -> {
112+
Platform.runLater(() -> progressIndicator.setProgress(percent));
113+
})
114+
.done(deployedManager -> {
115+
Platform.runLater(() -> {
116+
controlsBox.getChildren().add(startStopButtonFactory.create(deployedManager));
117+
deploymentMethods.setDisable(true);
118+
progressIndicator.setProgress(-1);
119+
});
120+
});
121+
122+
}
123+
124+
public DialogPane getRoot() {
125+
return root;
126+
}
127+
}
128+
129+

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import javafx.application.Platform;
88
import javafx.fxml.FXML;
99
import javafx.scene.Parent;
10+
import javafx.scene.control.Alert;
1011
import javafx.scene.control.ButtonType;
1112
import javafx.scene.control.Dialog;
1213
import javafx.scene.control.SplitPane;
@@ -39,6 +40,8 @@ public class MainWindowController {
3940
private Palette palette;
4041
@Inject
4142
private Project project;
43+
@Inject
44+
private DeployerController.Factory deployerControllerFactoy;
4245

4346
public void initialize() {
4447
pipelineView.prefHeightProperty().bind(bottomPane.heightProperty());
@@ -164,5 +167,21 @@ public void quit() {
164167
Platform.exit();
165168
}
166169
}
170+
171+
@FXML
172+
public void deployFRC() {
173+
if (project.getFile().isPresent()) {
174+
final DeployerController deployerController = deployerControllerFactoy.create();
175+
final Dialog<ButtonType> dialog = new Dialog();
176+
dialog.setDialogPane(deployerController.getRoot());
177+
dialog.setResizable(true);
178+
dialog.showAndWait();
179+
} else {
180+
final Alert alert = new Alert(Alert.AlertType.INFORMATION,
181+
"You must have saved your project before it can be deployed to a remote device.");
182+
alert.showAndWait();
183+
}
184+
185+
}
167186
}
168187

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package edu.wpi.grip.ui.deployment;
2+
3+
import edu.wpi.grip.ui.util.SupplierWithIO;
4+
import edu.wpi.grip.ui.util.deployment.DeployedInstanceManager;
5+
import javafx.application.Platform;
6+
import javafx.fxml.FXML;
7+
import javafx.fxml.FXMLLoader;
8+
import javafx.scene.control.Button;
9+
import javafx.scene.control.Label;
10+
import javafx.scene.control.ProgressIndicator;
11+
import javafx.scene.control.TitledPane;
12+
import javafx.scene.layout.GridPane;
13+
import org.jdeferred.DeferredCallable;
14+
import org.jdeferred.DeferredManager;
15+
import org.jdeferred.Promise;
16+
import org.jdeferred.impl.DefaultDeferredManager;
17+
18+
import java.io.IOException;
19+
import java.net.InetAddress;
20+
import java.util.function.Consumer;
21+
22+
public abstract class DeploymentOptionsController {
23+
24+
@FXML
25+
private TitledPane root;
26+
27+
@FXML
28+
private GridPane optionsGrid;
29+
30+
@FXML
31+
private Label deployErrorText;
32+
33+
@FXML
34+
private ProgressIndicator deploySpinner;
35+
36+
@FXML
37+
private Button deployButton;
38+
39+
private final String title;
40+
private final Consumer<DeployedInstanceManager> onDeployCallback;
41+
42+
protected DeploymentOptionsController(String title, Consumer<DeployedInstanceManager> onDeployCallback) {
43+
this.title = title;
44+
this.onDeployCallback = onDeployCallback;
45+
try {
46+
FXMLLoader.load(DeploymentOptionsController.class.getResource("DeploymentOptions.fxml"), null, null, c -> this);
47+
} catch (IOException e) {
48+
throw new IllegalStateException("Could not load FXML", e);
49+
}
50+
}
51+
52+
@FXML
53+
protected final void initialize() {
54+
root.setText(title);
55+
postInit();
56+
}
57+
58+
/**
59+
* Called after the initialize method
60+
*/
61+
protected abstract void postInit();
62+
63+
protected abstract Promise<DeployedInstanceManager, String, String> onDeploy();
64+
65+
@FXML
66+
private void deploy() {
67+
deploySpinner.setVisible(true);
68+
deployButton.setDisable(true);
69+
onDeploy()
70+
.progress(this::setErrorText)
71+
.fail((text) -> {
72+
setErrorText(text);
73+
Platform.runLater(() -> {
74+
deploySpinner.setVisible(false);
75+
deployButton.setDisable(false);
76+
});
77+
})
78+
.done((t) -> {
79+
onDeployCallback.accept(t);
80+
Platform.runLater(() -> {
81+
deploySpinner.setVisible(false);
82+
deployButton.setDisable(false);
83+
});
84+
});
85+
}
86+
87+
private void setErrorText(String text) {
88+
Platform.runLater(() -> {
89+
deployErrorText.setText(text);
90+
root.requestLayout();
91+
});
92+
}
93+
94+
protected GridPane getOptionsGrid() {
95+
return optionsGrid;
96+
}
97+
98+
protected Button getDeployButton() {
99+
return deployButton;
100+
}
101+
102+
public TitledPane getRoot() {
103+
return root;
104+
}
105+
106+
protected static Promise<InetAddress, Throwable, String> checkInetAddressReachable(SupplierWithIO<InetAddress> address){
107+
final DeferredManager checkAddressDeferred = new DefaultDeferredManager();
108+
return checkAddressDeferred.when(new DeferredCallable<InetAddress, String>() {
109+
@Override
110+
public InetAddress call() throws Exception {
111+
final InetAddress inetAddress = address.getWithIO();
112+
final int attemptCount = 5;
113+
for (int i = 0; i < attemptCount; i++) {
114+
if(inetAddress.isReachable(1000)) {
115+
return inetAddress;
116+
} else {
117+
notify("Attempt " + i + "/" + attemptCount + " failed");
118+
}
119+
}
120+
throw new IOException("Failed to connect");
121+
}
122+
});
123+
124+
}
125+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package edu.wpi.grip.ui.deployment;
2+
3+
4+
import edu.wpi.grip.ui.util.deployment.DeployedInstanceManager;
5+
6+
import java.io.OutputStream;
7+
import java.util.Arrays;
8+
import java.util.Collection;
9+
import java.util.function.Consumer;
10+
import java.util.function.Supplier;
11+
12+
public class DeploymentOptionsControllersFactory {
13+
14+
private final DeployedInstanceManager.Factory deployedInstanceManagerFactory;
15+
16+
public DeploymentOptionsControllersFactory(DeployedInstanceManager.Factory deployedInstanceManagerFactory) {
17+
this.deployedInstanceManagerFactory = deployedInstanceManagerFactory;
18+
}
19+
20+
21+
public Collection<DeploymentOptionsController> createControllers(Consumer<DeployedInstanceManager> onDeployCallback, Supplier<OutputStream> stdOut, Supplier<OutputStream> stdErr) {
22+
return Arrays.asList(
23+
new FRCDeploymentOptionsController(deployedInstanceManagerFactory, onDeployCallback, stdOut, stdErr),
24+
new FRCAdvancedDeployment(deployedInstanceManagerFactory, onDeployCallback, stdOut, stdErr)
25+
);
26+
}
27+
}

0 commit comments

Comments
 (0)