Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ project(":ui") {
compile project(path: ':core', configuration: 'shadow')
ideProvider project(path: ':core', configuration: 'compile')
compile group: 'org.controlsfx', name: 'controlsfx', version: '8.40.10'
compile group: 'org.apache.ant', name: 'ant-jsch', version: '1.8.1'
compile group: 'com.jcabi', name: 'jcabi-ssh', version: '1.5'
compile group: 'org.jdeferred', name: 'jdeferred-core', version: '1.2.4'
testCompile files(project(':core').sourceSets.test.output.classesDir)
testCompile files(project(':core').sourceSets.test.output.resourcesDir)
testCompile group: 'org.testfx', name: 'testfx-core', version: '4.0.+'
Expand Down
7 changes: 5 additions & 2 deletions core/src/main/java/edu/wpi/grip/core/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ public class Main {
@Inject
private Logger logger;

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

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


// This is done in order to indicate to the user using the deployment UI that this is running
System.out.println("SUCCESS! The project is running in headless mode!");
// There's nothing more to do in the main thread since we're in headless mode - sleep forever
for (; ; ) {
Thread.sleep(Integer.MAX_VALUE);
Expand Down
130 changes: 130 additions & 0 deletions ui/src/main/java/edu/wpi/grip/ui/DeployerController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package edu.wpi.grip.ui;


import com.google.common.base.Throwables;
import com.google.inject.Inject;
import edu.wpi.grip.ui.annotations.ParametrizedController;
import edu.wpi.grip.ui.components.StartStoppableButton;
import edu.wpi.grip.ui.deployment.DeploymentOptionsController;
import edu.wpi.grip.ui.deployment.DeploymentOptionsControllersFactory;
import edu.wpi.grip.ui.util.deployment.DeployedInstanceManager;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.Accordion;
import javafx.scene.control.DialogPane;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.function.Supplier;
import java.util.stream.Collectors;

@ParametrizedController(url = "DeployerPane.fxml")
public class DeployerController {

@FXML
private DialogPane root;

@FXML
private HBox controlsBox;

@FXML
private Accordion deploymentMethods;

@FXML
private TextArea stdOutStreamTextArea;

@FXML
private TextArea stdErrStreamTextArea;

@FXML
private ProgressBar progressIndicator;

private final StartStoppableButton.Factory startStopButtonFactory;
private final DeploymentOptionsControllersFactory optionsControllersFactory;

private class StreamToTextArea extends OutputStream {
private final TextArea outputArea;

public StreamToTextArea(TextArea outputArea) {
super();
this.outputArea = outputArea;
}

public StreamToTextArea reset() {
outputArea.clear();
return this;
}

@Override
public void write(int i) throws IOException {
outputArea.appendText(String.valueOf((char) i));
}
}


public interface Factory {
DeployerController create();
}

@Inject
DeployerController(StartStoppableButton.Factory startStopButtonFactory, DeploymentOptionsControllersFactory optionsControllersFactory) {
this.startStopButtonFactory = startStopButtonFactory;
this.optionsControllersFactory = optionsControllersFactory;
}

@FXML
@SuppressWarnings("PMD.UnusedPrivateMethod")
private void initialize() {
final Supplier<OutputStream> out = () ->
new PrintStream(new StreamToTextArea(stdOutStreamTextArea).reset(), false);
final Supplier<OutputStream> err = () ->
new PrintStream(new StreamToTextArea(stdErrStreamTextArea).reset(), false);
deploymentMethods.getPanes().addAll(
optionsControllersFactory
.createControllers(this::onDeploy, out, err)
.stream()
.map(DeploymentOptionsController::getRoot)
.collect(Collectors.toList()));
}

/**
* Calls {@link DeployedInstanceManager#deploy()} and displays the result to the UI.
* @param manager The manager to call deploy on
*/
private void onDeploy(DeployedInstanceManager manager) {
Platform.runLater(() -> {
progressIndicator.setProgress(0);
deploymentMethods.setDisable(true);
});
manager.deploy()
.fail(throwable -> {
Platform.runLater(() -> {
stdErrStreamTextArea.setText("Failed to deploy\n" +
Throwables.getStackTraceAsString(throwable)
);
deploymentMethods.setDisable(false);
});
})
.progress(percent -> {
Platform.runLater(() -> progressIndicator.setProgress(percent));
})
.done(deployedManager -> {
Platform.runLater(() -> {
controlsBox.getChildren().add(startStopButtonFactory.create(deployedManager));
deploymentMethods.setDisable(true);
progressIndicator.setProgress(-1);
});
});

}

public DialogPane getRoot() {
return root;
}
}


5 changes: 5 additions & 0 deletions ui/src/main/java/edu/wpi/grip/ui/GRIPUIModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import edu.wpi.grip.ui.annotations.ParametrizedController;
import edu.wpi.grip.ui.components.ExceptionWitnessResponderButton;
import edu.wpi.grip.ui.components.StartStoppableButton;
import edu.wpi.grip.ui.deployment.FRCAdvancedDeploymentOptionsController;
import edu.wpi.grip.ui.deployment.FRCDeploymentOptionsController;
import edu.wpi.grip.ui.pipeline.OutputSocketController;
import edu.wpi.grip.ui.pipeline.SocketHandleView;
import edu.wpi.grip.ui.pipeline.StepController;
Expand Down Expand Up @@ -70,6 +72,9 @@ public <I> void hear(final TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEnco
install(new FactoryModuleBuilder().build(OperationController.Factory.class));
install(new FactoryModuleBuilder().build(SocketHandleView.Factory.class));
install(new FactoryModuleBuilder().build(OutputSocketController.Factory.class));
install(new FactoryModuleBuilder().build(DeployerController.Factory.class));
install(new FactoryModuleBuilder().build(FRCDeploymentOptionsController.Factory.class));
install(new FactoryModuleBuilder().build(FRCAdvancedDeploymentOptionsController.Factory.class));
// End arbitrary controllers

// InputSocketController Factories
Expand Down
19 changes: 19 additions & 0 deletions ui/src/main/java/edu/wpi/grip/ui/MainWindowController.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.Parent;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.SplitPane;
Expand Down Expand Up @@ -39,6 +40,8 @@ public class MainWindowController {
private Palette palette;
@Inject
private Project project;
@Inject
private DeployerController.Factory deployerControllerFactoy;

public void initialize() {
pipelineView.prefHeightProperty().bind(bottomPane.heightProperty());
Expand Down Expand Up @@ -164,5 +167,21 @@ public void quit() {
Platform.exit();
}
}

@FXML
public void deployFRC() {
if (project.getFile().isPresent()) {
final DeployerController deployerController = deployerControllerFactoy.create();
final Dialog<ButtonType> dialog = new Dialog();
dialog.setDialogPane(deployerController.getRoot());
dialog.setResizable(true);
dialog.showAndWait();
} else {
final Alert alert = new Alert(Alert.AlertType.INFORMATION,
"You must have saved your project before it can be deployed to a remote device.");
alert.showAndWait();
}

}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package edu.wpi.grip.ui.deployment;

import edu.wpi.grip.ui.annotations.ParametrizedController;
import edu.wpi.grip.ui.util.SupplierWithIO;
import edu.wpi.grip.ui.util.deployment.DeployedInstanceManager;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.GridPane;
import org.jdeferred.DeferredCallable;
import org.jdeferred.DeferredManager;
import org.jdeferred.Promise;
import org.jdeferred.impl.DefaultDeferredManager;

import java.io.IOException;
import java.net.InetAddress;
import java.util.function.Consumer;

/**
* A simple UI component that can be used to display options for deploying to a remote device using
* asynchronous callbacks.
*/
@ParametrizedController(url = "DeploymentOptions.fxml")
public abstract class DeploymentOptionsController {

@FXML
private TitledPane root;

@FXML
private GridPane optionsGrid;

@FXML
private Label deployErrorText;

@FXML
private ProgressIndicator deploySpinner;

@FXML
private Button deployButton;

private final String title;
private final Consumer<DeployedInstanceManager> onDeployCallback;

DeploymentOptionsController(String title, Consumer<DeployedInstanceManager> onDeployCallback) {
this.title = title;
this.onDeployCallback = onDeployCallback;
}

@FXML
protected final void initialize() {
root.setText(title);
postInit();
}

/**
* Called after the initialize method
*/
protected abstract void postInit();

protected abstract Promise<DeployedInstanceManager, String, String> onDeploy();

@FXML
@SuppressWarnings("PMD.UnusedPrivateMethod")
private void deploy() {
deploySpinner.setVisible(true);
deployButton.setDisable(true);
onDeploy()
.progress(this::setErrorText)
.fail((text) -> {
setErrorText(text);
Platform.runLater(() -> {
deploySpinner.setVisible(false);
deployButton.setDisable(false);
});
})
.done((t) -> {
onDeployCallback.accept(t);
Platform.runLater(() -> {
deploySpinner.setVisible(false);
deployButton.setDisable(false);
});
});
}

private void setErrorText(String text) {
Platform.runLater(() -> {
deployErrorText.setText(text);
root.requestLayout();
});
}

protected GridPane getOptionsGrid() {
return optionsGrid;
}

protected Button getDeployButton() {
return deployButton;
}

public TitledPane getRoot() {
return root;
}

/**
* Checks that an InetAddress is reachable asynchronously
*
* @param address The address supplier to check
* @return A promise that is resolved when the InetAddress is determined to be reachable.
*/
protected static Promise<InetAddress, Throwable, String> checkInetAddressReachable(SupplierWithIO<InetAddress> address) {
final DeferredManager checkAddressDeferred = new DefaultDeferredManager();
return checkAddressDeferred.when(new DeferredCallable<InetAddress, String>() {
@Override
public InetAddress call() throws IOException {
final InetAddress inetAddress = address.getWithIO();
final int attemptCount = 5;
for (int i = 0; i < attemptCount; i++) {
if (inetAddress.isReachable(1000)) {
return inetAddress;
} else {
notify("Attempt " + i + "/" + attemptCount + " failed");
}
}
throw new IOException("Failed to connect");
}
});

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package edu.wpi.grip.ui.deployment;


import com.google.inject.Inject;
import com.google.inject.Singleton;
import edu.wpi.grip.ui.util.deployment.DeployedInstanceManager;

import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.function.Consumer;
import java.util.function.Supplier;

@Singleton
public class DeploymentOptionsControllersFactory {

private final FRCDeploymentOptionsController.Factory frcDeploymentOptionsControllerFactory;
private final FRCAdvancedDeploymentOptionsController.Factory frcAdvancedDeploymentOptionsControllerFactory;

@Inject
DeploymentOptionsControllersFactory(
FRCDeploymentOptionsController.Factory frcDeploymentOptionsControllerFactory,
FRCAdvancedDeploymentOptionsController.Factory frcAdvancedDeploymentOptionsControllerFactory) {
this.frcDeploymentOptionsControllerFactory = frcDeploymentOptionsControllerFactory;
this.frcAdvancedDeploymentOptionsControllerFactory = frcAdvancedDeploymentOptionsControllerFactory;
}


public Collection<DeploymentOptionsController> createControllers(Consumer<DeployedInstanceManager> onDeployCallback, Supplier<OutputStream> stdOut, Supplier<OutputStream> stdErr) {
return Arrays.asList(
frcDeploymentOptionsControllerFactory.create(onDeployCallback, stdOut, stdErr),
frcAdvancedDeploymentOptionsControllerFactory.create(onDeployCallback, stdOut, stdErr)
);
}
}
Loading