Skip to content

Commit 2e503ad

Browse files
committed
Merge pull request #251 from WPIRoboticsProjects/revert-235-revert-204-feat/copyToRemoteDeviceAndLaunch
Revert "Revert "Feat: Remote Deployment and Running""
2 parents a9a9597 + 5d9abd7 commit 2e503ad

File tree

19 files changed

+1060
-7
lines changed

19 files changed

+1060
-7
lines changed

build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ project(":ui") {
191191
compile project(path: ':core', configuration: 'shadow')
192192
ideProvider project(path: ':core', configuration: 'compile')
193193
compile group: 'org.controlsfx', name: 'controlsfx', version: '8.40.10'
194+
compile group: 'org.apache.ant', name: 'ant-jsch', version: '1.8.1'
195+
compile group: 'com.jcabi', name: 'jcabi-ssh', version: '1.5'
196+
compile group: 'org.jdeferred', name: 'jdeferred-core', version: '1.2.4'
194197
testCompile files(project(':core').sourceSets.test.output.classesDir)
195198
testCompile files(project(':core').sourceSets.test.output.resourcesDir)
196199
testCompile group: 'org.testfx', name: 'testfx-core', version: '4.0.+'

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

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import edu.wpi.grip.ui.annotations.ParametrizedController;
1212
import edu.wpi.grip.ui.components.ExceptionWitnessResponderButton;
1313
import edu.wpi.grip.ui.components.StartStoppableButton;
14+
import edu.wpi.grip.ui.deployment.FRCAdvancedDeploymentOptionsController;
15+
import edu.wpi.grip.ui.deployment.FRCDeploymentOptionsController;
1416
import edu.wpi.grip.ui.pipeline.OutputSocketController;
1517
import edu.wpi.grip.ui.pipeline.SocketHandleView;
1618
import edu.wpi.grip.ui.pipeline.StepController;
@@ -70,6 +72,9 @@ public <I> void hear(final TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEnco
7072
install(new FactoryModuleBuilder().build(OperationController.Factory.class));
7173
install(new FactoryModuleBuilder().build(SocketHandleView.Factory.class));
7274
install(new FactoryModuleBuilder().build(OutputSocketController.Factory.class));
75+
install(new FactoryModuleBuilder().build(DeployerController.Factory.class));
76+
install(new FactoryModuleBuilder().build(FRCDeploymentOptionsController.Factory.class));
77+
install(new FactoryModuleBuilder().build(FRCAdvancedDeploymentOptionsController.Factory.class));
7378
// End arbitrary controllers
7479

7580
// InputSocketController Factories

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: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package edu.wpi.grip.ui.deployment;
2+
3+
import edu.wpi.grip.ui.annotations.ParametrizedController;
4+
import edu.wpi.grip.ui.util.SupplierWithIO;
5+
import edu.wpi.grip.ui.util.deployment.DeployedInstanceManager;
6+
import javafx.application.Platform;
7+
import javafx.fxml.FXML;
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+
/**
23+
* A simple UI component that can be used to display options for deploying to a remote device using
24+
* asynchronous callbacks.
25+
*/
26+
@ParametrizedController(url = "DeploymentOptions.fxml")
27+
public abstract class DeploymentOptionsController {
28+
29+
@FXML
30+
private TitledPane root;
31+
32+
@FXML
33+
private GridPane optionsGrid;
34+
35+
@FXML
36+
private Label deployErrorText;
37+
38+
@FXML
39+
private ProgressIndicator deploySpinner;
40+
41+
@FXML
42+
private Button deployButton;
43+
44+
private final String title;
45+
private final Consumer<DeployedInstanceManager> onDeployCallback;
46+
47+
DeploymentOptionsController(String title, Consumer<DeployedInstanceManager> onDeployCallback) {
48+
this.title = title;
49+
this.onDeployCallback = onDeployCallback;
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+
@SuppressWarnings("PMD.UnusedPrivateMethod")
67+
private void deploy() {
68+
deploySpinner.setVisible(true);
69+
deployButton.setDisable(true);
70+
onDeploy()
71+
.progress(this::setErrorText)
72+
.fail((text) -> {
73+
setErrorText(text);
74+
Platform.runLater(() -> {
75+
deploySpinner.setVisible(false);
76+
deployButton.setDisable(false);
77+
});
78+
})
79+
.done((t) -> {
80+
onDeployCallback.accept(t);
81+
Platform.runLater(() -> {
82+
deploySpinner.setVisible(false);
83+
deployButton.setDisable(false);
84+
});
85+
});
86+
}
87+
88+
private void setErrorText(String text) {
89+
Platform.runLater(() -> {
90+
deployErrorText.setText(text);
91+
root.requestLayout();
92+
});
93+
}
94+
95+
protected GridPane getOptionsGrid() {
96+
return optionsGrid;
97+
}
98+
99+
protected Button getDeployButton() {
100+
return deployButton;
101+
}
102+
103+
public TitledPane getRoot() {
104+
return root;
105+
}
106+
107+
/**
108+
* Checks that an InetAddress is reachable asynchronously
109+
*
110+
* @param address The address supplier to check
111+
* @return A promise that is resolved when the InetAddress is determined to be reachable.
112+
*/
113+
protected static Promise<InetAddress, Throwable, String> checkInetAddressReachable(SupplierWithIO<InetAddress> address) {
114+
final DeferredManager checkAddressDeferred = new DefaultDeferredManager();
115+
return checkAddressDeferred.when(new DeferredCallable<InetAddress, String>() {
116+
@Override
117+
public InetAddress call() throws IOException {
118+
final InetAddress inetAddress = address.getWithIO();
119+
final int attemptCount = 5;
120+
for (int i = 0; i < attemptCount; i++) {
121+
if (inetAddress.isReachable(1000)) {
122+
return inetAddress;
123+
} else {
124+
notify("Attempt " + i + "/" + attemptCount + " failed");
125+
}
126+
}
127+
throw new IOException("Failed to connect");
128+
}
129+
});
130+
131+
}
132+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package edu.wpi.grip.ui.deployment;
2+
3+
4+
import com.google.inject.Inject;
5+
import com.google.inject.Singleton;
6+
import edu.wpi.grip.ui.util.deployment.DeployedInstanceManager;
7+
8+
import java.io.OutputStream;
9+
import java.util.Arrays;
10+
import java.util.Collection;
11+
import java.util.function.Consumer;
12+
import java.util.function.Supplier;
13+
14+
@Singleton
15+
public class DeploymentOptionsControllersFactory {
16+
17+
private final FRCDeploymentOptionsController.Factory frcDeploymentOptionsControllerFactory;
18+
private final FRCAdvancedDeploymentOptionsController.Factory frcAdvancedDeploymentOptionsControllerFactory;
19+
20+
@Inject
21+
DeploymentOptionsControllersFactory(
22+
FRCDeploymentOptionsController.Factory frcDeploymentOptionsControllerFactory,
23+
FRCAdvancedDeploymentOptionsController.Factory frcAdvancedDeploymentOptionsControllerFactory) {
24+
this.frcDeploymentOptionsControllerFactory = frcDeploymentOptionsControllerFactory;
25+
this.frcAdvancedDeploymentOptionsControllerFactory = frcAdvancedDeploymentOptionsControllerFactory;
26+
}
27+
28+
29+
public Collection<DeploymentOptionsController> createControllers(Consumer<DeployedInstanceManager> onDeployCallback, Supplier<OutputStream> stdOut, Supplier<OutputStream> stdErr) {
30+
return Arrays.asList(
31+
frcDeploymentOptionsControllerFactory.create(onDeployCallback, stdOut, stdErr),
32+
frcAdvancedDeploymentOptionsControllerFactory.create(onDeployCallback, stdOut, stdErr)
33+
);
34+
}
35+
}

0 commit comments

Comments
 (0)