From d102d016b758ddb641043e504387ce3149cde6f3 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Tue, 12 Jul 2022 15:33:52 +0200 Subject: [PATCH 001/120] WIP: Merging Ecdar 2.2 simulator into main --- src/main/java/ecdar/Ecdar.java | 21 +- .../controllers/LeftSimPaneController.java | 25 + .../ecdar/controllers/ProcessController.java | 279 ++++++++++ .../controllers/RightSimPaneController.java | 21 + .../ecdar/controllers/SimEdgeController.java | 421 +++++++++++++++ .../controllers/SimEdgePresentation.java | 32 ++ .../controllers/SimLocationController.java | 124 +++++ .../controllers/SimLocationPresentation.java | 490 ++++++++++++++++++ .../controllers/SimulatorController.java | 155 ++++++ .../SimulatorOverviewController.java | 372 +++++++++++++ .../TracePaneElementController.java | 182 +++++++ .../controllers/TransitionController.java | 45 ++ .../TransitionPaneElementController.java | 245 +++++++++ .../LeftSimPanePresentation.java | 37 ++ .../presentations/ProcessPresentation.java | 282 ++++++++++ .../RightSimPanePresentation.java | 45 ++ .../SimulatorOverviewPresentation.java | 20 + .../presentations/SimulatorPresentation.java | 21 + .../TracePaneElementPresentation.java | 96 ++++ .../TransitionPaneElementPresentation.java | 69 +++ .../presentations/TransitionPresentation.java | 98 ++++ .../java/ecdar/simulation/SimulationEdge.java | 8 + .../ecdar/simulation/SimulationHandler.java | 407 +++++++++++++++ .../ecdar/simulation/SimulationLocation.java | 8 + .../ecdar/simulation/SimulationState.java | 42 ++ .../simulation/SimulationStateSuccessor.java | 15 + .../java/ecdar/simulation/Transition.java | 17 + .../LeftSimPanePresentation.fxml | 37 ++ .../RightSimPanePresentation.fxml | 25 + .../SimulatorOverviewPresentation.fxml | 13 + .../presentations/SimulatorPresentation.fxml | 55 ++ .../TracePaneElementPresentation.fxml | 49 ++ .../TransitionPaneElementPresentation.fxml | 66 +++ .../presentations/TransitionPresentation.fxml | 18 + 34 files changed, 3832 insertions(+), 8 deletions(-) create mode 100755 src/main/java/ecdar/controllers/LeftSimPaneController.java create mode 100755 src/main/java/ecdar/controllers/ProcessController.java create mode 100755 src/main/java/ecdar/controllers/RightSimPaneController.java create mode 100755 src/main/java/ecdar/controllers/SimEdgeController.java create mode 100755 src/main/java/ecdar/controllers/SimEdgePresentation.java create mode 100755 src/main/java/ecdar/controllers/SimLocationController.java create mode 100755 src/main/java/ecdar/controllers/SimLocationPresentation.java create mode 100755 src/main/java/ecdar/controllers/SimulatorController.java create mode 100755 src/main/java/ecdar/controllers/SimulatorOverviewController.java create mode 100755 src/main/java/ecdar/controllers/TracePaneElementController.java create mode 100755 src/main/java/ecdar/controllers/TransitionController.java create mode 100755 src/main/java/ecdar/controllers/TransitionPaneElementController.java create mode 100755 src/main/java/ecdar/presentations/LeftSimPanePresentation.java create mode 100755 src/main/java/ecdar/presentations/ProcessPresentation.java create mode 100755 src/main/java/ecdar/presentations/RightSimPanePresentation.java create mode 100755 src/main/java/ecdar/presentations/SimulatorOverviewPresentation.java create mode 100755 src/main/java/ecdar/presentations/SimulatorPresentation.java create mode 100755 src/main/java/ecdar/presentations/TracePaneElementPresentation.java create mode 100755 src/main/java/ecdar/presentations/TransitionPaneElementPresentation.java create mode 100755 src/main/java/ecdar/presentations/TransitionPresentation.java create mode 100644 src/main/java/ecdar/simulation/SimulationEdge.java create mode 100755 src/main/java/ecdar/simulation/SimulationHandler.java create mode 100644 src/main/java/ecdar/simulation/SimulationLocation.java create mode 100644 src/main/java/ecdar/simulation/SimulationState.java create mode 100644 src/main/java/ecdar/simulation/SimulationStateSuccessor.java create mode 100644 src/main/java/ecdar/simulation/Transition.java create mode 100755 src/main/resources/ecdar/presentations/LeftSimPanePresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/SimulatorOverviewPresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/SimulatorPresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/TracePaneElementPresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/TransitionPaneElementPresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/TransitionPresentation.fxml diff --git a/src/main/java/ecdar/Ecdar.java b/src/main/java/ecdar/Ecdar.java index fba5513d..6a6801f0 100644 --- a/src/main/java/ecdar/Ecdar.java +++ b/src/main/java/ecdar/Ecdar.java @@ -4,6 +4,7 @@ import ecdar.abstractions.Project; import ecdar.backend.BackendDriver; import ecdar.backend.BackendHelper; +import ecdar.simulation.SimulationHandler; import ecdar.code_analysis.CodeAnalysis; import ecdar.controllers.EcdarController; import ecdar.presentations.BackgroundThreadPresentation; @@ -54,6 +55,7 @@ public class Ecdar extends Application { public static BooleanProperty shouldRunBackgroundQueries = new SimpleBooleanProperty(true); private static final BooleanProperty isSplit = new SimpleBooleanProperty(true); //Set to true to ensure correct behaviour at first toggle. private static final BackendDriver backendDriver = new BackendDriver(); + private static SimulationHandler simulationHandler; private Stage debugStage; /** @@ -122,6 +124,16 @@ public static Project getProject() { return project; } + /** + * Returns the backend driver used to execute queries and handle simulation + * @return BackendDriver + */ + public static BackendDriver getBackendDriver() { + return backendDriver; + } + + public static SimulationHandler getSimulationHandler() { return simulationHandler; } + public static EcdarPresentation getPresentation() { return presentation; } @@ -183,14 +195,6 @@ public static BooleanProperty toggleCanvasSplit() { return isSplit; } - /** - * Returns the backend driver used to execute queries and handle simulation - * @return BackendDriver - */ - public static BackendDriver getBackendDriver() { - return backendDriver; - } - public static double getDpiScale() { if (!autoScalingEnabled.getValue()) return 1; @@ -206,6 +210,7 @@ private void forceCreateFolder(final String directoryPath) throws IOException { public void start(final Stage stage) { // Load or create new project project = new Project(); + simulationHandler = new SimulationHandler(); // Set the title for the application stage.setTitle("Ecdar " + VERSION); diff --git a/src/main/java/ecdar/controllers/LeftSimPaneController.java b/src/main/java/ecdar/controllers/LeftSimPaneController.java new file mode 100755 index 00000000..d8dc30eb --- /dev/null +++ b/src/main/java/ecdar/controllers/LeftSimPaneController.java @@ -0,0 +1,25 @@ +package ecdar.controllers; + +import ecdar.presentations.TracePaneElementPresentation; +import ecdar.presentations.TransitionPaneElementPresentation; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.ScrollPane; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import java.net.URL; +import java.util.ResourceBundle; + +public class LeftSimPaneController implements Initializable { + public StackPane root; + public ScrollPane scrollPane; + public VBox scrollPaneVbox; + + public TransitionPaneElementPresentation transitionPanePresentation; + public TracePaneElementPresentation tracePanePresentation; + + @Override + public void initialize(URL location, ResourceBundle resources) { + + } +} diff --git a/src/main/java/ecdar/controllers/ProcessController.java b/src/main/java/ecdar/controllers/ProcessController.java new file mode 100755 index 00000000..84f9ff22 --- /dev/null +++ b/src/main/java/ecdar/controllers/ProcessController.java @@ -0,0 +1,279 @@ +package ecdar.controllers; + +import com.jfoenix.controls.JFXRippler; +import ecdar.abstractions.*; +import ecdar.presentations.SimEdgePresentation; +import ecdar.presentations.SimLocationPresentation; +import ecdar.simulation.SimulationEdge; +import ecdar.simulation.SimulationLocation; +import javafx.animation.Interpolator; +import javafx.animation.Transition; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableMap; +import javafx.fxml.Initializable; +import javafx.scene.Node; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.*; +import javafx.scene.shape.Circle; +import javafx.util.Duration; +import org.kordamp.ikonli.javafx.FontIcon; + +import java.math.BigDecimal; +import java.net.URL; +import java.util.*; + +/** + * The controller for the process shown in the {@link SimulatorOverviewController} + */ +public class ProcessController extends ModelController implements Initializable { + public StackPane componentPane; + public Pane modelContainerEdge; + public Pane modelContainerLocation; + public JFXRippler toggleValuesButton; + public VBox valueArea; + public FontIcon toggleValueButtonIcon; + private ObjectProperty component; + + /** + * Keep track of locations/edges and their associated presentation class, by having the as key-value pairs in a Map + * E.g. a {@link Location} key is the model behind the value {@link SimLocationPresentation} view + */ + private final Map locationPresentationMap = new HashMap<>(); + private final Map edgePresentationMap = new HashMap<>(); + private final ObservableMap variables = FXCollections.observableHashMap(); + private final ObservableMap clocks = FXCollections.observableHashMap(); + + @Override + public void initialize(final URL location, final ResourceBundle resources) { + component = new SimpleObjectProperty<>(new Component(true)); + initializeValues(); + } + + private void initializeValues() { + final Circle circle = new Circle(0); + if (getComponent().isDeclarationOpen()) { + circle.setRadius(1000); + } + final ObjectProperty clip = new SimpleObjectProperty<>(circle); + valueArea.clipProperty().bind(clip); + clip.set(circle); + } + + /** + * Highlights the edges and accompanying source/target locations in the process + * @param edges The edges to highlight + */ + public void highlightEdges(final SimulationEdge[] edges) { + for (int i = 0; i < edges.length; i++) { + final SimulationEdge edge = edges[i]; + + // Note that SimulationEdge contains a Location type from the UPPAAL libraries + // but we use a different Location class in our Maps + final Location source = edge.getSource(); + String sourceName = ""; + final Location target = edge.getTarget(); + String targetName = ""; + + // Match the Locations of the SimulationEdge with a SimulationLocation of the process, + // so we can get the source/target names (names are not available on a Location) + for (SimulationLocation sysloc : edge.getProcess().getLocations()) { + if (sysloc.getLocation() == source) { + sourceName = sysloc.getName(); + } else if (sysloc == target) { + targetName = sysloc.getName(); + } + } + + // If target name is empty the edge is a self loop + if (targetName == "") { + targetName = sourceName; + } + + boolean isSourceUniversal = false; + + // Iterate through all locations to check for Universal and Inconsistent locations + // The name of a Universal location may be "U2" in our system, but it is mapped to "Universal" in the engine + // This loop maps "Universal" to for example "U2" + for (Map.Entry locEntry: locationPresentationMap.entrySet()) { + if(locEntry.getKey().getType() == Location.Type.UNIVERSAL) { + if(sourceName.equals("Universal")) { + sourceName = locEntry.getKey().getId(); + isSourceUniversal = true; + } + + if(targetName.equals("Universal")) { + targetName = locEntry.getKey().getId(); + } + } + + if(locEntry.getKey().getType() == Location.Type.INCONSISTENT) { + if(sourceName.equals("Inconsistent")) { + sourceName = locEntry.getKey().getId(); + } + + if(targetName.equals("Inconsistent")) { + targetName = locEntry.getKey().getId(); + } + } + } + + // The edge name may contain ! or ?, and we need to replace those so we can compare our stored edge + String edgeName = edge.getName(); + EdgeStatus edgeStatus = EdgeStatus.INPUT; + if (edgeName.contains("?")) { + edgeName = edgeName.replace("?", ""); + } else if (edgeName.contains("!")) { + edgeName = edgeName.replace("!", ""); + edgeStatus = EdgeStatus.OUTPUT; + } + + // Self loop on a Universal locations means that the edge name should be mapped to * + if (isSourceUniversal && sourceName.equals(targetName)) { + edgeName = "*"; + } + + highlightEdge(edgeName, edgeStatus, sourceName, targetName); + } + } + + /** + * Unhighlights all edges and locations in the process + */ + public void unhighlightProcess() { + edgePresentationMap.forEach((key, value) -> value.getController().unhighlight()); + locationPresentationMap.forEach((key, value) -> value.unhighlight()); + } + + /** + * Helper method that finds the {@link SimLocationPresentation} and highlights it. + * Calls {@link ProcessController#highlightEdgeLocations(String, String)} to highlight the source/targets locations + * @param edgeName The name of the edge + * @param edgeStatus The status (input/output) of the edge to highlight + * @param sourceName The name of the source location + * @param targetName The name of the target location + */ + private void highlightEdge(final String edgeName, final EdgeStatus edgeStatus, final String sourceName, final String targetName) { + for (Map.Entry entry: edgePresentationMap.entrySet()) { + final String keyName = entry.getKey().getSync(); + final String keySourceName = entry.getKey().getSourceLocation().getId(); + final String keyTargetName = entry.getKey().getTargetLocation().getId(); + + // Multiple edges may have the same name, so we also check that the source and target match this edge + if(keyName.equals(edgeName) && + keySourceName.equals(sourceName) && + keyTargetName.equals(targetName) && + entry.getKey().ioStatus.get() == edgeStatus) { + + entry.getValue().getController().highlight(); + highlightEdgeLocations(keySourceName, keyTargetName); + } + } + } + + /** + * Helper method that finds the source/target {@link SimLocationPresentation} and highlights it + * @param sourceName The name of the source location + * @param targetName The name of the target location + */ + private void highlightEdgeLocations(final String sourceName, final String targetName) { + for (Map.Entry locEntry: locationPresentationMap.entrySet()) { + final String locName = locEntry.getKey().getId(); + + // Check if location is either source or target and highlight it + if(locName.equals(sourceName) || locName.equals(targetName)) { + locEntry.getValue().highlight(); + } + } + } + + /** + * Method that highlights all locations with the same name as the input {@link SimulationLocation} + * @param location The locations to highlight + */ + public void highlightLocation(final SimulationLocation location) { + for (Map.Entry locEntry: locationPresentationMap.entrySet()) { + final String locName = locEntry.getKey().getId(); + + if(locName.equals(location.getName())) { + locEntry.getValue().highlight(); + } + } + } + + /** + * Sets the component which is going to be shown as a process.
+ * This also initializes the rest of the views needed for the process to be shown properly + * @param component the component of the process + */ + public void setComponent(final Component component){ + this.component.set(component); + modelContainerEdge.getChildren().clear(); + modelContainerLocation.getChildren().clear(); + + component.getLocations().forEach(location -> { + final SimLocationPresentation lp = new SimLocationPresentation(location, component); + modelContainerLocation.getChildren().add(lp); + locationPresentationMap.put(location, lp); + }); + + component.getEdges().forEach(edge -> { + final SimEdgePresentation ep = new SimEdgePresentation(edge, component); + modelContainerEdge.getChildren().add(ep); + edgePresentationMap.put(edge, ep); + }); + } + + public void toggleValues(final MouseEvent mouseEvent) { + final Circle circle = new Circle(0); + circle.setCenterX(component.get().getBox().getWidth() - (toggleValuesButton.getWidth() - mouseEvent.getX())); + circle.setCenterY(-1 * mouseEvent.getY()); + + final ObjectProperty clip = new SimpleObjectProperty<>(circle); + valueArea.clipProperty().bind(clip); + + final Transition rippleEffect = new Transition() { + private final double maxRadius = Math.sqrt(Math.pow(getComponent().getBox().getWidth(), 2) + Math.pow(getComponent().getBox().getHeight(), 2)); + { + setCycleDuration(Duration.millis(500)); + } + + protected void interpolate(final double fraction) { + if (getComponent().isDeclarationOpen()) { + circle.setRadius(fraction * maxRadius); + } else { + circle.setRadius(maxRadius - fraction * maxRadius); + } + clip.set(circle); + } + }; + + final Interpolator interpolator = Interpolator.SPLINE(0.785, 0.135, 0.15, 0.86); + rippleEffect.setInterpolator(interpolator); + + rippleEffect.play(); + getComponent().declarationOpenProperty().set(!getComponent().isDeclarationOpen()); + } + + /** + * Gets the component linked to this process + * @return the component of the process + */ + public Component getComponent(){ + return component.get(); + } + + @Override + public HighLevelModelObject getModel() { + return component.get(); + } + + public ObservableMap getVariables() { + return variables; + } + + public ObservableMap getClocks() { + return clocks; + } +} diff --git a/src/main/java/ecdar/controllers/RightSimPaneController.java b/src/main/java/ecdar/controllers/RightSimPaneController.java new file mode 100755 index 00000000..c7d5b6db --- /dev/null +++ b/src/main/java/ecdar/controllers/RightSimPaneController.java @@ -0,0 +1,21 @@ +package ecdar.controllers; + +import ecdar.presentations.QueryPaneElementPresentation; +import javafx.fxml.Initializable; +import javafx.scene.layout.*; + +import java.net.URL; +import java.util.ResourceBundle; + +/** + * Controller class for the right pane in the simulator + */ +public class RightSimPaneController implements Initializable { + public StackPane root; + public VBox scrollPaneVbox; + public QueryPaneElementPresentation queryPaneElement; + + @Override + public void initialize(URL location, ResourceBundle resources) { + } +} diff --git a/src/main/java/ecdar/controllers/SimEdgeController.java b/src/main/java/ecdar/controllers/SimEdgeController.java new file mode 100755 index 00000000..2ca0048f --- /dev/null +++ b/src/main/java/ecdar/controllers/SimEdgeController.java @@ -0,0 +1,421 @@ +package ecdar.controllers; + +import ecdar.abstractions.Component; +import ecdar.abstractions.Edge; +import ecdar.abstractions.EdgeStatus; +import ecdar.abstractions.Nail; +import ecdar.model_canvas.arrow_heads.SimpleArrowHead; +import ecdar.presentations.Link; +import ecdar.presentations.NailPresentation; +import ecdar.presentations.SimNailPresentation; +import ecdar.utility.Highlightable; +import ecdar.utility.colors.Color; +import ecdar.utility.helpers.BindingHelper; +import ecdar.utility.helpers.Circular; +import ecdar.utility.helpers.ItemDragHelper; +import ecdar.utility.helpers.SelectHelper; +import ecdar.utility.keyboard.KeyboardTracker; +import javafx.animation.KeyFrame; +import javafx.animation.KeyValue; +import javafx.animation.Timeline; +import javafx.application.Platform; +import javafx.beans.property.*; +import javafx.beans.value.ChangeListener; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; +import javafx.fxml.Initializable; +import javafx.scene.Group; +import javafx.scene.Node; +import javafx.util.Duration; + +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.function.Consumer; + +/** + * The controller for the edge shown in the {@link ecdar.presentations.SimulatorOverviewPresentation} + */ +public class SimEdgeController implements Initializable, Highlightable { + private final ObservableList links = FXCollections.observableArrayList(); + private final ObjectProperty edge = new SimpleObjectProperty<>(); + private final ObjectProperty component = new SimpleObjectProperty<>(); + private final SimpleArrowHead simpleArrowHead = new SimpleArrowHead(); + private final SimpleBooleanProperty isHoveringEdge = new SimpleBooleanProperty(false); + private final SimpleIntegerProperty timeHoveringEdge = new SimpleIntegerProperty(0); + private final Map nailNailPresentationMap = new HashMap<>(); + public Group edgeRoot; + private Runnable collapseNail; + private Consumer enlargeNail; + private Consumer shrinkNail; + + @Override + public void initialize(final URL location, final ResourceBundle resources) { + initializeNailCollapse(); + + edge.addListener((obsEdge, oldEdge, newEdge) -> { + newEdge.targetCircularProperty().addListener(getNewTargetCircularListener(newEdge)); + component.addListener(getComponentChangeListener(newEdge)); + + // Invalidate the list of edges (to update UI and errors) + newEdge.targetCircularProperty().addListener(observable -> { + getComponent().removeEdge(getEdge()); + getComponent().addEdge(getEdge()); + }); + + // When an edge updates highlight property, + // we want to update the view to reflect current highlight property + edge.get().isHighlightedProperty().addListener(v -> { + if(edge.get().getIsHighlighted()) { + this.highlight(); + } else { + this.unhighlight(); + } + }); + }); + + ensureNailsInFront(); + } + + private void ensureNailsInFront() { + // When ever changes happens to the children of the edge root force nails in front and other elements to back + edgeRoot.getChildren().addListener((ListChangeListener) c -> { + while (c.next()) { + for (int i = 0; i < c.getAddedSize(); i++) { + final Node node = c.getAddedSubList().get(i); + if (node instanceof NailPresentation) { + node.toFront(); + } else { + node.toBack(); + } + } + } + }); + } + + private ChangeListener getComponentChangeListener(final Edge newEdge) { + return (obsComponent, oldComponent, newComponent) -> { + // Draw new edge from a location + if (newEdge.getNails().isEmpty() && newEdge.getTargetCircular() == null) { + final Link link = new Link(); + // Make dashed line, if output edge + if (newEdge.getStatus() == EdgeStatus.OUTPUT) link.makeDashed(); + links.add(link); + + // Add the link and its arrowhead to the view + edgeRoot.getChildren().addAll(link, simpleArrowHead); + + // Bind the first link and the arrowhead from the source location to the mouse + BindingHelper.bind(link, simpleArrowHead, newEdge.getSourceCircular(), + newComponent.getBox().getXProperty(), newComponent.getBox().getYProperty()); + } else if (newEdge.getTargetCircular() != null) { + + edgeRoot.getChildren().add(simpleArrowHead); + + final Circular[] previous = {newEdge.getSourceCircular()}; + + newEdge.getNails().forEach(nail -> { + final Link link = new Link(); + if (newEdge.getStatus() == EdgeStatus.OUTPUT) link.makeDashed(); + links.add(link); + + final SimNailPresentation nailPresentation = new SimNailPresentation(nail, newEdge, getComponent(), this); + nailNailPresentationMap.put(nail, nailPresentation); + + edgeRoot.getChildren().addAll(link, nailPresentation); + BindingHelper.bind(link, previous[0], nail); + + previous[0] = nail; + }); + + final Link link = new Link(); + if (newEdge.getStatus() == EdgeStatus.OUTPUT) link.makeDashed(); + links.add(link); + + edgeRoot.getChildren().add(link); + BindingHelper.bind(link, simpleArrowHead, previous[0], newEdge.getTargetCircular()); + } + + // Changes are made to the nails list + newEdge.getNails().addListener(getNailsChangeListener(newEdge, newComponent)); + + }; + } + + private ListChangeListener getNailsChangeListener(final Edge newEdge, final Component newComponent) { + return change -> { + while (change.next()) { + // There were added some nails + change.getAddedSubList().forEach(newNail -> { + // Create a new nail presentation based on the abstraction added to the list + final SimNailPresentation newNailPresentation = new SimNailPresentation(newNail, newEdge, newComponent, this); + nailNailPresentationMap.put(newNail, newNailPresentation); + + edgeRoot.getChildren().addAll(newNailPresentation); + + if (newEdge.getTargetCircular() != null) { + final int indexOfNewNail = edge.get().getNails().indexOf(newNail); + + final Link newLink = new Link(); + if (newEdge.getStatus() == EdgeStatus.OUTPUT) newLink.makeDashed(); + final Link pressedLink = links.get(indexOfNewNail); + links.add(indexOfNewNail, newLink); + + edgeRoot.getChildren().addAll(newLink); + + Circular oldStart = getEdge().getSourceCircular(); + Circular oldEnd = getEdge().getTargetCircular(); + + if (indexOfNewNail != 0) { + oldStart = getEdge().getNails().get(indexOfNewNail - 1); + } + + if (indexOfNewNail != getEdge().getNails().size() - 1) { + oldEnd = getEdge().getNails().get(indexOfNewNail + 1); + } + + BindingHelper.bind(newLink, oldStart, newNail); + + if (oldEnd.equals(getEdge().getTargetCircular())) { + BindingHelper.bind(pressedLink, simpleArrowHead, newNail, oldEnd); + } else { + BindingHelper.bind(pressedLink, newNail, oldEnd); + } + + if (isHoveringEdge.get()) { + enlargeNail.accept(newNail); + } + + } else { + // The previous last link must end in the new nail + final Link lastLink = links.get(links.size() - 1); + + // If the nail is the first in the list, bind it to the source location + // otherwise, bind it the the previous nail + final int nailIndex = edge.get().getNails().indexOf(newNail); + if (nailIndex == 0) { + BindingHelper.bind(lastLink, newEdge.getSourceCircular(), newNail); + } else { + final Nail previousNail = edge.get().getNails().get(nailIndex - 1); + BindingHelper.bind(lastLink, previousNail, newNail); + } + + // Create a new link that will bind from the new nail to the mouse + final Link newLink = new Link(); + if (newEdge.getStatus() == EdgeStatus.OUTPUT) newLink.makeDashed(); + links.add(newLink); + BindingHelper.bind(newLink, simpleArrowHead, newNail, newComponent.getBox().getXProperty(), newComponent.getBox().getYProperty()); + edgeRoot.getChildren().add(newLink); + } + }); + + change.getRemoved().forEach(removedNail -> { + final int removedIndex = change.getFrom(); + final SimNailPresentation removedNailPresentation = nailNailPresentationMap.remove(removedNail); + final Link danglingLink = links.get(removedIndex + 1); + edgeRoot.getChildren().remove(removedNailPresentation); + edgeRoot.getChildren().remove(links.get(removedIndex)); + + Circular newFrom = getEdge().getSourceCircular(); + Circular newTo = getEdge().getTargetCircular(); + + if (removedIndex > 0) { + newFrom = getEdge().getNails().get(removedIndex - 1); + } + + if (removedIndex - 1 != getEdge().getNails().size() - 1) { + newTo = getEdge().getNails().get(removedIndex); + } + + if (newTo.equals(getEdge().getTargetCircular())) { + BindingHelper.bind(danglingLink, simpleArrowHead, newFrom, newTo); + } else { + BindingHelper.bind(danglingLink, newFrom, newTo); + } + links.remove(removedIndex); + }); + } + }; + } + + private ChangeListener getNewTargetCircularListener(final Edge newEdge) { + // When the target location is set, finish drawing the edge + return (obsTargetLocation, oldTargetCircular, newTargetCircular) -> { + // If the nails list is empty, directly connect the source and target locations + // otherwise, bind the line from the last nail to the target location + final Link lastLink = links.get(links.size() - 1); + final ObservableList nails = getEdge().getNails(); + if (nails.size() == 0) { + BindingHelper.bind(lastLink, simpleArrowHead, newEdge.getSourceCircular(), newEdge.getTargetCircular()); + } else { + final Nail lastNail = nails.get(nails.size() - 1); + BindingHelper.bind(lastLink, simpleArrowHead, lastNail, newEdge.getTargetCircular()); + } + + KeyboardTracker.unregisterKeybind(KeyboardTracker.ABANDON_EDGE); + + // When the target location is set the + edgeRoot.setMouseTransparent(false); + }; + } + + /** + * Initializes functionality to enlarge, shirk, and collapse nails + */ + private void initializeNailCollapse() { + enlargeNail = nail -> { + if (!nail.getPropertyType().equals(Edge.PropertyType.NONE)) return; + final Timeline animation = new Timeline(); + + final KeyValue radius0 = new KeyValue(nail.radiusProperty(), NailPresentation.COLLAPSED_RADIUS); + final KeyValue radius2 = new KeyValue(nail.radiusProperty(), NailPresentation.HOVERED_RADIUS * 1.2); + final KeyValue radius1 = new KeyValue(nail.radiusProperty(), NailPresentation.HOVERED_RADIUS); + + final KeyFrame kf1 = new KeyFrame(Duration.millis(0), radius0); + final KeyFrame kf2 = new KeyFrame(Duration.millis(80), radius2); + final KeyFrame kf3 = new KeyFrame(Duration.millis(100), radius1); + + animation.getKeyFrames().addAll(kf1, kf2, kf3); + animation.play(); + }; + shrinkNail = nail -> { + if (!nail.getPropertyType().equals(Edge.PropertyType.NONE)) return; + final Timeline animation = new Timeline(); + + final KeyValue radius0 = new KeyValue(nail.radiusProperty(), NailPresentation.COLLAPSED_RADIUS); + final KeyValue radius1 = new KeyValue(nail.radiusProperty(), NailPresentation.HOVERED_RADIUS); + + final KeyFrame kf1 = new KeyFrame(Duration.millis(0), radius1); + final KeyFrame kf2 = new KeyFrame(Duration.millis(100), radius0); + + animation.getKeyFrames().addAll(kf1, kf2); + animation.play(); + }; + + collapseNail = () -> { + final int interval = 50; + int previousValue = 1; + + try { + while (true) { + Thread.sleep(interval); + + if (isHoveringEdge.get()) { + // Do not let the timer go above this threshold + if (timeHoveringEdge.get() <= 500) { + timeHoveringEdge.set(timeHoveringEdge.get() + interval); + } + } else { + timeHoveringEdge.set(timeHoveringEdge.get() - interval); + } + + if (previousValue >= 0 && timeHoveringEdge.get() < 0) { + // Run on UI thread + Platform.runLater(() -> { + // Collapse all nails + getEdge().getNails().forEach(shrinkNail); + }); + break; + } + previousValue = timeHoveringEdge.get(); + } + + } catch (final InterruptedException e) { + e.printStackTrace(); + } + }; + } + + public Edge getEdge() { + return edge.get(); + } + + public void setEdge(final Edge edge) { + this.edge.set(edge); + } + + public ObjectProperty edgeProperty() { + return edge; + } + + public Component getComponent() { + return component.get(); + } + + public void setComponent(final Component component) { + this.component.set(component); + } + + public ObjectProperty componentProperty() { + return component; + } + + /** + * Colors the edge model + * @param color the new color of the edge + * @param intensity the intensity of the edge + */ + public void color(final Color color, final Color.Intensity intensity) { + final Edge edge = getEdge(); + + // Set the color of the edge + edge.setColorIntensity(intensity); + edge.setColor(color); + } + + public Color getColor() { + return getEdge().getColor(); + } + + public Color.Intensity getColorIntensity() { + return getEdge().getColorIntensity(); + } + + public ItemDragHelper.DragBounds getDragBounds() { + return ItemDragHelper.DragBounds.generateLooseDragBounds(); + } + + /*** + * Highlights the child nodes of the edge + */ + @Override + public void highlight() { + // Clear the currently selected elements, so we don't have multiple things highlighted/selected + SelectHelper.clearSelectedElements(); + edgeRoot.getChildren().forEach(node -> { + if(node instanceof Highlightable) { + ((Highlightable) node).highlight(); + } + }); + } + + /*** + * Removes the highlight from child nodes + */ + @Override + public void unhighlight() { + edgeRoot.getChildren().forEach(node -> { + if(node instanceof Highlightable) { + ((Highlightable) node).unhighlight(); + } + }); + } + + public DoubleProperty xProperty() { + return edgeRoot.layoutXProperty(); + } + + public DoubleProperty yProperty() { + return edgeRoot.layoutYProperty(); + } + + public double getX() { + return xProperty().get(); + } + + public double getY() { + return yProperty().get(); + } +} diff --git a/src/main/java/ecdar/controllers/SimEdgePresentation.java b/src/main/java/ecdar/controllers/SimEdgePresentation.java new file mode 100755 index 00000000..8d74623b --- /dev/null +++ b/src/main/java/ecdar/controllers/SimEdgePresentation.java @@ -0,0 +1,32 @@ +package ecdar.presentations; + +import ecdar.abstractions.Component; +import ecdar.abstractions.Edge; +import ecdar.controllers.SimEdgeController; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.scene.Group; + +/** + * The presentation class for the edges shown in the {@link SimulatorOverviewPresentation} + */ +public class SimEdgePresentation extends Group { + private final SimEdgeController controller; + + private final ObjectProperty edge = new SimpleObjectProperty<>(); + private final ObjectProperty component = new SimpleObjectProperty<>(); + + public SimEdgePresentation(final Edge edge, final Component component) { + controller = new EcdarFXMLLoader().loadAndGetController("SimEdgePresentation.fxml", this); + + controller.setEdge(edge); + this.edge.bind(controller.edgeProperty()); + + controller.setComponent(component); + this.component.bind(controller.componentProperty()); + } + + public SimEdgeController getController() { + return controller; + } +} diff --git a/src/main/java/ecdar/controllers/SimLocationController.java b/src/main/java/ecdar/controllers/SimLocationController.java new file mode 100755 index 00000000..c8c39522 --- /dev/null +++ b/src/main/java/ecdar/controllers/SimLocationController.java @@ -0,0 +1,124 @@ +package ecdar.controllers; + +import ecdar.abstractions.Component; +import ecdar.abstractions.Location; +import ecdar.presentations.SimLocationPresentation; +import ecdar.presentations.SimTagPresentation; +import ecdar.utility.colors.Color; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.fxml.Initializable; +import javafx.scene.Group; +import javafx.scene.control.Label; +import javafx.scene.shape.Circle; +import javafx.scene.shape.Line; +import javafx.scene.shape.Path; + +import java.net.URL; +import java.util.ResourceBundle; + +/** + * The controller of a location shown in the {@link ecdar.presentations.SimulatorOverviewPresentation} + */ +public class SimLocationController implements Initializable { + private final ObjectProperty location = new SimpleObjectProperty<>(); + private final ObjectProperty component = new SimpleObjectProperty<>(); + public SimLocationPresentation root; + public Path notCommittedShape; + public Path notCommittedInitialIndicator; + public Group shakeContent; + public Circle circle; + public Circle circleShakeIndicator; + public Group scaleContent; + public SimTagPresentation nicknameTag; + public SimTagPresentation invariantTag; + public Label idLabel; + public Line nameTagLine; + public Line invariantTagLine; + + @Override + public void initialize(final URL location, final ResourceBundle resources) { + this.location.addListener((obsLocation, oldLocation, newLocation) -> { + // The radius property on the abstraction must reflect the radius in the view + newLocation.radiusProperty().bind(circle.radiusProperty()); + + // The scale property on the abstraction must reflect the radius in the view + newLocation.scaleProperty().bind(scaleContent.scaleXProperty()); + }); + + // Scale x and y 1:1 (based on the x-scale) + scaleContent.scaleYProperty().bind(scaleContent.scaleXProperty()); + } + + public Location getLocation() { + return location.get(); + } + + /** + * Set/places the given location on the view. + * This have to be done before adding the {@link SimLocationPresentation} to the view as nothing + * would then be displayed. + * @param location the location + */ + public void setLocation(final Location location) { + this.location.set(location); + root.setLayoutX(location.getX()); + root.setLayoutY(location.getY()); + location.xProperty().bindBidirectional(root.layoutXProperty()); + location.yProperty().bindBidirectional(root.layoutYProperty()); + } + + public ObjectProperty locationProperty() { + return location; + } + + public Component getComponent() { + return component.get(); + } + + public void setComponent(final Component component) { + this.component.set(component); + } + + public ObjectProperty componentProperty() { + return component; + } + + /** + * Colors the location model + * @param color the new color of the location + * @param intensity the intensity of the color + */ + public void color(final Color color, final Color.Intensity intensity) { + final Location location = getLocation(); + + // Set the color of the location + location.setColorIntensity(intensity); + location.setColor(color); + } + + public Color getColor() { + return getLocation().getColor(); + } + + public Color.Intensity getColorIntensity() { + return getLocation().getColorIntensity(); + } + + public DoubleProperty xProperty() { + return root.layoutXProperty(); + } + + public DoubleProperty yProperty() { + return root.layoutYProperty(); + } + + public double getX() { + return xProperty().get(); + } + + public double getY() { + return yProperty().get(); + } +} diff --git a/src/main/java/ecdar/controllers/SimLocationPresentation.java b/src/main/java/ecdar/controllers/SimLocationPresentation.java new file mode 100755 index 00000000..7d87e377 --- /dev/null +++ b/src/main/java/ecdar/controllers/SimLocationPresentation.java @@ -0,0 +1,490 @@ +package ecdar.presentations; + +import ecdar.abstractions.Component; +import ecdar.abstractions.Location; +import ecdar.controllers.SimLocationController; +import ecdar.utility.Highlightable; +import ecdar.utility.colors.Color; +import ecdar.utility.helpers.BindingHelper; +import ecdar.utility.helpers.SelectHelper; +import javafx.animation.*; +import javafx.beans.binding.DoubleBinding; +import javafx.beans.property.*; +import javafx.scene.Group; +import javafx.scene.control.Label; +import javafx.scene.effect.DropShadow; +import javafx.scene.paint.Paint; +import javafx.scene.shape.*; +import javafx.util.Duration; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import static ecdar.presentations.LocationPresentation.INITIAL_RADIUS; +import static ecdar.presentations.LocationPresentation.RADIUS; + +/** + * Presentation for a location in the {@link SimulatorOverviewPresentation}. + * This class should be refactored such that the shared code between this class + * and {@link LocationPresentation} is placed in a base class. + */ +public class SimLocationPresentation extends Group implements Highlightable { + + private static int id = 0; + private final SimLocationController controller; + private final Timeline initialAnimation = new Timeline(); + private final Timeline hoverAnimationEntered = new Timeline(); + private final Timeline hoverAnimationExited = new Timeline(); + private final Timeline hiddenAreaAnimationEntered = new Timeline(); + private final Timeline hiddenAreaAnimationExited = new Timeline(); + private final Timeline scaleShakeIndicatorBackgroundAnimation = new Timeline(); + private final Timeline shakeContentAnimation = new Timeline(); + private final List> updateColorDelegates = new ArrayList<>(); + private final DoubleProperty animation = new SimpleDoubleProperty(0); + private final DoubleBinding reverseAnimation = new SimpleDoubleProperty(1).subtract(animation); + + /** + * Constructs a Simulator Location ready to be placed on the view + * @param location the location model, which the presenter should show + * @param component the component where the location is + */ + public SimLocationPresentation(final Location location, final Component component) { + controller = new EcdarFXMLLoader().loadAndGetController("SimLocationPresentation.fxml", this); + + // Bind the component with the one of the controller + controller.setComponent(component); + + // Bind the location with the one of the controller + controller.setLocation(location); + + initializeIdLabel(); + initializeTypeGraphics(); + initializeLocationShapes(); + initializeTags(); + initializeShakeAnimation(); + initializeCircle(); + } + + /** + * Initializes the label in the middle of the location + */ + private void initializeIdLabel() { + final Location location = controller.getLocation(); + final Label idLabel = controller.idLabel; + + final DropShadow ds = new DropShadow(); + ds.setRadius(2); + ds.setSpread(1); + + idLabel.setEffect(ds); + + idLabel.textProperty().bind((location.idProperty())); + + // Center align the label + idLabel.widthProperty().addListener((obsWidth, oldWidth, newWidth) -> idLabel.translateXProperty().set(newWidth.doubleValue() / -2)); + idLabel.heightProperty().addListener((obsHeight, oldHeight, newHeight) -> idLabel.translateYProperty().set(newHeight.doubleValue() / -2)); + + final ObjectProperty color = location.colorProperty(); + final ObjectProperty colorIntensity = location.colorIntensityProperty(); + + // Delegate to style the label based on the color of the location + final BiConsumer updateColor = (newColor, newIntensity) -> { + idLabel.setTextFill(newColor.getTextColor(newIntensity)); + ds.setColor(newColor.getColor(newIntensity)); + }; + + updateColorDelegates.add(updateColor); + + // Set the initial color + updateColor.accept(color.get(), colorIntensity.get()); + + // Update the color of the circle when the color of the location is updated + color.addListener((obs, old, newColor) -> updateColor.accept(newColor, colorIntensity.get())); + } + + /** + * Initialize the tags placed on this location + */ + private void initializeTags() { + + // Set the layout from the model (if they are not both 0) + final Location loc = controller.getLocation(); + if((loc.getNicknameX() != 0) && (loc.getNicknameY() != 0)) { + controller.nicknameTag.setTranslateX(loc.getNicknameX()); + controller.nicknameTag.setTranslateY(loc.getNicknameY()); + } + + if((loc.getInvariantX() != 0) && (loc.getInvariantY() != 0)) { + controller.invariantTag.setTranslateX(loc.getInvariantX()); + controller.invariantTag.setTranslateY(loc.getInvariantY()); + } + + // Bind the model to the layout + loc.nicknameXProperty().bind(controller.nicknameTag.translateXProperty()); + loc.nicknameYProperty().bind(controller.nicknameTag.translateYProperty()); + loc.invariantXProperty().bind(controller.invariantTag.translateXProperty()); + loc.invariantYProperty().bind(controller.invariantTag.translateYProperty()); + + final Consumer updateTags = location -> { + // Update the color + controller.nicknameTag.bindToColor(location.colorProperty(), location.colorIntensityProperty(), true); + controller.invariantTag.bindToColor(location.colorProperty(), location.colorIntensityProperty(), false); + + // Update the invariant + controller.nicknameTag.setAndBindString(location.nicknameProperty()); + controller.invariantTag.setAndBindString(location.invariantProperty()); + + // Set the visibility of the name tag depending on the nickname + final Consumer updateVisibilityFromNickName = (nickname) -> { + if (nickname.equals("")) { + controller.nicknameTag.setOpacity(0); + } else { + controller.nicknameTag.setOpacity(1); + } + }; + + location.nicknameProperty().addListener((obs, oldNickname, newNickname) -> updateVisibilityFromNickName.accept(newNickname)); + updateVisibilityFromNickName.accept(location.getNickname()); + + // Set the visibility of the invariant tag depending on the invariant + final Consumer updateVisibilityFromInvariant = (invariant) -> { + if (invariant.equals("") ) { + controller.invariantTag.setOpacity(0); + } else { + controller.invariantTag.setOpacity(1); + } + }; + + location.invariantProperty().addListener((obs, oldInvariant, newInvariant) -> updateVisibilityFromInvariant.accept(newInvariant)); + updateVisibilityFromInvariant.accept(location.getInvariant()); + + controller.nicknameTag.setComponent(controller.getComponent()); + controller.nicknameTag.setLocationAware(location); + BindingHelper.bind(controller.nameTagLine, controller.nicknameTag); + + controller.invariantTag.setComponent(controller.getComponent()); + controller.invariantTag.setLocationAware(location); + BindingHelper.bind(controller.invariantTagLine, controller.invariantTag); + }; + + // Update the tags when the loc updates + controller.locationProperty().addListener(observable -> updateTags.accept(loc)); + + // Initialize the tags from the current loc + updateTags.accept(loc); + } + + /** + * Initialize the circle which makes up most of the location + */ + private void initializeCircle() { + final Location location = controller.getLocation(); + + final Circle circle = controller.circle; + circle.setRadius(RADIUS); + final ObjectProperty color = location.colorProperty(); + final ObjectProperty colorIntensity = location.colorIntensityProperty(); + + // Delegate to style the label based on the color of the location + final BiConsumer updateColor = (newColor, newIntensity) -> { + circle.setFill(newColor.getColor(newIntensity)); + circle.setStroke(newColor.getColor(newIntensity.next(2))); + }; + + updateColorDelegates.add(updateColor); + + // Set the initial color + updateColor.accept(color.get(), colorIntensity.get()); + + // Update the color of the circle when the color of the location is updated + color.addListener((obs, old, newColor) -> updateColor.accept(newColor, colorIntensity.get())); + colorIntensity.addListener((obs, old, newIntensity) -> updateColor.accept(color.get(), newIntensity)); + } + + /** + * Initializes the shapes of a urgent location + */ + private void initializeLocationShapes() { + final Path notCommittedShape = controller.notCommittedShape; + + // Bind sizes for shape that transforms between urgent and normal + initializeLocationShapes(notCommittedShape, RADIUS); + + final Location location = controller.getLocation(); + + final BiConsumer updateUrgencies = (oldUrgency, newUrgency) -> { + final Transition toUrgent = new Transition() { + { + setCycleDuration(Duration.millis(200)); + } + + @Override + protected void interpolate(final double frac) { + animation.set(frac); + } + }; + + final Transition toNormal = new Transition() { + { + setCycleDuration(Duration.millis(200)); + } + + @Override + protected void interpolate(final double frac) { + animation.set(1-frac); + } + }; + + if(oldUrgency.equals(Location.Urgency.NORMAL) && !newUrgency.equals(Location.Urgency.NORMAL)) { + toUrgent.play(); + } else if(newUrgency.equals(Location.Urgency.NORMAL)) { + toNormal.play(); + } + notCommittedShape.setVisible(true); + }; + + location.urgencyProperty().addListener((obsUrgency, oldUrgency, newUrgency) -> { + updateUrgencies.accept(oldUrgency, newUrgency); + }); + + updateUrgencies.accept(Location.Urgency.NORMAL, location.getUrgency()); + + // Update the colors + final ObjectProperty color = location.colorProperty(); + final ObjectProperty colorIntensity = location.colorIntensityProperty(); + + // Delegate to style the label based on the color of the location + final BiConsumer updateColor = (newColor, newIntensity) -> { + notCommittedShape.setFill(newColor.getColor(newIntensity)); + notCommittedShape.setStroke(newColor.getColor(newIntensity.next(2))); + }; + + updateColorDelegates.add(updateColor); + + // Set the initial color + updateColor.accept(color.get(), colorIntensity.get()); + + // Update the color of the circle when the color of the location is updated + color.addListener((obs, old, newColor) -> updateColor.accept(newColor, colorIntensity.get())); + } + + private void initializeTypeGraphics() { + final Location location = controller.getLocation(); + + final Path notCommittedInitialIndicator = controller.notCommittedInitialIndicator; + + // Bind visibility and size of normal shape + initializeLocationShapes(notCommittedInitialIndicator, INITIAL_RADIUS); + notCommittedInitialIndicator.visibleProperty().bind(location.typeProperty().isEqualTo(Location.Type.INITIAL)); + // As the style sometimes "forget" its values + notCommittedInitialIndicator.setStrokeType(StrokeType.INSIDE); + notCommittedInitialIndicator.setStroke(Paint.valueOf("white")); + notCommittedInitialIndicator.setFill(Paint.valueOf("transparent")); + } + + public void animateIn() { + initialAnimation.play(); + } + + public void animateHoverEntered() { + + if (shakeContentAnimation.getStatus().equals(Animation.Status.RUNNING)) return; + + hoverAnimationEntered.play(); + } + + public void animateHoverExited() { + if (shakeContentAnimation.getStatus().equals(Animation.Status.RUNNING)) return; + + hoverAnimationExited.play(); + } + + public void animateLocationEntered() { + hiddenAreaAnimationExited.stop(); + hiddenAreaAnimationEntered.play(); + } + + public void animateLocationExited() { + hiddenAreaAnimationEntered.stop(); + hiddenAreaAnimationExited.play(); + } + + + /** + * Initializes the animation of shaking the location. Can for instance be used when the user tries an + * action which is not allowed, i.e. deleting + */ + private void initializeShakeAnimation() { + final Interpolator interpolator = Interpolator.SPLINE(0.645, 0.045, 0.355, 1); + + final KeyValue scale0x = new KeyValue(controller.scaleContent.scaleXProperty(), 1, interpolator); + final KeyValue radius0 = new KeyValue(controller.circleShakeIndicator.radiusProperty(), 0, interpolator); + final KeyValue opacity0 = new KeyValue(controller.circleShakeIndicator.opacityProperty(), 0, interpolator); + + final KeyValue scale1x = new KeyValue(controller.scaleContent.scaleXProperty(), 1.3, interpolator); + final KeyValue radius1 = new KeyValue(controller.circleShakeIndicator.radiusProperty(), controller.circle.getRadius() * 0.85, interpolator); + final KeyValue opacity1 = new KeyValue(controller.circleShakeIndicator.opacityProperty(), 0.2, interpolator); + + final KeyFrame kf1 = new KeyFrame(Duration.millis(0), scale0x, radius0, opacity0); + final KeyFrame kf2 = new KeyFrame(Duration.millis(2500), scale1x, radius1, opacity1); + final KeyFrame kf3 = new KeyFrame(Duration.millis(3300), radius0, opacity0); + final KeyFrame kf4 = new KeyFrame(Duration.millis(3500), scale0x); + final KeyFrame kfEnd = new KeyFrame(Duration.millis(8000)); + + scaleShakeIndicatorBackgroundAnimation.getKeyFrames().addAll(kf1, kf2, kf3, kf4, kfEnd); + + final KeyValue noShakeX = new KeyValue(controller.shakeContent.translateXProperty(), 0, interpolator); + final KeyValue shakeLeftX = new KeyValue(controller.shakeContent.translateXProperty(), -1, interpolator); + final KeyValue shakeRightX = new KeyValue(controller.shakeContent.translateXProperty(), 1, interpolator); + + final KeyFrame[] shakeFrames = { + new KeyFrame(Duration.millis(0), noShakeX), + new KeyFrame(Duration.millis(1450), noShakeX), + + new KeyFrame(Duration.millis(1500), shakeLeftX), + new KeyFrame(Duration.millis(1550), shakeRightX), + new KeyFrame(Duration.millis(1600), shakeLeftX), + new KeyFrame(Duration.millis(1650), shakeRightX), + new KeyFrame(Duration.millis(1700), shakeLeftX), + new KeyFrame(Duration.millis(1750), shakeRightX), + new KeyFrame(Duration.millis(1800), shakeLeftX), + new KeyFrame(Duration.millis(1850), shakeRightX), + new KeyFrame(Duration.millis(1900), shakeLeftX), + new KeyFrame(Duration.millis(1950), shakeRightX), + new KeyFrame(Duration.millis(2000), shakeLeftX), + new KeyFrame(Duration.millis(2050), shakeRightX), + new KeyFrame(Duration.millis(2100), shakeLeftX), + new KeyFrame(Duration.millis(2150), shakeRightX), + new KeyFrame(Duration.millis(2200), shakeLeftX), + new KeyFrame(Duration.millis(2250), shakeRightX), + + new KeyFrame(Duration.millis(2300), noShakeX), + new KeyFrame(Duration.millis(8000)) + }; + + shakeContentAnimation.getKeyFrames().addAll(shakeFrames); + + shakeContentAnimation.setCycleCount(1000); + scaleShakeIndicatorBackgroundAnimation.setCycleCount(1000); + } + + /** + * Plays the shake animation + * @param start false - the animation resets to the beginning
+ * true - the animation starts + */ + public void animateShakeWarning(final boolean start) { + if (start) { + scaleShakeIndicatorBackgroundAnimation.play(); + shakeContentAnimation.play(); + } else { + controller.scaleContent.scaleXProperty().set(1); + scaleShakeIndicatorBackgroundAnimation.playFromStart(); + scaleShakeIndicatorBackgroundAnimation.stop(); + + controller.circleShakeIndicator.setOpacity(0); + shakeContentAnimation.playFromStart(); + shakeContentAnimation.stop(); + } + } + + /** + * Get the controller associated with this presenter + * @return the controller + */ + public SimLocationController getController() { + return controller; + } + + private void initializeLocationShapes(final Path locationShape, final double radius) { + final double c = 0.551915024494; + final double circleToOctagonLineRatio = 0.35; + + final MoveTo moveTo = new MoveTo(); + moveTo.xProperty().bind(animation.multiply(circleToOctagonLineRatio * radius)); + moveTo.yProperty().set(radius); + + final CubicCurveTo cc1 = new CubicCurveTo(); + cc1.controlX1Property().bind(reverseAnimation.multiply(c * radius).add(animation.multiply(circleToOctagonLineRatio * radius))); + cc1.controlY1Property().bind(reverseAnimation.multiply(radius).add(animation.multiply(radius))); + cc1.controlX2Property().bind(reverseAnimation.multiply(radius).add(animation.multiply(radius))); + cc1.controlY2Property().bind(reverseAnimation.multiply(c * radius).add(animation.multiply(circleToOctagonLineRatio * radius))); + cc1.setX(radius); + cc1.yProperty().bind(animation.multiply(circleToOctagonLineRatio * radius)); + + + final LineTo lineTo1 = new LineTo(); + lineTo1.xProperty().bind(cc1.xProperty()); + lineTo1.yProperty().bind(cc1.yProperty().multiply(-1)); + + final CubicCurveTo cc2 = new CubicCurveTo(); + cc2.controlX1Property().bind(cc1.controlX2Property()); + cc2.controlY1Property().bind(cc1.controlY2Property().multiply(-1)); + cc2.controlX2Property().bind(cc1.controlX1Property()); + cc2.controlY2Property().bind(cc1.controlY1Property().multiply(-1)); + cc2.xProperty().bind(moveTo.xProperty()); + cc2.yProperty().bind(moveTo.yProperty().multiply(-1)); + + + final LineTo lineTo2 = new LineTo(); + lineTo2.xProperty().bind(cc2.xProperty().multiply(-1)); + lineTo2.yProperty().bind(cc2.yProperty()); + + final CubicCurveTo cc3 = new CubicCurveTo(); + cc3.controlX1Property().bind(cc2.controlX2Property().multiply(-1)); + cc3.controlY1Property().bind(cc2.controlY2Property()); + cc3.controlX2Property().bind(cc2.controlX1Property().multiply(-1)); + cc3.controlY2Property().bind(cc2.controlY1Property()); + cc3.xProperty().bind(lineTo1.xProperty().multiply(-1)); + cc3.yProperty().bind(lineTo1.yProperty()); + + + final LineTo lineTo3 = new LineTo(); + lineTo3.xProperty().bind(cc3.xProperty()); + lineTo3.yProperty().bind(cc3.yProperty().multiply(-1)); + + final CubicCurveTo cc4 = new CubicCurveTo(); + cc4.controlX1Property().bind(cc3.controlX2Property()); + cc4.controlY1Property().bind(cc3.controlY2Property().multiply(-1)); + cc4.controlX2Property().bind(cc3.controlX1Property()); + cc4.controlY2Property().bind(cc3.controlY1Property().multiply(-1)); + cc4.xProperty().bind(lineTo2.xProperty()); + cc4.yProperty().bind(lineTo2.yProperty().multiply(-1)); + + + final LineTo lineTo4 = new LineTo(); + lineTo4.xProperty().bind(moveTo.xProperty()); + lineTo4.yProperty().bind(moveTo.yProperty()); + + + locationShape.getElements().add(moveTo); + locationShape.getElements().add(cc1); + + locationShape.getElements().add(lineTo1); + locationShape.getElements().add(cc2); + + locationShape.getElements().add(lineTo2); + locationShape.getElements().add(cc3); + + locationShape.getElements().add(lineTo3); + locationShape.getElements().add(cc4); + + locationShape.getElements().add(lineTo4); + } + + @Override + public void highlight() { + updateColorDelegates.forEach(colorConsumer -> colorConsumer.accept(SelectHelper.SELECT_COLOR, SelectHelper.SELECT_COLOR_INTENSITY_NORMAL)); + } + + @Override + public void unhighlight() { + updateColorDelegates.forEach(colorConsumer -> { + final Location location = controller.getLocation(); + + colorConsumer.accept(location.getColor(), location.getColorIntensity()); + }); + } +} \ No newline at end of file diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java new file mode 100755 index 00000000..56cc1b3f --- /dev/null +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -0,0 +1,155 @@ +package ecdar.controllers; + +import ecdar.Ecdar; +import ecdar.abstractions.*; +import ecdar.simulation.SimulationHandler; +import ecdar.presentations.LeftSimPanePresentation; +import ecdar.presentations.RightSimPanePresentation; +import ecdar.presentations.SimulatorOverviewPresentation; +import ecdar.simulation.SimulationState; +import ecdar.simulation.Transition; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.fxml.Initializable; +import javafx.scene.control.Label; +import javafx.scene.layout.StackPane; +import javafx.scene.shape.Rectangle; + +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.ResourceBundle; + +public class SimulatorController implements Initializable { + + public StackPane root; + public SimulatorOverviewPresentation overviewPresentation; + public StackPane toolbar; + public Label rightPaneFillerElement; + public Label leftPaneFillerElement; + public Rectangle bottomFillerElement; + public RightSimPanePresentation rightSimPane; + public LeftSimPanePresentation leftSimPane; + private Declarations systemDeclarations; + private boolean firstTimeInSimulator; + + private final static DoubleProperty width = new SimpleDoubleProperty(), + height = new SimpleDoubleProperty(); + private static ObjectProperty selectedTransition = new SimpleObjectProperty<>(); + private static ObjectProperty selectedState = new SimpleObjectProperty<>(); + + @Override + public void initialize(URL location, ResourceBundle resources) { + root.widthProperty().addListener((observable, oldValue, newValue) -> width.setValue(newValue)); + root.heightProperty().addListener((observable, oldValue, newValue) -> height.setValue(newValue)); + firstTimeInSimulator = true; + } + + /** + * Prepares the simulator to be shown.
+ * It also prepares the processes to be shown in the {@link SimulatorOverviewPresentation} by:
+ * - Building the system if it has been updated or never have been created.
+ * - Adding the components which are going to be used in the simulation to + */ + public void willShow() { + final SimulationHandler sm = Ecdar.getSimulationHandler(); + boolean shouldSimulationBeReset = true; + + //Have the user left a trace or is he simulating a query + if (sm.traceLog.size() >= 2 || sm.getCurrentSimulation().contains(SimulationHandler.QUERY_PREFIX)) { + shouldSimulationBeReset = false; + } + + if (!firstTimeInSimulator && !overviewPresentation.getController().getComponentObservableList() + .containsAll(findComponentsInCurrentSimulation())) { + shouldSimulationBeReset = true; + } + + if (shouldSimulationBeReset || firstTimeInSimulator) { + resetSimulation(); + sm.resetToInitialLocation(); + } + overviewPresentation.getController().addProcessesToGroup(); + overviewPresentation.getController().highlightProcessState(sm.getCurrentState()); + } + + /** + * Resets the current simulation, and prepares for a new simulation by clearing the + * {@link SimulatorOverviewController#processContainer} and adding the processes of the new simulation. + */ + private void resetSimulation() { + final SimulationHandler sm = Ecdar.getSimulationHandler(); + sm.initializeDefaultSystem(); + overviewPresentation.getController().clearOverview(); + overviewPresentation.getController().getComponentObservableList().clear(); + overviewPresentation.getController().getComponentObservableList().addAll(findComponentsInCurrentSimulation()); + firstTimeInSimulator = false; + } + + /** + * Finds the components that are used in the current simulation by looking at the component found in + * {@link Project#getComponents()} and compare them to the processes declared in the {@link SimulationHandler#getSystem()} + *

+ * TODO This does currently not work if the same component is used multiple times. + * + * @return all the components used in the current simulation + */ + private List findComponentsInCurrentSimulation() { + //Show components from the system + final SimulationHandler sm = Ecdar.getSimulationHandler(); + List components = new ArrayList<>(); +// for (int i = 0; i < sm.getSystem().getNoOfProcesses(); i++) { +// final int finalI = i; // when using a var in lambda it has to be final +// final List filteredList = Ecdar.getProject().getComponents().filtered(component -> { +// return component.getName().contentEquals(sm.getSystem().getProcess(finalI).getName()); +// }); +// components.addAll(filteredList); +// } + return components; + } + + /** + * Resets the simulation and prepares the view for showing the new simulation to the user + */ + public void resetCurrentSimulation() { + overviewPresentation.getController().removeProcessesFromGroup(); + resetSimulation(); + Ecdar.getSimulationHandler().resetToInitialLocation(); + overviewPresentation.getController().addProcessesToGroup(); + } + + public void willHide() { + overviewPresentation.getController().removeProcessesFromGroup(); + overviewPresentation.getController().getComponentObservableList().forEach(component -> { + // Previously reset coordinates of component box + }); + overviewPresentation.getController().unhighlightProcesses(); + } + + public static DoubleProperty getWidthProperty() { + return width; + } + + public static DoubleProperty getHeightProperty() { + return height; + } + + + public static ObjectProperty getSelectedTransitionProperty() { + return selectedTransition; + } + + public static void setSelectedTransition(Transition selectedTransition) { + SimulatorController.selectedTransition.set(selectedTransition); + } + + public static ObjectProperty getSelectedStateProperty() { + return selectedState; + } + + public static void setSelectedState(SimulationState selectedState) { + SimulatorController.selectedState.set(selectedState); + } +} diff --git a/src/main/java/ecdar/controllers/SimulatorOverviewController.java b/src/main/java/ecdar/controllers/SimulatorOverviewController.java new file mode 100755 index 00000000..160e9e51 --- /dev/null +++ b/src/main/java/ecdar/controllers/SimulatorOverviewController.java @@ -0,0 +1,372 @@ +package ecdar.controllers; + +import ecdar.Ecdar; +import ecdar.abstractions.*; +import ecdar.presentations.ProcessPresentation; +import ecdar.simulation.SimulationState; +import ecdar.simulation.Transition; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; +import javafx.beans.InvalidationListener; +import javafx.collections.*; +import javafx.fxml.Initializable; +import javafx.geometry.Insets; +import javafx.scene.Group; +import javafx.scene.control.ScrollPane; +import javafx.scene.layout.*; + +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; + +/** + * The controller of the middle part of the simulator. + * It is here where processes of a simulation will be shown. + */ +public class SimulatorOverviewController implements Initializable { + public AnchorPane root; + public ScrollPane scrollPane; + public FlowPane processContainer; + public Group groupContainer; + + /** + * The amount that is going be zoomed in/out for each press on + or - + */ + private final double SCALE_DELTA = 1.1; + + /** + * The max that the user can zoom in + */ + private static final double MAX_ZOOM_IN = 1.6; + + /** + * The max that the user can zoom in + */ + private static final double MAX_ZOOM_OUT = 0.5; + + /** + * Offset such that the view does not overlap with the scroll bar on the right hand sig. + */ + private static final int SUPER_SPECIAL_SCROLLPANE_OFFSET = 20; + + private final ObservableList componentArrayList = FXCollections.observableArrayList(); + private final ObservableMap processPresentations = FXCollections.observableHashMap(); + + /** + * Is true if a reset of the zoom have been requested, false if not. + */ + private boolean resetZoom = false; + private boolean isMaxZoomInReached = false; + private boolean isMaxZoomOutReached = false; + + @Override + public void initialize(final URL location, final ResourceBundle resources) { + groupContainer = new Group(); + processContainer = new FlowPane(); + //In case that the processContainer gets moved around we have to keep in into place. + initializeProcessContainer(); + + initializeWindowResizing(); + initializeZoom(); + initializeHighlighting(); + initializeSimulationVariables(); + // Add the processes and group to the view + addProcessesToGroup(); + scrollPane.setContent(groupContainer); + } + + /** + * Initializes the {@link #processContainer} with its correct styling, and placement on the view. + * It also adds a {@link ListChangeListener} on {@link #componentArrayList} where it adds the + * {@link Component}s which are needed to the processContainer. + */ + private void initializeProcessContainer() { + processContainer.translateXProperty().addListener((observable, oldValue, newValue)-> { + processContainer.setTranslateX(0); + }); + //Sets the space between the processes + processContainer.setHgap(10); + processContainer.setVgap(10); + + // padding to the scrollpane + processContainer.setPadding(new Insets(5)); + + componentArrayList.addListener((ListChangeListener) c -> { + final Map processes = new HashMap<>(); + while (c.next()){ + if (c.wasRemoved()) { + clearOverview(); + } else { + c.getAddedSubList().forEach(o -> processes.put(o.getName(), new ProcessPresentation(o))); + } + } + // Highlight the current state when the processes change + highlightProcessState(Ecdar.getSimulationHandler().getCurrentState()); + processContainer.getChildren().addAll(processes.values()); + processPresentations.putAll(processes); + }); + } + + /** + * Clears the {@link #processContainer} and the {@link #processPresentations}. + */ + void clearOverview() { + processContainer.getChildren().clear(); + processPresentations.clear(); + } + + /** + * Setup listeners for displaying clock and variable values on the {@link ProcessPresentation} + */ + private void initializeSimulationVariables() { + Ecdar.getSimulationHandler().getSimulationVariables().addListener((InvalidationListener) obs -> { + Ecdar.getSimulationHandler().getSimulationVariables().forEach((s, bigDecimal) -> { + if (!s.equals("t(0)")) {// As t(0) does not belong to any process + final String[] spittedString = s.split("\\."); + // If the process containing the var is not there we just skip it + if (spittedString.length > 0 && processPresentations.size() > 0) { + processPresentations.get(spittedString[0]).getController().getVariables().put(spittedString[1], bigDecimal); + } + } + }); + }); + Ecdar.getSimulationHandler().getSimulationClocks().addListener((InvalidationListener) obs -> { + if(processPresentations.size() == 0) return; + Ecdar.getSimulationHandler().getSimulationClocks().forEach((s, bigDecimal) -> { + if (!s.equals("t(0)")) {// As t(0) does not belong to any process + final String[] spittedString = s.split("\\."); + // If the process containing the clock is not there we just skip it + if (spittedString.length > 0 && processPresentations.size() > 0) { + processPresentations.get(spittedString[0]).getController().getClocks().put(spittedString[1], bigDecimal); + } + } + }); + }); + } + + /** + * Removes {@link #processContainer} from the {@link #groupContainer}.
+ * In this way the {@link Component}s in the processContainer will then again be resizable, + * as the class {@link Group} makes its children not resizeable. + * @see Group + */ + void removeProcessesFromGroup(){ + groupContainer.getChildren().removeAll(processContainer); + } + + /** + * Adds the {@link #processContainer} to the {@link #groupContainer}.
+ * This method is usually needed to called if {@link #removeProcessesFromGroup()} have been called, or + * if the processContainer just need to be added to the groupContainer.
+ * This method makes sure that the processContainer will be added to the groupContainer + * which is needed to show the {@link ProcessPresentation}s in the {@link #scrollPane}. + * If the processContainer is already contained in the groupContainer + * the method does nothing but return. + * @see #removeProcessesFromGroup() + */ + void addProcessesToGroup(){ + if(groupContainer.getChildren().contains(processContainer)) return; + groupContainer.getChildren().add(processContainer); + } + + /** + * Initializes the zoom functionality in {@link #processContainer} + */ + private void initializeZoom() { + processContainer.scaleXProperty().addListener((observable, oldValue, newValue) -> { + if(newValue.doubleValue() > MAX_ZOOM_IN) isMaxZoomInReached = true; + if(newValue.doubleValue() < MAX_ZOOM_OUT) isMaxZoomOutReached = true; + + handleWidthOnScale(oldValue, newValue); + }); + + // to support pinch zooming + //TODO this should be fixed at as it does not work as it should + /* + processContainer.setOnZoom(event -> { + //Tries to zoom in/out but max is reached + if(event.getZoomFactor() >= 1 && isMaxZoomInReached) return; + if(event.getZoomFactor() < 1 && isMaxZoomOutReached) return; + + isMaxZoomInReached = false; + isMaxZoomOutReached = false; + + processContainer.setScaleX(processContainer.getScaleX() * event.getZoomFactor()); + processContainer.setScaleY(processContainer.getScaleY() * event.getZoomFactor()); + });*/ + } + + /** + * Initializes listener for change of width in {@link #scrollPane} which also affects {@link #processContainer}
+ * This does also take the zooming into account when doing the resizing. + */ + private void initializeWindowResizing() { + scrollPane.widthProperty().addListener((observable, oldValue, newValue) -> { + final double width = (newValue.doubleValue()) * (1 + (1 - processContainer.getScaleX())); + if(processContainer.getScaleX() > 1) { //Zoomed in + processContainer.setMinWidth(width); + processContainer.setMaxWidth(width); + } else if (processContainer.getScaleX() < 1) { //Zoomed out + processContainer.setMinWidth(width); + processContainer.setMaxWidth(width); + final double deltaWidth = newValue.doubleValue() - groupContainer.layoutBoundsProperty().get().getWidth(); + processContainer.setMinWidth(processContainer.getWidth() + (deltaWidth - SUPER_SPECIAL_SCROLLPANE_OFFSET) * (1 + (1 - processContainer.getScaleX()))); + processContainer.setMaxWidth(processContainer.getWidth() + (deltaWidth - SUPER_SPECIAL_SCROLLPANE_OFFSET) * (1 + (1 - processContainer.getScaleX()))); + } else { // Reset + processContainer.setMinWidth(newValue.doubleValue() - SUPER_SPECIAL_SCROLLPANE_OFFSET); + processContainer.setMaxWidth(newValue.doubleValue() - SUPER_SPECIAL_SCROLLPANE_OFFSET); + } + }); + } + + /** + * Increments the {@link #processContainer} scaleX and scaleY properties + * which creates the zoom-in feeling. Resizing of the view is handled by {@link #handleWidthOnScale(Number, Number)} + * @see FlowPane#scaleXProperty() + * @see FlowPane#scaleYProperty() + */ + void zoomIn() { + if (isMaxZoomInReached) return; + isMaxZoomOutReached = false; + processContainer.setScaleX(processContainer.getScaleX() * SCALE_DELTA); + processContainer.setScaleY(processContainer.getScaleY() * SCALE_DELTA); + } + + + /** + * Decrements the {@link #processContainer} scaleX and scaleY properties + * which creates the zoom-in feeling. Resizing of the view is handled by {@link #handleWidthOnScale(Number, Number)} + * @see FlowPane#scaleXProperty() + * @see FlowPane#scaleYProperty() + */ + void zoomOut() { + if(isMaxZoomOutReached) return; + isMaxZoomInReached = false; + processContainer.setScaleX(processContainer.getScaleX() * (1 / SCALE_DELTA)); + processContainer.setScaleY(processContainer.getScaleY() * (1 / SCALE_DELTA)); + } + + /** + * Resets the scaling of the {@link #processContainer}, and hereby the zoom + * @see FlowPane#scaleXProperty() + * @see FlowPane#scaleYProperty() + */ + void resetZoom() { + if(processContainer.getScaleX() == 1) return; + resetZoom = true; + isMaxZoomInReached = false; + isMaxZoomOutReached = false; + processContainer.setScaleX(1); + processContainer.setScaleY(1); + } + + /** + * Handles the scaling of the width of the {@link #processContainer} + * @param oldValue the width of {@link #scrollPane} before the change + * @param newValue the width of {@link #scrollPane} after the change + */ + private void handleWidthOnScale(final Number oldValue, final Number newValue) { + if(resetZoom) { //Zoom reset + resetZoom = false; + processContainer.setMinWidth(scrollPane.getWidth() - SUPER_SPECIAL_SCROLLPANE_OFFSET); + processContainer.setMaxWidth(scrollPane.getWidth() - SUPER_SPECIAL_SCROLLPANE_OFFSET); + } else if(oldValue.doubleValue() > newValue.doubleValue()) { //Zoom in + resetZoom = false; + processContainer.setMinWidth(Math.round(processContainer.getWidth() * SCALE_DELTA)); + processContainer.setMaxWidth(Math.round(processContainer.getWidth() * SCALE_DELTA)); + } else { // Zoom out + resetZoom = false; + processContainer.setMinWidth(Math.round(processContainer.getWidth() * (1 / SCALE_DELTA))); + processContainer.setMaxWidth(Math.round(processContainer.getWidth() * (1 / SCALE_DELTA))); + } + } + + /** + * Initializer method to setup listeners that handle highlighting when selected/current state/transition changes + */ + private void initializeHighlighting() { + SimulatorController.getSelectedTransitionProperty().addListener((observable, oldTransition, newTransition) -> { + unhighlightProcesses(); + + // If the new transition is not null, we want to highlight the locations and edges in the new value + // otherwise we highlight the current state + if(newTransition != null) { + highlightProcessTransition(newTransition); + } else { + highlightProcessState(Ecdar.getSimulationHandler().getCurrentState()); + } + }); + + SimulatorController.getSelectedStateProperty().addListener((observable, oldState, newState) -> { + unhighlightProcesses(); + + // If the new state is not null, we want to highlight the locations in the new value + // otherwise we highlight the current state + if(newState != null) { + highlightProcessState(newState); + } else { + highlightProcessState(Ecdar.getSimulationHandler().getCurrentState()); + } + }); + } + + /** + * Highlights all the processes involved in the transition. + * Finds the processes involved in the transition (processes with edges in the transition) and highlights their edges + * Also fades processes that are not active in the selected transition + * @param transition The transition for which we highlight the involved processes + */ + public void highlightProcessTransition(final Transition transition) { + final var edges = transition.getEdges(); + + // List of all processes to show as inactive if they are not involved in a transition + // Processes are removed from this list, if they have an edge in the transition + final ArrayList processesToHide = new ArrayList<>(processPresentations.values()); + + for (final Edge edge : edges) { + final Process process = edge.getProcess(); + + // Find the processes that have edges involved in this transition + final ProcessPresentation presentation = processPresentations.get(process.getName()); + presentation.getController().highlightEdges(edges); + processesToHide.remove(presentation); + } + + processesToHide.forEach(ProcessPresentation::showInactive); + } + + /** + * Unhighlights all processes + */ + public void unhighlightProcesses() { + for(final ProcessPresentation presentation: processPresentations.values()) { + presentation.getController().unhighlightProcess(); + presentation.showActive(); + } + } + + /** + * Finds the processes for the input locations in the input {@link SimulationState} and highlights the locations. + * @param state The state with the locations to highlight + */ + public void highlightProcessState(final SimulationState state) { + for (int i = 0; i < state.getLocations().size(); i++) { + final Location loc = state.getLocations().get(i); + + for(final ProcessPresentation presentation: processPresentations.values()) { + final String processName = presentation.getController().getComponent().getName(); + +// if(processName.equals(loc.getProcess().getName())) { +// presentation.getController().highlightLocation(loc); +// } + } + } + } + + public ObservableList getComponentObservableList() { + return componentArrayList; + } +} diff --git a/src/main/java/ecdar/controllers/TracePaneElementController.java b/src/main/java/ecdar/controllers/TracePaneElementController.java new file mode 100755 index 00000000..6f0bf4ec --- /dev/null +++ b/src/main/java/ecdar/controllers/TracePaneElementController.java @@ -0,0 +1,182 @@ +package ecdar.controllers; + +import com.jfoenix.controls.JFXRippler; +import ecdar.Ecdar; +import ecdar.abstractions.Location; +import ecdar.simulation.SimulationState; +import ecdar.simulation.SimulationHandler; +import ecdar.presentations.TransitionPresentation; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.collections.ListChangeListener; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Label; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; +import org.kordamp.ikonli.javafx.FontIcon; + +import java.net.URL; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.ResourceBundle; + +/** + * The controller class for the trace pane element that can be inserted into a simulator pane + */ +public class TracePaneElementController implements Initializable { + public AnchorPane toolbar; + public Label traceTitle; + public JFXRippler expandTrace; + public VBox traceList; + public VBox traceVbox; + public FontIcon expandTraceIcon; + public AnchorPane traceSummary; + public Label summaryTitleLabel; + public Label summarySubtitleLabel; + + private SimpleBooleanProperty isTraceExpanded = new SimpleBooleanProperty(false); + private Map transitionPresentationMap = new LinkedHashMap<>(); + private SimpleIntegerProperty numberOfSteps = new SimpleIntegerProperty(0); + + @Override + public void initialize(URL location, ResourceBundle resources) { + Ecdar.getSimulationHandler().getTraceLog().addListener((ListChangeListener) c -> { + while (c.next()) { + for(final SimulationState state: c.getAddedSubList()) { + insertTraceState(state, true); + } + + for(final SimulationState state: c.getRemoved()) { + traceList.getChildren().remove(transitionPresentationMap.get(state)); + transitionPresentationMap.remove(state); + } + } + + numberOfSteps.set(transitionPresentationMap.size()); + }); + + initializeTraceExpand(); + } + + /** + * Initializes the expand functionality that allows the user to show or hide the trace. + * By default the trace is shown. + */ + private void initializeTraceExpand() { + isTraceExpanded.addListener((obs, oldVal, newVal) -> { + if(newVal) { + showTrace(); + expandTraceIcon.setIconLiteral("gmi-expand-less"); + expandTraceIcon.setIconSize(24); + } else { + hideTrace(); + expandTraceIcon.setIconLiteral("gmi-expand-more"); + expandTraceIcon.setIconSize(24); + } + }); + + isTraceExpanded.set(true); + } + + + /** + * Removes all the trace view elements as to hide the trace from the user + * Also shows the summary view when the trace is hidden + */ + private void hideTrace() { + traceList.getChildren().clear(); + traceVbox.getChildren().add(traceSummary); + } + + /** + * Shows the trace by inserting a {@link TransitionPresentation} for each trace state + * Also hides the summary view, since it should only be visible when the trace is hidden + */ + private void showTrace() { + transitionPresentationMap.forEach((state, presentation) -> { + insertTraceState(state, false); + }); + traceVbox.getChildren().remove(traceSummary); + } + + /** + * Instantiates a {@link TransitionPresentation} for a {@link SimulationState} and adds it to the view + * @param state The state the should be inserted into the trace log + * @param shouldAnimate A boolean that indicates whether the trace should fade in when added to the view + */ + private void insertTraceState(final SimulationState state, final boolean shouldAnimate) { + final TransitionPresentation transitionPresentation = new TransitionPresentation(); + transitionPresentationMap.put(state, transitionPresentation); + + transitionPresentation.setOnMouseReleased(event -> { + event.consume(); + final SimulationHandler simHandler = Ecdar.getSimulationHandler(); + if (simHandler == null) return; + Ecdar.getSimulationHandler().selectTransitionFromLog(state); + }); + + EventHandler mouseEntered = transitionPresentation.getOnMouseEntered(); + transitionPresentation.setOnMouseEntered(event -> { + SimulatorController.setSelectedState(state); + mouseEntered.handle(event); + }); + + EventHandler mouseExited = transitionPresentation.getOnMouseExited(); + transitionPresentation.setOnMouseExited(event -> { + SimulatorController.setSelectedState(null); + mouseExited.handle(event); + }); + + + String title = traceString(state); + transitionPresentation.getController().setTitle(title); + + // Only insert the presentation into the view if the trace is expanded + if(isTraceExpanded.get()) { + traceList.getChildren().add(transitionPresentation); + if(shouldAnimate) { + transitionPresentation.playFadeAnimation(); + } + } + } + + /** + * A helper method that returns a string representing a state in the trace log + * @param state The SimulationState to represent + * @return A string representing the state + */ + private String traceString(SimulationState state) { + String title = "("; + int length = state.getLocations().size(); + for (int i = 0; i < length ; i++) { + Location loc = state.getLocations().get(i); + String locationName = loc.getNickname(); + if (i == length-1) { + title += locationName; + } else { + title += locationName + ", "; + } + } + title += ")"; + + return title; + } + + /** + * Method to be called when clicking on the expand rippler in the trace toolbar + */ + @FXML + private void expandTrace() { + if(isTraceExpanded.get()) { + isTraceExpanded.set(false); + } else { + isTraceExpanded.set(true); + } + } + + public SimpleIntegerProperty getNumberOfStepsProperty() { + return numberOfSteps; + } +} diff --git a/src/main/java/ecdar/controllers/TransitionController.java b/src/main/java/ecdar/controllers/TransitionController.java new file mode 100755 index 00000000..7bacda2e --- /dev/null +++ b/src/main/java/ecdar/controllers/TransitionController.java @@ -0,0 +1,45 @@ +package ecdar.controllers; + +import com.jfoenix.controls.JFXRippler; +import ecdar.simulation.Transition; +import javafx.beans.property.SimpleObjectProperty; +import javafx.fxml.Initializable; +import javafx.scene.control.Label; +import javafx.scene.layout.AnchorPane; + +import java.net.URL; +import java.util.ResourceBundle; + +/** + * The controller class for the transition view element. + * It represents a single transition and may be used by classes like {@see TransitionPaneElementController} + * to show a list of transitions + */ +public class TransitionController implements Initializable { + public AnchorPane root; + public Label titleLabel; + public JFXRippler rippler; + + // The transition that the view represents + private SimpleObjectProperty transition = new SimpleObjectProperty<>(); + private SimpleObjectProperty title = new SimpleObjectProperty<>(); + + @Override + public void initialize(URL location, ResourceBundle resources) { + title.addListener(((observable, oldValue, newValue) -> { + titleLabel.setText(newValue); + })); + } + + public void setTitle(String title) { + this.title.set(title); + } + + public void setTransition(Transition transition) { + this.transition.set(transition); + } + + public Transition getTransition() { + return transition.get(); + } +} diff --git a/src/main/java/ecdar/controllers/TransitionPaneElementController.java b/src/main/java/ecdar/controllers/TransitionPaneElementController.java new file mode 100755 index 00000000..5dc6dbde --- /dev/null +++ b/src/main/java/ecdar/controllers/TransitionPaneElementController.java @@ -0,0 +1,245 @@ +package ecdar.controllers; + +import com.jfoenix.controls.JFXRippler; +import com.jfoenix.controls.JFXTextField; +import ecdar.Ecdar; +import ecdar.abstractions.Edge; +import ecdar.simulation.Transition; +import ecdar.simulation.SimulationHandler; +import ecdar.presentations.TransitionPresentation; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.ListChangeListener; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Label; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; +import org.kordamp.ikonli.javafx.FontIcon; + +import java.math.BigDecimal; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; + +/** + * The controller class for the transition pane element that can be inserted into the simulator panes + */ +public class TransitionPaneElementController implements Initializable { + public AnchorPane root; + public VBox paneElementVbox; + public VBox transitionList; + public AnchorPane toolbar; + public Label toolbarTitle; + public JFXRippler refreshRippler; + public JFXRippler expandTransition; + public FontIcon expandTransitionIcon; + public AnchorPane delayChooser; + public JFXTextField delayTextField; + + private SimpleBooleanProperty isTransitionExpanded = new SimpleBooleanProperty(false); + private Map transitionPresentationMap = new HashMap<>(); + private SimpleObjectProperty delay = new SimpleObjectProperty<>(BigDecimal.ZERO); + + @Override + public void initialize(URL location, ResourceBundle resources) { + Ecdar.getSimulationHandler().availableTransitions.addListener((ListChangeListener) c -> { + while (c.next()) { + for (Transition trans : c.getAddedSubList()) { + insertTransition(trans); + } + + for (final Transition trans: c.getRemoved()) { + transitionList.getChildren().remove(transitionPresentationMap.get(trans)); + transitionPresentationMap.remove(trans); + } + } + }); + + initializeTransitionExpand(); + initializeDelayChooser(); + } + + /** + * Sets up listeners for the delay chooser. + * Listens for changes in text property and updates the textfield with a sanitized value (e.g. no letters in delay). + * Also listens for changes in focus, so there's always a value in the textfield, even if the user deleted the text. + * Adds tooltip for the textfield. + */ + private void initializeDelayChooser() { + delayTextField.textProperty().addListener(((observable, oldValue, newValue) -> { + delayTextChanged(oldValue, newValue); + })); + + delayTextField.focusedProperty().addListener((observable, oldValue, newValue) -> { + // If the textfield loses focus and the user didn't enter anything + // show the value 0.0 + if(!newValue && delay.get().equals(BigDecimal.ZERO)) { + delayTextField.setText("0.0"); + } + }); + + Tooltip.install(delayTextField, new Tooltip("Enter delay to use for next transition")); + } + + /** + * Initializes the expand functionality that allows the user to show or hide the transitions. + * By default the transitions are shown. + */ + private void initializeTransitionExpand() { + isTransitionExpanded.addListener((obs, oldVal, newVal) -> { + if(newVal) { + if(!paneElementVbox.getChildren().contains(delayChooser)) { + // Add the delay chooser just below the toolbar + paneElementVbox.getChildren().add(1, delayChooser); + } + showTransitions(); + expandTransitionIcon.setIconLiteral("gmi-expand-less"); + expandTransitionIcon.setIconSize(24); + } else { + paneElementVbox.getChildren().remove(delayChooser); + hideTransitions(); + expandTransitionIcon.setIconLiteral("gmi-expand-more"); + expandTransitionIcon.setIconSize(24); + } + }); + + isTransitionExpanded.set(true); + } + + /** + * Removes all the transition view elements as to hide the transitions from the user + */ + private void hideTransitions() { + transitionList.getChildren().clear(); + } + + /** + * Shows the available transitions by inserting a {@link TransitionPresentation} for each transition + */ + private void showTransitions() { + transitionPresentationMap.forEach((transition, presentation) -> { + insertTransition(transition); + }); + } + + /** + * Instantiates a TransitionPresentation for a Transition and adds it to the view + * @param transition The transition that should be inserted into the view + */ + private void insertTransition(Transition transition) { + final TransitionPresentation transitionPresentation = new TransitionPresentation(); + String title = transitionString(transition); + transitionPresentation.getController().setTitle(title); + transitionPresentation.getController().setTransition(transition); + + // Update the selected transition when mouse entered. + // Add the event to existing mouseEntered events + // e.g. TransitionPresentation already has mouseEntered functionality and we want to keep it + EventHandler mouseEntered = transitionPresentation.getOnMouseEntered(); + transitionPresentation.setOnMouseEntered(event -> { + SimulatorController.setSelectedTransition(transitionPresentation.getController().getTransition()); + mouseEntered.handle(event); + }); + + EventHandler mouseExited = transitionPresentation.getOnMouseExited(); + transitionPresentation.setOnMouseExited(event -> { + SimulatorController.setSelectedTransition(null); + mouseExited.handle(event); + }); + + transitionPresentation.setOnMouseClicked(event -> { + event.consume(); + + // Performs the next step of the simulation when clicking on a transition + SimulationHandler simHandler = Ecdar.getSimulationHandler(); + if (simHandler != null) { + simHandler.nextStep(transitionPresentation.getController().getTransition(), this.delay.get()); + } + }); + + transitionPresentationMap.put(transition, transitionPresentation); + + // Only insert the presentation into the view if the transitions are expanded + // Avoids inserting duplicate elements in the view (it's still added to the map) + if(isTransitionExpanded.get()) { + transitionList.getChildren().add(transitionPresentation); + } + } + + /** + * A helper method that returns a string representing a transition in the transition chooser + * @param transition The {@link Transition} to represent + * @return A string representing the transition + */ + private String transitionString(Transition transition) { + String title = transition.getLabel(); + if(transition.getEdges() != null) { + for (Edge edge : transition.getEdges()) { + title += " " + edge.getId(); + } + } + return title; + } + + /** + * Method to be called when clicking on the expand rippler in the transition toolbar + */ + @FXML + private void expandTransitions() { + if(isTransitionExpanded.get()) { + isTransitionExpanded.set(false); + } else { + isTransitionExpanded.set(true); + } + } + + /** + * Gets the initial step from the SimulationHandler. + * Used by the refresh button. + */ + @FXML + private void refreshTransitions() { + SimulatorController.setSelectedTransition(null); +// MainController.openReloadSimulationDialog(); // ToDo: Implement + } + + /** + * Sanitizes the input that the user inserts into the delay textfield. + * Checks if the text can be converted into a BigDecimal otherwise show the previous value. + * For example avoids users entering letters. + * @param oldValue The old value to show if the newvalue cannot be converted to a BigDecimal + * @param newValue The new value to show in the textfield + */ + @FXML + private void delayTextChanged(String oldValue, String newValue) { + // If the value is empty (the user deleted the value), assume that the value is 0.0 but do not update the text + if(newValue.isEmpty()) { + this.delay.set(BigDecimal.ZERO); + } else { + // Try to convert the new value into a BigDecimal + // Note that we don't setText here, as the new value is already shown in the textfield + try { + BigDecimal bd = new BigDecimal(newValue); + + // Checking the string for "-" instead of whether bd is negative is due to the case of -0.0 + // So checking the string is just simpler + if(newValue.contains("-")) { + throw new NumberFormatException(); + } + + this.delay.set(bd); + + } catch (NumberFormatException ex) { + // If the conversion was not possible, show the old value + this.delayTextField.setText(oldValue); + this.delay.set(new BigDecimal(oldValue)); + } + } + + } + +} diff --git a/src/main/java/ecdar/presentations/LeftSimPanePresentation.java b/src/main/java/ecdar/presentations/LeftSimPanePresentation.java new file mode 100755 index 00000000..adc693ed --- /dev/null +++ b/src/main/java/ecdar/presentations/LeftSimPanePresentation.java @@ -0,0 +1,37 @@ +package ecdar.presentations; + +import ecdar.controllers.LeftSimPaneController; +import ecdar.utility.colors.Color; +import javafx.geometry.Insets; +import javafx.scene.layout.*; + +public class LeftSimPanePresentation extends StackPane { + private LeftSimPaneController controller; + + public LeftSimPanePresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("LeftSimPanePresentation.fxml", this); + + initializeBackground(); + initializeRightBorder(); + } + + private void initializeBackground() { + controller.scrollPaneVbox.setBackground(new Background(new BackgroundFill( + Color.GREY.getColor(Color.Intensity.I200), + CornerRadii.EMPTY, + Insets.EMPTY + ))); + } + + /** + * Initializes the thin border on the right side of the transition toolbar + */ + private void initializeRightBorder() { + controller.transitionPanePresentation.getController().toolbar.setBorder(new Border(new BorderStroke( + Color.GREY_BLUE.getColor(Color.Intensity.I900), + BorderStrokeStyle.SOLID, + CornerRadii.EMPTY, + new BorderWidths(0, 1, 0, 0) + ))); + } +} diff --git a/src/main/java/ecdar/presentations/ProcessPresentation.java b/src/main/java/ecdar/presentations/ProcessPresentation.java new file mode 100755 index 00000000..51cebab0 --- /dev/null +++ b/src/main/java/ecdar/presentations/ProcessPresentation.java @@ -0,0 +1,282 @@ +package ecdar.presentations; + +import ecdar.abstractions.Component; +import ecdar.abstractions.Edge; +import ecdar.abstractions.Location; +import ecdar.abstractions.Nail; +import ecdar.controllers.ModelController; +import ecdar.controllers.ProcessController; +import ecdar.utility.colors.Color; +import javafx.beans.InvalidationListener; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.layout.*; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Path; +import javafx.scene.shape.Rectangle; +import javafx.scene.shape.Shape; +import javafx.scene.text.Font; + +import java.math.BigDecimal; +import java.util.List; +import java.util.function.BiConsumer; + +import static ecdar.presentations.Grid.GRID_SIZE; + +/** + * The presenter of a Process which is shown in {@link SimulatorOverviewPresentation}.
+ * This class have some of the same functionality as {@link ComponentPresentation} and could be refactored + * into a base class. + */ +public class ProcessPresentation extends ModelPresentation { + private final ProcessController controller; + + /** + * Constructs a Process ready to go to the view. + * @param component the component which the process should look like + */ + public ProcessPresentation(final Component component){ + controller = new EcdarFXMLLoader().loadAndGetController("ProcessPresentation.fxml", this); + controller.setComponent(component); + super.initialize(component.getBox()); + // Initialize methods that is sensitive to width and height + final Runnable onUpdateSize = () -> { + initializeToolbar(); + initializeFrame(); + initializeBackground(); + }; + + onUpdateSize.run(); + + // Re run initialisation on update of width and height property + component.getBox().getWidthProperty().addListener(observable -> onUpdateSize.run()); + component.getBox().getHeightProperty().addListener(observable -> onUpdateSize.run()); + setValueAreaStyle(); + setToggleValueButtonStyle(); + + controller.getClocks().forEach(this::addValueToValueArea); + controller.getVariables().forEach(this::addValueToValueArea); + + controller.getClocks().addListener((InvalidationListener) obs -> { + controller.getClocks().forEach((s, bigDecimal) -> { + final List filteredList = controller.valueArea.getChildren().filtered(node -> { + if (!(node instanceof Label))// we currently only want to look at labels + return false; + final String[] splitString = ((Label) node).getText().split("="); + return splitString[0].trim().equals(s); + }); + controller.valueArea.getChildren().removeAll(filteredList); + addValueToValueArea(s, bigDecimal); + }); + }); + controller.getVariables().addListener((InvalidationListener) obs -> { + controller.getVariables().forEach((s, bigDecimal) -> { + final List filteredList = controller.valueArea.getChildren().filtered(node -> { + if (!(node instanceof Label))// we currently only want to look at labels + return false; + final String[] splitString = ((Label) node).getText().split("="); + return splitString[0].trim().equals(s); + }); + controller.valueArea.getChildren().removeAll(filteredList); + addValueToValueArea(s, bigDecimal); + }); + }); + } + + /** + * Sets the Icon and Icon size of the {@link ProcessController#toggleValueButtonIcon} + */ + private void setToggleValueButtonStyle() { + controller.toggleValueButtonIcon.setIconLiteral("gmi-code"); + controller.toggleValueButtonIcon.setIconSize(17); + } + + /** + * Set the style needed for the {@link ProcessController#valueArea}. + * This include padding and background. + */ + private void setValueAreaStyle() { + // As for some reason the styling fail to be applied we need to set the styling again here + controller.valueArea.setPadding(new Insets(16, 4, 16, 4)); + controller.valueArea.setBackground(new Background( + new BackgroundFill(Paint.valueOf("rgba(242, 243, 244, 0.85)"),CornerRadii.EMPTY, Insets.EMPTY))); + } + + private void addValueToValueArea(final String s, final BigDecimal bigDecimal) { + final Label valueLabel = new Label(); + valueLabel.setText(s + " = " + bigDecimal); + // As the styling sometimes are gone missing + valueLabel.setFont(Font.font("Roboto Mono Medium",13)); + controller.valueArea.getChildren().add(valueLabel); + } + + /** + * Initializes the frame around the body of the process. + * It also updates the color if the color is changed + */ + private void initializeFrame() { + final Component component = controller.getComponent(); + + final Shape[] mask = new Shape[1]; + final Rectangle rectangle = new Rectangle(component.getBox().getWidth(), component.getBox().getHeight()); + + final BiConsumer updateColor = (newColor, newIntensity) -> { + // Mask the parent of the frame (will also mask the background) + mask[0] = Path.subtract(rectangle, TOP_LEFT_CORNER); + controller.frame.setClip(mask[0]); + controller.background.setClip(Path.union(mask[0], mask[0])); + controller.background.setOpacity(0.5); + + // Bind the missing lines that we cropped away + controller.topLeftLine.setStartX(Grid.CORNER_SIZE); + controller.topLeftLine.setStartY(0); + controller.topLeftLine.setEndX(0); + controller.topLeftLine.setEndY(Grid.CORNER_SIZE); + controller.topLeftLine.setStroke(newColor.getColor(newIntensity.next(2))); + controller.topLeftLine.setStrokeWidth(1.25); + StackPane.setAlignment(controller.topLeftLine, Pos.TOP_LEFT); + + // Set the stroke color to two shades darker + controller.frame.setBorder(new Border(new BorderStroke( + newColor.getColor(newIntensity.next(2)), + BorderStrokeStyle.SOLID, + CornerRadii.EMPTY, + new BorderWidths(1), + Insets.EMPTY + ))); + }; + component.colorProperty().addListener(observable -> { + updateColor.accept(component.getColor(), component.getColorIntensity()); + }); + updateColor.accept(component.getColor(), component.getColorIntensity()); + } + + /** + * Initializes the background of the process with the right color + * If the color is changed it will also update it self + */ + private void initializeBackground() { + final Component component = controller.getComponent(); + + // Bind the background width and height to the values in the model + controller.background.widthProperty().bind(component.getBox().getWidthProperty()); + controller.background.heightProperty().bind(component.getBox().getHeightProperty()); + controller.background.setFill(component.getColor().getColor(component.getColorIntensity().next(-10).next(2))); + final BiConsumer updateColor = (newColor, newIntensity) -> { + // Set the background color to the lightest possible version of the color + controller.background.setFill(newColor.getColor(newIntensity.next(-10).next(2))); + }; + component.colorProperty().addListener(observable -> { + updateColor.accept(component.getColor(), component.getColorIntensity()); + }); + + updateColor.accept(component.getColor(), component.getColorIntensity()); + } + + /** + * Initialize the Toolbar of the process.
+ * The toolbar is where the {@link ProcessController#name} is placed. + */ + private void initializeToolbar() { + final Component component = controller.getComponent(); + + final BiConsumer updateColor = (newColor, newIntensity) -> { + // Set the background of the toolbar + controller.toolbar.setBackground(new Background(new BackgroundFill( + newColor.getColor(newIntensity), + CornerRadii.EMPTY, + Insets.EMPTY + ))); + + // Set the icon color and rippler color of the toggleDeclarationButton + controller.toggleValuesButton.setRipplerFill(newColor.getTextColor(newIntensity)); + + controller.toolbar.setPrefHeight(Grid.TOOL_BAR_HEIGHT); + controller.toggleValuesButton.setBackground(Background.EMPTY); + }; + controller.getComponent().colorProperty().addListener(observable -> updateColor.accept(component.getColor(), component.getColorIntensity())); + + updateColor.accept(component.getColor(), component.getColorIntensity()); + + // Set a hover effect for the controller.toggleDeclarationButton + controller.toggleValuesButton.setOnMouseEntered(event -> controller.toggleValuesButton.setCursor(Cursor.HAND)); + controller.toggleValuesButton.setOnMouseExited(event -> controller.toggleValuesButton.setCursor(Cursor.DEFAULT)); + } + + /** + * Fades the process. + * Used if it is not involved in a transition + */ + public void showInactive() { + setOpacity(0.5); + } + + /** + * Show the process as active. + * Used to reset the effect of {@link ProcessPresentation#showInactive()} + */ + public void showActive() { + setOpacity(1.0); + } + + @Override + ModelController getModelController() { + return controller; + } + + /** + * Gets the minimum possible width when dragging the anchor. + * The width is based on the x coordinate of locations, nails and the signature arrows.
+ * This should be removed from {@link ModelPresentation} and made into an interface of its own + * @return the minimum possible width. + */ + @Override + @Deprecated + double getDragAnchorMinWidth() { + final Component component = controller.getComponent(); + double minWidth = 10 * GRID_SIZE; + + for (final Location location : component.getLocations()) { + minWidth = Math.max(minWidth, location.getX() + GRID_SIZE * 2); + } + + for (final Edge edge : component.getEdges()) { + for (final Nail nail : edge.getNails()) { + minWidth = Math.max(minWidth, nail.getX() + GRID_SIZE); + } + } + return minWidth; + } + + /** + * Gets the minimum possible height when dragging the anchor. + * The height is based on the y coordinate of locations, nails and the signature arrows
+ * This should be removed from {@link ModelPresentation} and made into an interface of its own + * @return the minimum possible height. + */ + @Override + @Deprecated + double getDragAnchorMinHeight() { + final Component component = controller.getComponent(); + double minHeight = 10 * GRID_SIZE; + + for (final Location location : component.getLocations()) { + minHeight = Math.max(minHeight, location.getY() + GRID_SIZE * 2); + } + + for (final Edge edge : component.getEdges()) { + for (final Nail nail : edge.getNails()) { + minHeight = Math.max(minHeight, nail.getY() + GRID_SIZE); + } + } + + return minHeight; + } + + public ProcessController getController() { + return controller; + } +} diff --git a/src/main/java/ecdar/presentations/RightSimPanePresentation.java b/src/main/java/ecdar/presentations/RightSimPanePresentation.java new file mode 100755 index 00000000..729b8829 --- /dev/null +++ b/src/main/java/ecdar/presentations/RightSimPanePresentation.java @@ -0,0 +1,45 @@ +package ecdar.presentations; + +import ecdar.controllers.RightSimPaneController; +import ecdar.utility.colors.Color; +import ecdar.utility.helpers.DropShadowHelper; +import javafx.geometry.Insets; +import javafx.scene.layout.*; + +/** + * Presentation class for the right pane in the simulator + */ +public class RightSimPanePresentation extends StackPane { + private RightSimPaneController controller; + + public RightSimPanePresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("RightSimPanePresentation.fxml", this); + + initializeBackground(); + initializeLeftBorder(); + } + + /** + * Sets the background color of the ScrollPane Vbox + */ + private void initializeBackground() { + controller.scrollPaneVbox.setBackground(new Background(new BackgroundFill( + Color.GREY.getColor(Color.Intensity.I200), + CornerRadii.EMPTY, + Insets.EMPTY + ))); + } + + /** + * Initializes the thin border on the left side of the querypane toolbar + */ + private void initializeLeftBorder() { + controller.queryPaneElement.getController().toolbar.setBorder(new Border(new BorderStroke( + Color.GREY_BLUE.getColor(Color.Intensity.I900), + BorderStrokeStyle.SOLID, + CornerRadii.EMPTY, + new BorderWidths(0, 0, 0, 1) + ))); + } + +} diff --git a/src/main/java/ecdar/presentations/SimulatorOverviewPresentation.java b/src/main/java/ecdar/presentations/SimulatorOverviewPresentation.java new file mode 100755 index 00000000..15a34e54 --- /dev/null +++ b/src/main/java/ecdar/presentations/SimulatorOverviewPresentation.java @@ -0,0 +1,20 @@ +package ecdar.presentations; + +import ecdar.controllers.SimulatorOverviewController; +import javafx.scene.layout.AnchorPane; + +/** + * The presenter of the middle part of the simulator. + * It is here where processes of a simulation will be shown. + */ +public class SimulatorOverviewPresentation extends AnchorPane { + private final SimulatorOverviewController controller; + + public SimulatorOverviewPresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("SimulatorOverviewPresentation.fxml", this); + } + + public SimulatorOverviewController getController() { + return controller; + } +} diff --git a/src/main/java/ecdar/presentations/SimulatorPresentation.java b/src/main/java/ecdar/presentations/SimulatorPresentation.java new file mode 100755 index 00000000..2d1a5105 --- /dev/null +++ b/src/main/java/ecdar/presentations/SimulatorPresentation.java @@ -0,0 +1,21 @@ +package ecdar.presentations; + +import ecdar.controllers.SimulatorController; +import javafx.scene.layout.StackPane; + +public class SimulatorPresentation extends StackPane { + private final SimulatorController controller; + + public SimulatorPresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("SimulatorPresentation.fxml", this); + } + + + /** + * The way to get the associated/linked controller of this presenter + * @return the controller linked to this presenter + */ + public SimulatorController getController() { + return controller; + } +} diff --git a/src/main/java/ecdar/presentations/TracePaneElementPresentation.java b/src/main/java/ecdar/presentations/TracePaneElementPresentation.java new file mode 100755 index 00000000..4e9a15b3 --- /dev/null +++ b/src/main/java/ecdar/presentations/TracePaneElementPresentation.java @@ -0,0 +1,96 @@ +package ecdar.presentations; + +import com.jfoenix.controls.JFXRippler; +import ecdar.controllers.TracePaneElementController; +import ecdar.utility.colors.Color; +import javafx.geometry.Insets; +import javafx.scene.Cursor; +import javafx.scene.layout.*; + +import java.util.function.BiConsumer; + +/** + * The presentation class for the trace element that can be inserted into the simulator panes + */ +public class TracePaneElementPresentation extends AnchorPane { + final private TracePaneElementController controller; + + public TracePaneElementPresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("TracePaneElementPresentation.fxml", this); + + initializeToolbar(); + initializeSummaryView(); + } + + /** + * Initializes the tool bar that contains the trace pane element's title and buttons + * Sets the color of the bar and title label. Also sets the look of the rippler effect + */ + private void initializeToolbar() { + final Color color = Color.GREY_BLUE; + final Color.Intensity colorIntensity = Color.Intensity.I800; + + controller.toolbar.setBackground(new Background(new BackgroundFill( + color.getColor(colorIntensity), + CornerRadii.EMPTY, + Insets.EMPTY))); + controller.traceTitle.setTextFill(color.getTextColor(colorIntensity)); + + controller.expandTrace.setMaskType(JFXRippler.RipplerMask.CIRCLE); + controller.expandTrace.setRipplerFill(color.getTextColor(colorIntensity)); + } + + /** + * Initializes the summary view so it is update when steps are taken in the trace. + * Also changes the color and cursor when mouse enters and exits the summary view. + */ + private void initializeSummaryView() { + controller.getNumberOfStepsProperty().addListener( + (observable, oldValue, newValue) -> updateSummaryTitle(newValue.intValue())); + + final Color color = Color.GREY_BLUE; + final Color.Intensity colorIntensity = Color.Intensity.I50; + + final BiConsumer setBackground = (newColor, newIntensity) -> { + controller.traceSummary.setBackground(new Background(new BackgroundFill( + newColor.getColor(newIntensity), + CornerRadii.EMPTY, + Insets.EMPTY + ))); + + controller.traceSummary.setBorder(new Border(new BorderStroke( + newColor.getColor(newIntensity.next(2)), + BorderStrokeStyle.SOLID, + CornerRadii.EMPTY, + new BorderWidths(0, 0, 1, 0) + ))); + }; + + // Update the background when hovered + controller.traceSummary.setOnMouseEntered(event -> { + setBackground.accept(color, colorIntensity.next()); + setCursor(Cursor.HAND); + }); + + // Update the background when the mouse exits + controller.traceSummary.setOnMouseExited(event -> { + setBackground.accept(color, colorIntensity); + setCursor(Cursor.DEFAULT); + }); + + // Update the background initially + setBackground.accept(color, colorIntensity); + } + + /** + * Updates the text of the summary title label with the current number of steps in the trace + * @param steps The number of steps in the trace + */ + private void updateSummaryTitle(int steps) { + controller.summaryTitleLabel.setText(steps + " number of steps in trace"); + } + + public TracePaneElementController getController() { + return controller; + } +} diff --git a/src/main/java/ecdar/presentations/TransitionPaneElementPresentation.java b/src/main/java/ecdar/presentations/TransitionPaneElementPresentation.java new file mode 100755 index 00000000..9dbac052 --- /dev/null +++ b/src/main/java/ecdar/presentations/TransitionPaneElementPresentation.java @@ -0,0 +1,69 @@ +package ecdar.presentations; + +import com.jfoenix.controls.JFXRippler; +import ecdar.controllers.TransitionPaneElementController; +import ecdar.utility.colors.Color; +import javafx.geometry.Insets; +import javafx.scene.layout.*; + +/** + * The presentation class for the transition pane element that can be inserted into the simulator panes + */ +public class TransitionPaneElementPresentation extends AnchorPane { + final private TransitionPaneElementController controller; + + public TransitionPaneElementPresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("TransitionPaneElementPresentation.fxml", this); + + initializeToolbar(); + initializeDelayChooser(); + } + + /** + * Initializes the toolbar for the transition pane element. + * Sets the background of the toolbar and changes the title color. + * Also changes the look of the rippler effect. + */ + private void initializeToolbar() { + final Color color = Color.GREY_BLUE; + final Color.Intensity colorIntensity = Color.Intensity.I800; + + // Set the background of the toolbar + controller.toolbar.setBackground(new Background(new BackgroundFill( + color.getColor(colorIntensity), + CornerRadii.EMPTY, + Insets.EMPTY))); + // Set the font color of elements in the toolbar + controller.toolbarTitle.setTextFill(color.getTextColor(colorIntensity)); + + controller.refreshRippler.setMaskType(JFXRippler.RipplerMask.CIRCLE); + controller.refreshRippler.setRipplerFill(color.getTextColor(colorIntensity)); + + controller.expandTransition.setMaskType(JFXRippler.RipplerMask.CIRCLE); + controller.expandTransition.setRipplerFill(color.getTextColor(colorIntensity)); + } + + /** + * Sets the background color of the delay chooser + */ + private void initializeDelayChooser() { + final Color color = Color.GREY_BLUE; + final Color.Intensity colorIntensity = Color.Intensity.I50; + controller.delayChooser.setBackground(new Background(new BackgroundFill( + color.getColor(colorIntensity), + CornerRadii.EMPTY, + Insets.EMPTY + ))); + + controller.delayChooser.setBorder(new Border(new BorderStroke( + color.getColor(colorIntensity.next(2)), + BorderStrokeStyle.SOLID, + CornerRadii.EMPTY, + new BorderWidths(0, 0, 1, 0) + ))); + } + + public TransitionPaneElementController getController() { + return controller; + } +} diff --git a/src/main/java/ecdar/presentations/TransitionPresentation.java b/src/main/java/ecdar/presentations/TransitionPresentation.java new file mode 100755 index 00000000..15fb47e6 --- /dev/null +++ b/src/main/java/ecdar/presentations/TransitionPresentation.java @@ -0,0 +1,98 @@ +package ecdar.presentations; + +import com.jfoenix.controls.JFXRippler; +import ecdar.controllers.TransitionController; +import ecdar.utility.colors.Color; +import javafx.animation.FadeTransition; +import javafx.animation.Interpolator; +import javafx.geometry.Insets; +import javafx.scene.Cursor; +import javafx.scene.layout.*; +import javafx.util.Duration; + +import java.util.function.BiConsumer; + +/** + * The presentation class for a transition view element. + * It represents a single transition and may be used by classes like {@see TransitionPaneElementController} + * to show a list of transitions + */ +public class TransitionPresentation extends AnchorPane { + private TransitionController controller; + private FadeTransition transition; + + public TransitionPresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("TransitionPresentation.fxml", this); + + initializeRippler(); + initializeColors(); + initializeFadeAnimation(); + } + + /** + * Initializes the rippler. + * Sets the color, mask and position of the rippler effect. + */ + private void initializeRippler() { + final Color color = Color.GREY_BLUE; + final Color.Intensity colorIntensity = Color.Intensity.I400; + + controller.rippler.setMaskType(JFXRippler.RipplerMask.RECT); + controller.rippler.setRipplerFill(color.getColor(colorIntensity)); + controller.rippler.setPosition(JFXRippler.RipplerPos.BACK); + } + + /** + * Initializes the colors of the view. + * The background of the view changes colors depending on whether a mouse enters or exits the view. + */ + private void initializeColors() { + final Color color = Color.GREY_BLUE; + final Color.Intensity colorIntensity = Color.Intensity.I50; + + final BiConsumer setBackground = (newColor, newIntensity) -> { + setBackground(new Background(new BackgroundFill( + newColor.getColor(newIntensity), + CornerRadii.EMPTY, + Insets.EMPTY + ))); + + setBorder(new Border(new BorderStroke( + newColor.getColor(newIntensity.next(2)), + BorderStrokeStyle.SOLID, + CornerRadii.EMPTY, + new BorderWidths(0, 0, 1, 0) + ))); + }; + + // Update the background when hovered + setOnMouseEntered(event -> { + setBackground.accept(color, colorIntensity.next()); + setCursor(Cursor.HAND); + }); + + // Update the background when the mouse exits + setOnMouseExited(event -> { + setBackground.accept(color, colorIntensity); + setCursor(Cursor.DEFAULT); + }); + + // Update the background initially + setBackground.accept(color, colorIntensity); + } + + private void initializeFadeAnimation() { + this.transition = new FadeTransition(Duration.millis(500), this); + transition.setFromValue(0); + transition.setToValue(1); + transition.setInterpolator(Interpolator.EASE_IN); + } + + public void playFadeAnimation() { + this.transition.play(); + } + + public TransitionController getController() { + return controller; + } +} diff --git a/src/main/java/ecdar/simulation/SimulationEdge.java b/src/main/java/ecdar/simulation/SimulationEdge.java new file mode 100644 index 00000000..696f573f --- /dev/null +++ b/src/main/java/ecdar/simulation/SimulationEdge.java @@ -0,0 +1,8 @@ +package ecdar.simulation; + +public class SimulationEdge { + public String getName() { + // ToDo: Implement + return "Edge name"; + } +} diff --git a/src/main/java/ecdar/simulation/SimulationHandler.java b/src/main/java/ecdar/simulation/SimulationHandler.java new file mode 100755 index 00000000..65d2ea73 --- /dev/null +++ b/src/main/java/ecdar/simulation/SimulationHandler.java @@ -0,0 +1,407 @@ +package ecdar.simulation; + +import ecdar.Ecdar; +import ecdar.abstractions.*; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.ObservableMap; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Map; + +/** + * Handles state changes, updates of values / clocks, and keeps track of all the transitions that + * have been taken throughout a simulation. + */ +public class SimulationHandler { + public static final String QUERY_PREFIX = "Query: "; + private ObjectProperty currentConcreteState; + private ObjectProperty initialConcreteState; + private ObjectProperty currentTime; + private BigDecimal delay; + private ArrayList edgesSelected; + private EcdarSystem system; + private SimulationStateSuccessor successor; + private int numberOfSteps; + + /** + * A string to keep track what is currently being simulated + * For now the string is prefixed with {@link #QUERY_PREFIX} when doing a query simulation + * and kept empty when doing system simulations + */ + private String currentSimulation = ""; + + private final ObservableMap simulationVariables = FXCollections.observableHashMap(); + private final ObservableMap simulationClocks = FXCollections.observableHashMap(); + /** + * For some reason the successor.getTransitions() only sometimes returns some of the transitions + * that are available, when running the initial step. + * That is why we need to keep track of the initial transitions. + */ + private final ObservableList initialTransitions = FXCollections.observableArrayList(); + public ObservableList traceLog = FXCollections.observableArrayList(); + public ObservableList availableTransitions = FXCollections.observableArrayList(); + + /** + * Empty constructor that should be used if the system or project has not be initialized yet + */ + public SimulationHandler() { + + } + + /** + * Initializes the default system (non-query system) + */ + public void initializeDefaultSystem() { + currentSimulation = ""; + } + + /** + * Initializes the values and properties in the {@link SimulationHandler}. + * Can also be used as a reset of the simulation. + * THIS METHOD DOES NOT RESET THE ENGINE, + */ + private void initializeSimulation() { + // Initialization + this.delay = new BigDecimal(0); + this.edgesSelected = new ArrayList<>(); + this.numberOfSteps = 0; + this.availableTransitions.clear(); + this.simulationVariables.clear(); + this.simulationClocks.clear(); + this.traceLog.clear(); + this.currentConcreteState = new SimpleObjectProperty<>(getInitialConcreteState()); + this.initialConcreteState = new SimpleObjectProperty<>(getInitialConcreteState()); + this.currentTime = new SimpleObjectProperty<>(BigDecimal.ZERO); + + //Preparation for the simulation + this.system = getSystem(); + this.currentConcreteState.get().setTime(currentTime.getValue()); + this.initialTransitions.clear(); + this.successor = null; + } + + /** + * Reloads the whole simulation sets the initial transitions, states, etc + */ + public void initialStep() { + initializeSimulation(); + final SimulationState currentState = currentConcreteState.get(); + successor = getStateSuccessor(); + + //Save the previous states, and get the new + currentConcreteState.set(successor.getState()); + this.traceLog.add(currentState); + numberOfSteps++; + + //Updates the transitions available + availableTransitions.addAll(FXCollections.observableArrayList(successor.getTransitions())); + initialTransitions.addAll(availableTransitions); + updateAllValues(); + } + + /** + * Resets the simulation to the initial location + * where the SimulationState is the {@link SimulationHandler#initialConcreteState}, when there are + * elements in the {@link SimulationHandler#traceLog}. Otherwise it calls {@link SimulationHandler#initialStep} + */ + public void resetToInitialLocation() { + //If the simulation has not begone + if (traceLog.size() == 0) + initialStep(); + else + selectTransitionFromLog(initialConcreteState.get()); + } + + /** + * Resets the simulation to the state after executing the given transition.
+ * This method also resets the state, variables, and clocks to the values they had after the given transition. + * This also updates {@link SimulationHandler#availableTransitions} such that + * it displays the available transitions after taking the given transition. + * + * @param transition the transition which the simulation should go back to + */ + public void selectTransitionFromLog(final SimulationState transition) { + final int indexInTrace = traceLog.indexOf(transition); + final SimulationState selectedState; + if (indexInTrace == -1) { + System.out.println("Cannot find transition: " + transition); + Ecdar.showToast("Cannot find transition: " + transition); + return; + } else if (indexInTrace == numberOfSteps - 1) { + return; //you have selected the current system + } else { + selectedState = traceLog.get(indexInTrace); + } + final int sizeOfTraceLog = traceLog.size(); + final int maxRetries = 3; + int numberOfRetries = 0; + edgesSelected = new ArrayList<>(); + //In case that we fail we have to save the time we had before + final BigDecimal tempTime = currentTime.get(); + + currentTime.setValue(new BigDecimal(selectedState.getTime().doubleValue())); + successor.getState().setTime(currentTime.getValue()); + + while (numberOfRetries < maxRetries) { + successor = getStateSuccessor(); + break; + } + currentConcreteState.set(selectedState); + setSimVarAndClocks(); + traceLog.remove(indexInTrace + 1, sizeOfTraceLog); + availableTransitions.clear(); + + // If the user selected the initial/first state in the trace log, we do not trust the engine, + // as it only gives us a subset of the available transitions, in some cases. + if (indexInTrace == 0) availableTransitions.addAll(initialTransitions); + else availableTransitions.addAll(successor.getTransitions()); + + numberOfSteps = indexInTrace + 1; + } + + /** + * Take a step in the simulation. + * + * @param selectedTransitionIndex the index of the availableTransition that you want to take. + * @param delay the time which should pass after the transition. + */ + public void nextStep(final int selectedTransitionIndex, final BigDecimal delay) { + if (selectedTransitionIndex > availableTransitions.size()) { + Ecdar.showToast("The selected transition index: " + selectedTransitionIndex + " is bigger than it should: " + availableTransitions); + return; + } + + final Transition selectedTransition = availableTransitions.get(selectedTransitionIndex); + edgesSelected = new ArrayList<>(); + + //Preparing for the step + for (int i = 0; i < selectedTransition.getEdges().size(); i++) { + edgesSelected.set(i, selectedTransition.getEdges().get(i)); + } + + final int maxRetries = 3; + int numberOfRetries = 0; + + // getConcreteSuccessor may throw a "ProtocolException: Word expected" but in some cases calling the same + // method again does not throw this exception, and actually gives us the expected result. + // This loop calls the method a number of times (maxRetries) + while (numberOfRetries < maxRetries) { + successor = getStateSuccessor(); + // Break from the loop if the method call was a success + break; + } + + //Save the previous states, and get the new + currentConcreteState.set(successor.getState()); + this.traceLog.add(currentConcreteState.get()); + + // increments the number of steps taken during this simulation + numberOfSteps++; + + //Updates the transitions available + availableTransitions.clear(); + availableTransitions.setAll(successor.getTransitions()); + this.delay = delay; + updateAllValues(); + } + + private SimulationStateSuccessor getStateSuccessor() { + // ToDo: Implement + return new SimulationStateSuccessor(); + } + + /** + * An overload of {@link SimulationHandler#nextStep(int, BigDecimal)} where the delay is 0. + * + * @param selectedTransition the index of the availableTransition that you want to take. + */ + public void nextStep(final int selectedTransition) { + nextStep(selectedTransition, BigDecimal.ZERO); + } + + public void nextStep(final Transition transition, final BigDecimal delay) { + int index = availableTransitions.indexOf(transition); + if (index != -1) { + nextStep(index, delay); + } + } + + /** + * Updates all values and clocks that are used doing the current simulation. + * It also stores the variables in the {@link SimulationHandler#simulationVariables} + * and the clocks in {@link SimulationHandler#simulationClocks}. + */ + private void updateAllValues() { + currentTime.set(currentTime.get().add(delay)); + successor.getState().setTime(currentTime.get()); + setSimVarAndClocks(); + } + + /** + * Sets the value of simulation variables and clocks, based on {@link SimulationHandler#currentConcreteState} + */ + private void setSimVarAndClocks() { + // The variables and clocks are all found in the getVariables array + // the array is always of the following order: variables, clocks. + // The noOfVars variable thus also functions as an offset for the clocks in the getVariables array +// final int noOfClocks = engine.getSystem().getNoOfClocks(); +// final int noOfVars = engine.getSystem().getNoOfVariables(); + +// for (int i = 0; i < noOfVars; i++){ +// simulationVariables.put(engine.getSystem().getVariableName(i), +// currentConcreteState.get().getVariables()[i].getValue(BigDecimal.ZERO)); +// } + + // As the clocks values starts after the variables values in currentConcreteState.get().getVariables() + // Then i needs to start where the variables ends. + // j is needed to map the correct name with the value +// for (int i = noOfVars, j = 0; i < noOfClocks + noOfVars ; i++, j++) { +// simulationClocks.put(engine.getSystem().getClockName(j), +// currentConcreteState.get().getVariables()[i].getValue(BigDecimal.ZERO)); +// } + } + + /** + * Getter for the current concrete state + * + * @return the current {@link SimulationState} + */ + public SimulationState getCurrentState() { + return currentConcreteState.get(); + } + + /** + * The way to get the time in the current state of a simulation + * + * @return the time in the current state + */ + public BigDecimal getCurrentTime() { + return currentTime.get(); + } + + public ObjectProperty currentTimeProperty() { + return currentTime; + } + + /** + * The way to get the delay of the latest step in the simulation + * + * @return the delay of the latest step in the in the simulation + */ + public BigDecimal getDelay() { + return delay; + } + + /** + * The number of total steps taken in the current simulation + * + * @return the number of steps + */ + public int getNumberOfSteps() { + return numberOfSteps; + } + + /** + * All the transitions taken in this simulation + * + * @return an {@link ObservableList} of all the transitions taken in this simulation so far + */ + public ObservableList getTraceLog() { + return traceLog; + } + + /** + * All the available transitions in this state + * + * @return an {@link ObservableList} of all the currently available transitions in this state + */ + public ObservableList getAvailableTransitions() { + return availableTransitions; + } + + /** + * All the variables connected to the current simulation. + * This does not return any clocks, if you need please use {@link SimulationHandler#getSimulationClocks()} instead + * + * @return a {@link Map} where the name (String) is the key, and a {@link BigDecimal} is the value + */ + public ObservableMap getSimulationVariables() { + return simulationVariables; + } + + /** + * All the clocks connected to the current simulation. + * + * @return a {@link Map} where the name (String) is the key, and a {@link BigDecimal} is the clock value + * @see SimulationHandler#getSimulationVariables() + */ + public ObservableMap getSimulationClocks() { + return simulationClocks; + } + + /** + * The initial state of the current simulation + * + * @return the initial {@link SimulationState} of this simulation + */ + public SimulationState getInitialConcreteState() { + // ToDo: Implement + return initialConcreteState.get(); + } + + public ObjectProperty initialConcreteStateProperty() { + return initialConcreteState; + } + + /** + * Prints all available transitions to {@link System#out}. + * This is very useful for debugging. + * If a string representation is needed please use {@link SimulationHandler#getAvailableTransitionsAsStrings()} + * instead. + */ + public void printAvailableTransitions() { + System.out.println("---------------------------------"); + + System.out.println(numberOfSteps + " Successor state " + currentConcreteState.toString() + " Entry time " + currentTime); + System.out.print("Available transitions: "); + availableTransitions.forEach( + Transition -> System.out.println(Transition.getLabel() + " ")); + + if (!availableTransitions.isEmpty()) { + for (int i = 0; i < availableTransitions.get(0).getEdges().size(); i++) { + // ToDo: Implement +// System.out.println("Edges: " + +// availableTransitions.get(0).getEdges().get(i).getEdge().getSource().getPropertyValue("name") + +// "." + availableTransitions.get(0).getEdges().get(i).getName() + " --> " + +// availableTransitions.get(0).getEdges().get(i).getEdge().getTarget().getPropertyValue("name")); + } + } + + System.out.println("---------------------------------"); + } + + /** + * To get all available transitions as strings + * + * @return an ArrayList of all the enabled transitions + */ + public ArrayList getAvailableTransitionsAsStrings() { + final ArrayList transitions = new ArrayList<>(); + for (final Transition Transition : availableTransitions) { + transitions.add(Transition.getLabel()); + } + return transitions; + } + + public EcdarSystem getSystem() { + return system; + } + + public String getCurrentSimulation() { + return currentSimulation; + } +} \ No newline at end of file diff --git a/src/main/java/ecdar/simulation/SimulationLocation.java b/src/main/java/ecdar/simulation/SimulationLocation.java new file mode 100644 index 00000000..5bcbba6a --- /dev/null +++ b/src/main/java/ecdar/simulation/SimulationLocation.java @@ -0,0 +1,8 @@ +package ecdar.simulation; + +public class SimulationLocation { + public String getName() { + // ToDo: Implement + return "Location name"; + } +} diff --git a/src/main/java/ecdar/simulation/SimulationState.java b/src/main/java/ecdar/simulation/SimulationState.java new file mode 100644 index 00000000..933864f1 --- /dev/null +++ b/src/main/java/ecdar/simulation/SimulationState.java @@ -0,0 +1,42 @@ +package ecdar.simulation; + +import ecdar.abstractions.Location; + +import java.math.BigDecimal; +import java.util.ArrayList; + +public class SimulationState { + public void setTime(BigDecimal value) { + // ToDo: Implement + } + + public Number getTime() { + // ToDo: Implement + return new Number() { + @Override + public int intValue() { + return 0; + } + + @Override + public long longValue() { + return 0; + } + + @Override + public float floatValue() { + return 0; + } + + @Override + public double doubleValue() { + return 0; + } + }; + } + + public ArrayList getLocations() { + // ToDo: Implement + return new ArrayList<>(); + } +} diff --git a/src/main/java/ecdar/simulation/SimulationStateSuccessor.java b/src/main/java/ecdar/simulation/SimulationStateSuccessor.java new file mode 100644 index 00000000..0c519adb --- /dev/null +++ b/src/main/java/ecdar/simulation/SimulationStateSuccessor.java @@ -0,0 +1,15 @@ +package ecdar.simulation; + +import java.util.ArrayList; + +public class SimulationStateSuccessor { + public ArrayList getTransitions() { + // ToDo: Implement + return new ArrayList<>(); + } + + public SimulationState getState() { + // ToDo: Implement + return new SimulationState(); + } +} diff --git a/src/main/java/ecdar/simulation/Transition.java b/src/main/java/ecdar/simulation/Transition.java new file mode 100644 index 00000000..da5bac14 --- /dev/null +++ b/src/main/java/ecdar/simulation/Transition.java @@ -0,0 +1,17 @@ +package ecdar.simulation; + +import ecdar.abstractions.Edge; + +import java.util.ArrayList; + +public class Transition { + public String getLabel() { + // ToDo: Implement + return "Transition label"; + } + + public ArrayList getEdges() { + // ToDo: Implement + return new ArrayList<>(); + } +} diff --git a/src/main/resources/ecdar/presentations/LeftSimPanePresentation.fxml b/src/main/resources/ecdar/presentations/LeftSimPanePresentation.fxml new file mode 100755 index 00000000..28ed941f --- /dev/null +++ b/src/main/resources/ecdar/presentations/LeftSimPanePresentation.fxml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml b/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml new file mode 100755 index 00000000..de2b3c4c --- /dev/null +++ b/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + diff --git a/src/main/resources/ecdar/presentations/SimulatorOverviewPresentation.fxml b/src/main/resources/ecdar/presentations/SimulatorOverviewPresentation.fxml new file mode 100755 index 00000000..f854ef33 --- /dev/null +++ b/src/main/resources/ecdar/presentations/SimulatorOverviewPresentation.fxml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/src/main/resources/ecdar/presentations/SimulatorPresentation.fxml b/src/main/resources/ecdar/presentations/SimulatorPresentation.fxml new file mode 100755 index 00000000..4f56d4fd --- /dev/null +++ b/src/main/resources/ecdar/presentations/SimulatorPresentation.fxml @@ -0,0 +1,55 @@ + + + + + + + + + + + +

+ + + + + + + + + + + + +
+ + + + + + + + + + + + + + + diff --git a/src/main/resources/ecdar/presentations/TracePaneElementPresentation.fxml b/src/main/resources/ecdar/presentations/TracePaneElementPresentation.fxml new file mode 100755 index 00000000..49cb83da --- /dev/null +++ b/src/main/resources/ecdar/presentations/TracePaneElementPresentation.fxml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ecdar/presentations/TransitionPaneElementPresentation.fxml b/src/main/resources/ecdar/presentations/TransitionPaneElementPresentation.fxml new file mode 100755 index 00000000..aeab56de --- /dev/null +++ b/src/main/resources/ecdar/presentations/TransitionPaneElementPresentation.fxml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ecdar/presentations/TransitionPresentation.fxml b/src/main/resources/ecdar/presentations/TransitionPresentation.fxml new file mode 100755 index 00000000..3fa1c5cf --- /dev/null +++ b/src/main/resources/ecdar/presentations/TransitionPresentation.fxml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + From a5b558c04a6092dc34022eab273196596ec14cab Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Wed, 13 Jul 2022 11:06:21 +0200 Subject: [PATCH 002/120] WIP: Compiling system with functionalities from Main working (simulator still not working, but presentation can be displayed) --- .../controllers/DeclarationsController.java | 8 +- .../ecdar/controllers/EcdarController.java | 465 +++++------------- .../ecdar/controllers/EditorController.java | 380 ++++++++++++++ .../ecdar/controllers/ProcessController.java | 40 +- .../controllers/RightSimPaneController.java | 3 +- .../ecdar/controllers/SimNailController.java | 126 +++++ .../controllers/SimulatorController.java | 0 .../SimulatorOverviewController.java | 8 +- .../presentations/EcdarPresentation.java | 228 +-------- .../presentations/EditorPresentation.java | 201 ++++++++ .../ecdar/presentations/FilePresentation.java | 2 +- .../RightSimPanePresentation.java | 2 +- .../SimEdgePresentation.java | 0 .../SimLocationPresentation.java | 4 +- .../presentations/SimNailPresentation.java | 258 ++++++++++ .../presentations/SimTagPresentation.java | 186 +++++++ .../ecdar/utility/helpers/BindingHelper.java | 10 + .../presentations/EcdarPresentation.fxml | 82 +-- .../presentations/EditorPresentation.fxml | 81 +++ .../RightSimPanePresentation.fxml | 3 +- .../presentations/SimEdgePresentation.fxml | 12 + .../SimLocationPresentation.fxml | 39 ++ .../presentations/SimNailPresentation.fxml | 23 + .../presentations/SimTagPresentation.fxml | 11 + 24 files changed, 1517 insertions(+), 655 deletions(-) create mode 100644 src/main/java/ecdar/controllers/EditorController.java create mode 100755 src/main/java/ecdar/controllers/SimNailController.java mode change 100755 => 100644 src/main/java/ecdar/controllers/SimulatorController.java mode change 100755 => 100644 src/main/java/ecdar/controllers/SimulatorOverviewController.java create mode 100644 src/main/java/ecdar/presentations/EditorPresentation.java rename src/main/java/ecdar/{controllers => presentations}/SimEdgePresentation.java (100%) rename src/main/java/ecdar/{controllers => presentations}/SimLocationPresentation.java (97%) create mode 100755 src/main/java/ecdar/presentations/SimNailPresentation.java create mode 100755 src/main/java/ecdar/presentations/SimTagPresentation.java create mode 100644 src/main/resources/ecdar/presentations/EditorPresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/SimEdgePresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/SimLocationPresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/SimNailPresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/SimTagPresentation.fxml diff --git a/src/main/java/ecdar/controllers/DeclarationsController.java b/src/main/java/ecdar/controllers/DeclarationsController.java index e08f9256..94875042 100644 --- a/src/main/java/ecdar/controllers/DeclarationsController.java +++ b/src/main/java/ecdar/controllers/DeclarationsController.java @@ -42,10 +42,10 @@ public void initialize(final URL location, final ResourceBundle resources) { */ private void initializeWidthAndHeight() { // Fetch width and height of canvas and update - root.minWidthProperty().bind(Ecdar.getPresentation().getController().canvasPane.minWidthProperty()); - root.maxWidthProperty().bind(Ecdar.getPresentation().getController().canvasPane.maxWidthProperty()); - root.minHeightProperty().bind(Ecdar.getPresentation().getController().canvasPane.minHeightProperty()); - root.maxHeightProperty().bind(Ecdar.getPresentation().getController().canvasPane.maxHeightProperty()); + root.minWidthProperty().bind(Ecdar.getPresentation().getController().getEditorPresentation().getController().canvasPane.minWidthProperty()); + root.maxWidthProperty().bind(Ecdar.getPresentation().getController().getEditorPresentation().getController().canvasPane.maxWidthProperty()); + root.minHeightProperty().bind(Ecdar.getPresentation().getController().getEditorPresentation().getController().canvasPane.minHeightProperty()); + root.maxHeightProperty().bind(Ecdar.getPresentation().getController().getEditorPresentation().getController().canvasPane.maxHeightProperty()); updateOffset(EcdarController.getActiveCanvasPresentation().getController().getInsetShouldShow().get()); EcdarController.getActiveCanvasPresentation().getController().getInsetShouldShow().addListener((observable, oldValue, newValue) -> { diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index 9baee9c7..806b040c 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -12,7 +12,6 @@ import ecdar.presentations.*; import ecdar.utility.UndoRedoStack; import ecdar.utility.colors.Color; -import ecdar.utility.colors.EnabledColor; import ecdar.utility.helpers.SelectHelper; import ecdar.utility.keyboard.Keybind; import ecdar.utility.keyboard.KeyboardTracker; @@ -23,7 +22,6 @@ import javafx.beans.binding.When; import javafx.beans.property.*; import javafx.collections.ListChangeListener; -import javafx.collections.ObservableList; import javafx.embed.swing.SwingFXUtils; import javafx.fxml.FXML; import javafx.fxml.Initializable; @@ -39,7 +37,6 @@ import javafx.scene.text.Text; import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; -import javafx.util.Pair; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; @@ -56,29 +53,21 @@ public class EcdarController implements Initializable { private static long reachabilityTime = Long.MAX_VALUE; private static ExecutorService reachabilityService; - private static final ObjectProperty globalEdgeStatus = new SimpleObjectProperty<>(EdgeStatus.INPUT); - // View stuff public StackPane root; public BorderPane borderPane; - public StackPane canvasPane; public StackPane topPane; public StackPane leftPane; public StackPane rightPane; public Rectangle bottomFillerElement; public QueryPanePresentation queryPane; public ProjectPanePresentation filePane; - public HBox toolbar; public MessageTabPanePresentation messageTabPane; public StackPane dialogContainer; public JFXDialog dialog; public StackPane modalBar; public JFXTextField queryTextField; public JFXTextField commentTextField; - public JFXRippler colorSelected; - public JFXRippler deleteSelected; - public JFXRippler undo; - public JFXRippler redo; public ImageView helpInitialImage; public StackPane helpInitialPane; @@ -89,10 +78,6 @@ public class EcdarController implements Initializable { public ImageView helpOutputImage; public StackPane helpOutputPane; - public JFXButton switchToInputButton; - public JFXButton switchToOutputButton; - public JFXToggleButton switchEdgeStatusButton; - public MenuItem menuEditMoveLeft; public MenuItem menuEditMoveUp; public MenuItem menuEditMoveRight; @@ -109,7 +94,7 @@ public class EcdarController implements Initializable { public MenuItem menuBarViewFilePanel; public MenuItem menuBarViewQueryPanel; public MenuItem menuBarViewGrid; - public MenuItem menuBarAutoscaling; + public MenuItem menuBarViewAutoscaling; public Menu menuViewMenuScaling; public ToggleGroup scaling; public RadioMenuItem scaleXS; @@ -120,6 +105,8 @@ public class EcdarController implements Initializable { public RadioMenuItem scaleXXL; public RadioMenuItem scaleXXXL; public MenuItem menuBarViewCanvasSplit; + public MenuItem menuBarViewEditor; + public MenuItem menuBarViewSimulator; public MenuItem menuBarFileCreateNewProject; public MenuItem menuBarFileOpenProject; public Menu menuBarFileRecentProjects; @@ -161,10 +148,80 @@ public static void runReachabilityAnalysis() { reachabilityTime = System.currentTimeMillis() + 500; } - private static final ObjectProperty activeCanvasPresentation = new SimpleObjectProperty<>(new CanvasPresentation()); + /** + * Enumeration to keep track of which mode the application is in + */ + private enum Mode { + Editor, Simulator + } + + /** + * currentMode is a property that keeps track of which mode the application is in. + * The initial mode is Mode.Editor + */ + private static final ObjectProperty currentMode = new SimpleObjectProperty<>(Mode.Editor); + + private static final EditorPresentation editorPresentation = new EditorPresentation(); + private static final SimulatorPresentation simulatorPresentation = new SimulatorPresentation(); + + @Override + public void initialize(final URL location, final ResourceBundle resources) { + initializeDialogs(); + initializeKeybindings(); + initializeStatusBar(); + initializeMenuBar(); + startBackgroundQueriesThread(); // Will terminate immediately if background queries are turned off + + bottomFillerElement.heightProperty().bind(messageTabPane.maxHeightProperty()); + messageTabPane.getController().setRunnableForOpeningAndClosingMessageTabPane(this::changeInsetsOfFileAndQueryPanes); + + // Update file coloring when active model changes + editorPresentation.getController().getActiveCanvasPresentation().getController().activeComponentProperty().addListener(observable -> filePane.getController().updateColorsOnFilePresentations()); + editorPresentation.getController().activeCanvasPresentationProperty().addListener(observable -> filePane.getController().updateColorsOnFilePresentations()); + + borderPane.centerProperty().addListener((observable, oldValue, newValue) -> System.out.println(newValue.getClass())); + borderPane.setCenter(editorPresentation); + } + + public StackPane getCenter() { + if (currentMode.get().equals(Mode.Editor)) { + return editorPresentation.getController().canvasPane; + } else { + return simulatorPresentation; + } + } public static EdgeStatus getGlobalEdgeStatus() { - return globalEdgeStatus.get(); + return editorPresentation.getController().getGlobalEdgeStatus(); + } + + public EditorPresentation getEditorPresentation() { + return editorPresentation; + } + + public SimulatorPresentation getSimulatorPresentation() { + return simulatorPresentation; + } + + public static CanvasPresentation getActiveCanvasPresentation() { + return editorPresentation.getController().getActiveCanvasPresentation(); + } + + public static DoubleProperty getActiveCanvasZoomFactor() { + return getActiveCanvasPresentation().getController().zoomHelper.currentZoomFactor; + } + + public static void setActiveCanvasPresentation(CanvasPresentation newActiveCanvasPresentation) { + getActiveCanvasPresentation().setOpacity(0.75); + newActiveCanvasPresentation.setOpacity(1); + editorPresentation.getController().setActiveCanvasPresentation(newActiveCanvasPresentation); + } + + public static void setActiveModelForActiveCanvas(HighLevelModelObject newActiveModel) { + getActiveCanvasPresentation().getController().setActiveModel(newActiveModel); + + // Change zoom level to fit new active model + Platform.runLater(() -> getActiveCanvasPresentation().getController().zoomHelper.zoomToFit()); } /** @@ -197,26 +254,7 @@ private double getNewCalculatedScale() { return (Double.parseDouble(scaling.getSelectedToggle().getProperties().get("scale").toString()) * Ecdar.getDpiScale()) * 13.0; } - private void scaleEdgeStatusToggle(double size) { - switchEdgeStatusButton.setScaleX(size / 13.0); - switchEdgeStatusButton.setScaleY(size / 13.0); - } - - @Override - public void initialize(final URL location, final ResourceBundle resources) { - initilizeDialogs(); - initializeCanvasPane(); - initializeEdgeStatusHandling(); - initializeKeybindings(); - initializeStatusBar(); - initializeMenuBar(); - startBackgroundQueriesThread(); // Will terminate immediately if background queries are turned off - - bottomFillerElement.heightProperty().bind(messageTabPane.maxHeightProperty()); - messageTabPane.getController().setRunnableForOpeningAndClosingMessageTabPane(this::changeInsetsOfFileAndQueryPanes); - } - - private void initilizeDialogs() { + private void initializeDialogs() { dialog.setDialogContainer(dialogContainer); dialogContainer.opacityProperty().bind(dialog.getChildren().get(0).scaleXProperty()); dialog.setOnDialogClosed(event -> dialogContainer.setVisible(false)); @@ -269,7 +307,6 @@ private void initializeDialog(JFXDialog dialog, StackPane dialogContainer) { filePane.getStyleClass().add("responsive-pane-sizing"); queryPane.getStyleClass().add("responsive-pane-sizing"); - initializeEdgeStatusHandling(); initializeKeybindings(); initializeStatusBar(); } @@ -302,17 +339,14 @@ private void initializeKeybindings() { event.consume(); nudgeSelected(NudgeDirection.UP); })); - KeyboardTracker.registerKeybind(KeyboardTracker.NUDGE_DOWN, new Keybind(new KeyCodeCombination(KeyCode.DOWN), (event) -> { event.consume(); nudgeSelected(NudgeDirection.DOWN); })); - KeyboardTracker.registerKeybind(KeyboardTracker.NUDGE_LEFT, new Keybind(new KeyCodeCombination(KeyCode.LEFT), (event) -> { event.consume(); nudgeSelected(NudgeDirection.LEFT); })); - KeyboardTracker.registerKeybind(KeyboardTracker.NUDGE_RIGHT, new Keybind(new KeyCodeCombination(KeyCode.RIGHT), (event) -> { event.consume(); nudgeSelected(NudgeDirection.RIGHT); @@ -326,67 +360,6 @@ private void initializeKeybindings() { KeyboardTracker.registerKeybind(KeyboardTracker.NUDGE_A, new Keybind(new KeyCodeCombination(KeyCode.A), () -> nudgeSelected(NudgeDirection.LEFT))); KeyboardTracker.registerKeybind(KeyboardTracker.NUDGE_S, new Keybind(new KeyCodeCombination(KeyCode.S), () -> nudgeSelected(NudgeDirection.DOWN))); KeyboardTracker.registerKeybind(KeyboardTracker.NUDGE_D, new Keybind(new KeyCodeCombination(KeyCode.D), () -> nudgeSelected(NudgeDirection.RIGHT))); - - // Keybind for deleting the selected elements - KeyboardTracker.registerKeybind(KeyboardTracker.DELETE_SELECTED, new Keybind(new KeyCodeCombination(KeyCode.DELETE), this::deleteSelectedClicked)); - - // Keybinds for coloring the selected elements - EnabledColor.enabledColors.forEach(enabledColor -> { - KeyboardTracker.registerKeybind(KeyboardTracker.COLOR_SELECTED + "_" + enabledColor.keyCode.getName(), new Keybind(new KeyCodeCombination(enabledColor.keyCode), () -> { - - final List> previousColor = new ArrayList<>(); - - SelectHelper.getSelectedElements().forEach(selectable -> { - previousColor.add(new Pair<>(selectable, new EnabledColor(selectable.getColor(), selectable.getColorIntensity()))); - }); - changeColorOnSelectedElements(enabledColor, previousColor); - SelectHelper.clearSelectedElements(); - })); - }); - } - - /** - * Handles the change of color on selected objects - * - * @param enabledColor The new color for the selected objects - * @param previousColor The color old color of the selected objects - */ - public void changeColorOnSelectedElements(final EnabledColor enabledColor, - final List> previousColor) { - UndoRedoStack.pushAndPerform(() -> { // Perform - SelectHelper.getSelectedElements() - .forEach(selectable -> selectable.color(enabledColor.color, enabledColor.intensity)); - }, () -> { // Undo - previousColor.forEach(selectableEnabledColorPair -> selectableEnabledColorPair.getKey().color(selectableEnabledColorPair.getValue().color, selectableEnabledColorPair.getValue().intensity)); - }, String.format("Changed the color of %d elements to %s", previousColor.size(), enabledColor.color.name()), "color-lens"); - } - - /** - * Initializes edge status. - * Input is the default status. - * This method sets buttons for edge status whenever the status changes. - */ - private void initializeEdgeStatusHandling() { - globalEdgeStatus.set(EdgeStatus.INPUT); - - Tooltip.install(switchToInputButton, new Tooltip("Switch to input mode")); - Tooltip.install(switchToOutputButton, new Tooltip("Switch to output mode")); - switchToInputButton.setDisableVisualFocus(true); // Hiding input button rippler on start-up - - globalEdgeStatus.addListener(((observable, oldValue, newValue) -> { - if (newValue.equals(EdgeStatus.INPUT)) { - switchToInputButton.setTextFill(javafx.scene.paint.Color.WHITE); - switchToOutputButton.setTextFill(javafx.scene.paint.Color.GREY); - switchEdgeStatusButton.setSelected(false); - } else { - switchToInputButton.setTextFill(javafx.scene.paint.Color.GREY); - switchToOutputButton.setTextFill(javafx.scene.paint.Color.WHITE); - switchEdgeStatusButton.setSelected(true); - } - })); - - // Ensure that the rippler is centered when scale is changed - Platform.runLater(() -> ((JFXRippler) switchEdgeStatusButton.lookup(".jfx-rippler")).setRipplerRecenter(true)); } private void startBackgroundQueriesThread() { @@ -537,27 +510,6 @@ private void initializeMenuBar() { initializeHelpMenu(); } - public static CanvasPresentation getActiveCanvasPresentation() { - return activeCanvasPresentation.get(); - } - - public static DoubleProperty getActiveCanvasZoomFactor() { - return getActiveCanvasPresentation().getController().zoomHelper.currentZoomFactor; - } - - public static void setActiveCanvasPresentation(CanvasPresentation newActiveCanvasPresentation) { - activeCanvasPresentation.get().setOpacity(0.75); - newActiveCanvasPresentation.setOpacity(1); - activeCanvasPresentation.set(newActiveCanvasPresentation); - } - - public static void setActiveModelForActiveCanvas(HighLevelModelObject newActiveModel) { - EcdarController.getActiveCanvasPresentation().getController().setActiveModel(newActiveModel); - - // Change zoom level to fit new active model - Platform.runLater(() -> EcdarController.getActiveCanvasPresentation().getController().zoomHelper.zoomToFit()); - } - private void initializeHelpMenu() { menuBarHelpHelp.setOnAction(event -> Ecdar.showHelp()); @@ -574,7 +526,6 @@ private void initializeHelpMenu() { }); aboutAcceptButton.setOnAction(event -> aboutDialog.close()); aboutDialog.setOnDialogClosed(event -> aboutContainer.setVisible(false)); // hide container when dialog is fully closed - } /** @@ -664,14 +615,14 @@ private void initializeViewMenu() { menuBarViewGrid.getGraphic().opacityProperty().bind(new When(isOn).then(1).otherwise(0)); }); - menuBarAutoscaling.getGraphic().setOpacity(Ecdar.autoScalingEnabled.getValue() ? 1 : 0); - menuBarAutoscaling.setOnAction(event -> { + menuBarViewAutoscaling.getGraphic().setOpacity(Ecdar.autoScalingEnabled.getValue() ? 1 : 0); + menuBarViewAutoscaling.setOnAction(event -> { Ecdar.autoScalingEnabled.setValue(!Ecdar.autoScalingEnabled.getValue()); updateScaling(getNewCalculatedScale() / 13); Ecdar.preferences.put("autoscaling", String.valueOf(Ecdar.autoScalingEnabled.getValue())); }); Ecdar.autoScalingEnabled.addListener((observable, oldValue, newValue) -> { - menuBarAutoscaling.getGraphic().opacityProperty().setValue(newValue ? 1 : 0); + menuBarViewAutoscaling.getGraphic().opacityProperty().setValue(newValue ? 1 : 0); }); scaling.selectedToggleProperty().addListener((observable, oldValue, newValue) -> updateScaling(Double.parseDouble(newValue.getProperties().get("scale").toString()))); @@ -680,14 +631,28 @@ private void initializeViewMenu() { menuBarViewCanvasSplit.setOnAction(event -> { final BooleanProperty isSplit = Ecdar.toggleCanvasSplit(); if (isSplit.get()) { - Platform.runLater(this::setCanvasModeToSingular); + Platform.runLater(() -> editorPresentation.getController().setCanvasModeToSingular()); menuBarViewCanvasSplit.setText("Split canvas"); } else { - Platform.runLater(this::setCanvasModeToSplit); + Platform.runLater(() -> editorPresentation.getController().setCanvasModeToSplit()); menuBarViewCanvasSplit.setText("Merge canvases"); } }); + menuBarViewEditor.setAccelerator(new KeyCodeCombination(KeyCode.DIGIT1, KeyCombination.SHORTCUT_DOWN)); + menuBarViewEditor.setOnAction(event -> editorRipplerClicked()); + + menuBarViewSimulator.setAccelerator(new KeyCodeCombination(KeyCode.DIGIT2, KeyCombination.SHORTCUT_DOWN)); + menuBarViewSimulator.setOnAction(event -> simulatorRipplerClicked()); + + currentMode.addListener((obs, oldMode, newMode) -> { + if (newMode == Mode.Editor && oldMode != newMode) { + enterEditorMode(); + } else if (newMode == Mode.Simulator && oldMode != newMode) { + enterSimulatorMode(); + } + }); + // On startup, set the scaling to the values saved in preferences Platform.runLater(() -> { Ecdar.autoScalingEnabled.setValue(Ecdar.preferences.getBoolean("autoscaling", true)); @@ -716,7 +681,7 @@ private void updateScaling(double newScale) { Ecdar.preferences.put("scale", String.valueOf(newScale)); scaleIcons(root, newCalculatedScale); - scaleEdgeStatusToggle(newCalculatedScale); + editorPresentation.getController().scaleEdgeStatusToggle(newCalculatedScale); messageTabPane.getController().updateScale(newScale); // Update listeners of UI scale @@ -982,153 +947,52 @@ private void initializeFileExportAsPng() { }); } - private void initializeCanvasPane() { - Platform.runLater(this::setCanvasModeToSingular); - } - /** - * Removes the canvases and adds a new one, with the active component of the active canvasPresentation. + * Method for click on the Editor rippler. Changes mode to the editor */ - private void setCanvasModeToSingular() { - canvasPane.getChildren().clear(); - CanvasPresentation canvasPresentation = new CanvasPresentation(); - HighLevelModelObject model = activeCanvasPresentation.get().getController().getActiveModel(); - if (model != null) { - canvasPresentation.getController().setActiveModel(activeCanvasPresentation.get().getController().getActiveModel()); - } else { - // If no components where found, the project has not been initialized. The active model will be updated when the project is initialized - canvasPresentation.getController().setActiveModel(Ecdar.getProject().getComponents().stream().findFirst().orElse(null)); + private void editorRipplerClicked() { + if (currentMode.get() != Mode.Editor) { + currentMode.setValue(Mode.Editor); } - - canvasPane.getChildren().add(canvasPresentation); - activeCanvasPresentation.set(canvasPresentation); - filePane.getController().updateColorsOnFilePresentations(); - - Rectangle clip = new Rectangle(); - clip.setArcWidth(1); - clip.setArcHeight(1); - clip.widthProperty().bind(canvasPane.widthProperty()); - clip.heightProperty().bind(canvasPane.heightProperty()); - canvasPresentation.getController().zoomablePane.setClip(clip); - - canvasPresentation.getController().zoomablePane.minWidthProperty().bind(canvasPane.widthProperty()); - canvasPresentation.getController().zoomablePane.maxWidthProperty().bind(canvasPane.widthProperty()); - canvasPresentation.getController().zoomablePane.minHeightProperty().bind(canvasPane.heightProperty()); - canvasPresentation.getController().zoomablePane.maxHeightProperty().bind(canvasPane.heightProperty()); } /** - * Removes the canvas and adds a GridPane with four new canvases, with different active components, - * the first being the one previously displayed on the single canvas. + * Method for click on the Simulator rippler. Changes mode to the simulator */ - private void setCanvasModeToSplit() { - canvasPane.getChildren().clear(); - - GridPane canvasGrid = new GridPane(); - - canvasGrid.addColumn(0); - canvasGrid.addColumn(1); - canvasGrid.addRow(0); - canvasGrid.addRow(1); - - ColumnConstraints col1 = new ColumnConstraints(); - col1.setPercentWidth(50); - - RowConstraints row1 = new RowConstraints(); - row1.setPercentHeight(50); - - canvasGrid.getColumnConstraints().add(col1); - canvasGrid.getColumnConstraints().add(col1); - canvasGrid.getRowConstraints().add(row1); - canvasGrid.getRowConstraints().add(row1); - - ObservableList components = Ecdar.getProject().getComponents(); - int currentCompNum = 0, numComponents = components.size(); - - // Add the canvasPresentation at the top-left - CanvasPresentation canvasPresentation = initializeNewCanvasPresentation(); - canvasPresentation.getController().setActiveModel(getActiveCanvasPresentation().getController().getActiveModel()); - canvasGrid.add(canvasPresentation, 0, 0); - setActiveCanvasPresentation(canvasPresentation); - - // Add the canvasPresentation at the top-right - canvasPresentation = initializeNewCanvasPresentationWithActiveComponent(components, currentCompNum); - canvasPresentation.setOpacity(0.75); - canvasGrid.add(canvasPresentation, 1, 0); - - // Update the startIndex for the next canvasPresentation - for (int i = 0; i < numComponents; i++) { - if (canvasPresentation.getController().getActiveModel() != null && canvasPresentation.getController().getActiveModel().equals(components.get(i))) { - currentCompNum = i + 1; - } + private void simulatorRipplerClicked() { + if (currentMode.get() != Mode.Simulator) { + currentMode.setValue(Mode.Simulator); } - - // Add the canvasPresentation at the bottom-left - canvasPresentation = initializeNewCanvasPresentationWithActiveComponent(components, currentCompNum); - canvasPresentation.setOpacity(0.75); - canvasGrid.add(canvasPresentation, 0, 1); - - // Update the startIndex for the next canvasPresentation - for (int i = 0; i < numComponents; i++) - if (canvasPresentation.getController().getActiveModel() != null && canvasPresentation.getController().getActiveModel().equals(components.get(i))) { - currentCompNum = i + 1; - } - - // Add the canvasPresentation at the bottom-right - canvasPresentation = initializeNewCanvasPresentationWithActiveComponent(components, currentCompNum); - canvasPresentation.setOpacity(0.75); - canvasGrid.add(canvasPresentation, 1, 1); - - canvasPane.getChildren().add(canvasGrid); - filePane.getController().updateColorsOnFilePresentations(); } /** - * Initialize a new CanvasShellPresentation and set its active component to the next component encountered from the startIndex and return it - * - * @param components the list of components for assigning active component of the CanvasPresentation - * @param startIndex the index to start at when trying to find the component to set as active - * @return new CanvasShellPresentation + * Changes the view and mode to the editor + * Only enter if the mode is not already Editor */ - private CanvasPresentation initializeNewCanvasPresentationWithActiveComponent(ObservableList components, int startIndex) { - CanvasPresentation canvasPresentation = initializeNewCanvasPresentation(); - - int numComponents = components.size(); - canvasPresentation.getController().setActiveModel(null); - for (int currentCompNum = startIndex; currentCompNum < numComponents; currentCompNum++) { - if (getActiveCanvasPresentation().getController().getActiveModel() != components.get(currentCompNum)) { - canvasPresentation.getController().setActiveModel(components.get(currentCompNum)); - break; - } - } + private void enterEditorMode() { +// ToDo NIELS: Consider implementing willShow and willHide to handle general elements that should only be available for one of the modes +// editorPresentation.getController().willShow(); +// simulatorPresentation.getController().willHide(); - return canvasPresentation; + borderPane.setCenter(editorPresentation); + + // Enable or disable the menu items that can be used when in the simulator +// updateMenuItems(); } /** - * Initialize a new CanvasPresentation and return it - * - * @return new CanvasPresentation + * Changes the view and mode to the simulator + * Only enter if the mode is not already Simulator */ - private CanvasPresentation initializeNewCanvasPresentation() { - CanvasPresentation canvasPresentation = new CanvasPresentation(); - canvasPresentation.setBorder(new Border(new BorderStroke(Color.GREY.getColor(Color.Intensity.I500), BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderStroke.THIN))); - - // Set th clip of the zoomable pane to be half of the canvasPane, - // to ensure a 2 by 2 grid without overflowing borders - Rectangle clip = new Rectangle(); - clip.setArcWidth(1); - clip.setArcHeight(1); - clip.widthProperty().bind(canvasPane.widthProperty().divide(2)); - clip.heightProperty().bind(canvasPane.heightProperty().divide(2)); - canvasPresentation.getController().zoomablePane.setClip(clip); - - canvasPresentation.getController().zoomablePane.minWidthProperty().bind(canvasPane.widthProperty().divide(2)); - canvasPresentation.getController().zoomablePane.maxWidthProperty().bind(canvasPane.widthProperty().divide(2)); - canvasPresentation.getController().zoomablePane.minHeightProperty().bind(canvasPane.heightProperty().divide(2)); - canvasPresentation.getController().zoomablePane.maxHeightProperty().bind(canvasPane.heightProperty().divide(2)); - - return canvasPresentation; + private void enterSimulatorMode() { +// ToDo NIELS: Consider implementing willShow and willHide to handle general elements that should only be available for one of the modes +// ecdarPresentation.getController().willHide(); +// simulatorPresentation.getController().willShow(); + + borderPane.setCenter(simulatorPresentation); + + // Enable or disable the menu items that can be used when in the simulator +// updateMenuItems(); } /** @@ -1339,85 +1203,6 @@ private void nudgeSelected(final NudgeDirection direction) { "open-with"); } - @FXML - private void deleteSelectedClicked() { - if (SelectHelper.getSelectedElements().size() == 0) return; - - // Run through the selected elements and look for something that we can delete - SelectHelper.getSelectedElements().forEach(selectable -> { - if (selectable instanceof LocationController) { - ((LocationController) selectable).tryDelete(); - } else if (selectable instanceof EdgeController) { - final Component component = ((EdgeController) selectable).getComponent(); - final DisplayableEdge edge = ((EdgeController) selectable).getEdge(); - - // Dont delete edge if it is locked - if (edge.getIsLockedProperty().getValue()) { - return; - } - - UndoRedoStack.pushAndPerform(() -> { // Perform - // Remove the edge - component.removeEdge(edge); - }, () -> { // Undo - // Re-all the edge - component.addEdge(edge); - }, String.format("Deleted %s", selectable.toString()), "delete"); - } else if (selectable instanceof NailController) { - ((NailController) selectable).tryDelete(); - } - }); - - SelectHelper.clearSelectedElements(); - } - - @FXML - private void undoClicked() { - UndoRedoStack.undo(); - } - - @FXML - private void redoClicked() { - UndoRedoStack.redo(); - } - - /** - * Switch to input edge mode - */ - @FXML - private void switchToInputClicked() { - setGlobalEdgeStatus(EdgeStatus.INPUT); - } - - /** - * Switch to output edge mode - */ - @FXML - private void switchToOutputClicked() { - setGlobalEdgeStatus(EdgeStatus.OUTPUT); - } - - /** - * Switch edge status. - */ - @FXML - private void switchEdgeStatusClicked() { - if (getGlobalEdgeStatus().equals(EdgeStatus.INPUT)) { - setGlobalEdgeStatus(EdgeStatus.OUTPUT); - } else { - setGlobalEdgeStatus(EdgeStatus.INPUT); - } - } - - /** - * Sets the global edge status. - * - * @param status the status - */ - private void setGlobalEdgeStatus(EdgeStatus status) { - globalEdgeStatus.set(status); - } - @FXML private void closeQueryDialog() { dialog.close(); diff --git a/src/main/java/ecdar/controllers/EditorController.java b/src/main/java/ecdar/controllers/EditorController.java new file mode 100644 index 00000000..605cc6fd --- /dev/null +++ b/src/main/java/ecdar/controllers/EditorController.java @@ -0,0 +1,380 @@ +package ecdar.controllers; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXRippler; +import com.jfoenix.controls.JFXToggleButton; +import ecdar.Ecdar; +import ecdar.abstractions.Component; +import ecdar.abstractions.DisplayableEdge; +import ecdar.abstractions.EdgeStatus; +import ecdar.abstractions.HighLevelModelObject; +import ecdar.presentations.CanvasPresentation; +import ecdar.utility.UndoRedoStack; +import ecdar.utility.colors.Color; +import ecdar.utility.colors.EnabledColor; +import ecdar.utility.helpers.SelectHelper; +import ecdar.utility.keyboard.Keybind; +import ecdar.utility.keyboard.KeyboardTracker; +import javafx.application.Platform; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Tooltip; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.layout.*; +import javafx.scene.shape.Rectangle; +import javafx.util.Pair; + +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.ResourceBundle; + +public class EditorController implements Initializable { + public VBox root; + public HBox toolbar; + public JFXRippler colorSelected; + public JFXRippler deleteSelected; + public JFXRippler undo; + public JFXRippler redo; + public JFXButton switchToInputButton; + public JFXButton switchToOutputButton; + public JFXToggleButton switchEdgeStatusButton; + public StackPane canvasPane; + + private final ObjectProperty globalEdgeStatus = new SimpleObjectProperty<>(EdgeStatus.INPUT); + private final ObjectProperty activeCanvasPresentation = new SimpleObjectProperty<>(new CanvasPresentation()); + + @Override + public void initialize(final URL location, final ResourceBundle resources) { + initializeCanvasPane(); + initializeEdgeStatusHandling(); + initializeKeybindings(); + } + + public EdgeStatus getGlobalEdgeStatus() { + return globalEdgeStatus.get(); + } + + ObjectProperty activeCanvasPresentationProperty() { + return activeCanvasPresentation; + } + + public CanvasPresentation getActiveCanvasPresentation() { + return activeCanvasPresentation.get(); + } + + public void setActiveCanvasPresentation(CanvasPresentation newActiveCanvasPresentation) { + activeCanvasPresentation.get().setOpacity(0.75); + newActiveCanvasPresentation.setOpacity(1); + activeCanvasPresentation.set(newActiveCanvasPresentation); + } + + public void setActiveModelForActiveCanvas(HighLevelModelObject newActiveModel) { + EcdarController.getActiveCanvasPresentation().getController().setActiveModel(newActiveModel); + + // Change zoom level to fit new active model + Platform.runLater(() -> EcdarController.getActiveCanvasPresentation().getController().zoomHelper.zoomToFit()); + } + + private void initializeCanvasPane() { + Platform.runLater(this::setCanvasModeToSingular); + } + + private void initializeKeybindings() { + // Keybinds for coloring the selected elements + EnabledColor.enabledColors.forEach(enabledColor -> { + KeyboardTracker.registerKeybind(KeyboardTracker.COLOR_SELECTED + "_" + enabledColor.keyCode.getName(), new Keybind(new KeyCodeCombination(enabledColor.keyCode), () -> { + + final List> previousColor = new ArrayList<>(); + + SelectHelper.getSelectedElements().forEach(selectable -> { + previousColor.add(new Pair<>(selectable, new EnabledColor(selectable.getColor(), selectable.getColorIntensity()))); + }); + changeColorOnSelectedElements(enabledColor, previousColor); + SelectHelper.clearSelectedElements(); + })); + }); + + // Keybind for deleting the selected elements + KeyboardTracker.registerKeybind(KeyboardTracker.DELETE_SELECTED, new Keybind(new KeyCodeCombination(KeyCode.DELETE), this::deleteSelectedClicked)); + } + + /** + * Initializes edge status. + * Input is the default status. + * This method sets buttons for edge status whenever the status changes. + */ + private void initializeEdgeStatusHandling() { + globalEdgeStatus.set(EdgeStatus.INPUT); + + Tooltip.install(switchToInputButton, new Tooltip("Switch to input mode")); + Tooltip.install(switchToOutputButton, new Tooltip("Switch to output mode")); + switchToInputButton.setDisableVisualFocus(true); // Hiding input button rippler on start-up + + globalEdgeStatus.addListener(((observable, oldValue, newValue) -> { + if (newValue.equals(EdgeStatus.INPUT)) { + switchToInputButton.setTextFill(javafx.scene.paint.Color.WHITE); + switchToOutputButton.setTextFill(javafx.scene.paint.Color.GREY); + switchEdgeStatusButton.setSelected(false); + } else { + switchToInputButton.setTextFill(javafx.scene.paint.Color.GREY); + switchToOutputButton.setTextFill(javafx.scene.paint.Color.WHITE); + switchEdgeStatusButton.setSelected(true); + } + })); + + // Ensure that the rippler is centered when scale is changed + Platform.runLater(() -> { + var rippler = ((JFXRippler) switchEdgeStatusButton.lookup(".jfx-rippler")); + if (rippler == null) return; + rippler.setRipplerRecenter(true); + }); + } + + /** + * Sets the global edge status. + * + * @param status the status + */ + private void setGlobalEdgeStatus(EdgeStatus status) { + globalEdgeStatus.set(status); + } + + void scaleEdgeStatusToggle(double size) { + switchEdgeStatusButton.setScaleX(size / 13.0); + switchEdgeStatusButton.setScaleY(size / 13.0); + } + + /** + * Handles the change of color on selected objects + * + * @param enabledColor The new color for the selected objects + * @param previousColor The color old color of the selected objects + */ + public void changeColorOnSelectedElements(final EnabledColor enabledColor, + final List> previousColor) { + UndoRedoStack.pushAndPerform(() -> { // Perform + SelectHelper.getSelectedElements() + .forEach(selectable -> selectable.color(enabledColor.color, enabledColor.intensity)); + }, () -> { // Undo + previousColor.forEach(selectableEnabledColorPair -> selectableEnabledColorPair.getKey().color(selectableEnabledColorPair.getValue().color, selectableEnabledColorPair.getValue().intensity)); + }, String.format("Changed the color of %d elements to %s", previousColor.size(), enabledColor.color.name()), "color-lens"); + } + + /** + * Removes the canvases and adds a new one, with the active component of the active canvasPresentation. + */ + void setCanvasModeToSingular() { + canvasPane.getChildren().clear(); + CanvasPresentation canvasPresentation = new CanvasPresentation(); + HighLevelModelObject model = activeCanvasPresentation.get().getController().getActiveModel(); + if (model != null) { + canvasPresentation.getController().setActiveModel(activeCanvasPresentation.get().getController().getActiveModel()); + } else { + // If no components where found, the project has not been initialized. The active model will be updated when the project is initialized + canvasPresentation.getController().setActiveModel(Ecdar.getProject().getComponents().stream().findFirst().orElse(null)); + } + + canvasPane.getChildren().add(canvasPresentation); + activeCanvasPresentation.set(canvasPresentation); + + Rectangle clip = new Rectangle(); + clip.setArcWidth(1); + clip.setArcHeight(1); + clip.widthProperty().bind(canvasPane.widthProperty()); + clip.heightProperty().bind(canvasPane.heightProperty()); + + canvasPresentation.getController().zoomablePane.setClip(clip); + canvasPresentation.getController().zoomablePane.minWidthProperty().bind(canvasPane.widthProperty()); + canvasPresentation.getController().zoomablePane.maxWidthProperty().bind(canvasPane.widthProperty()); + canvasPresentation.getController().zoomablePane.minHeightProperty().bind(canvasPane.heightProperty()); + canvasPresentation.getController().zoomablePane.maxHeightProperty().bind(canvasPane.heightProperty()); + } + + /** + * Removes the canvas and adds a GridPane with four new canvases, with different active components, + * the first being the one previously displayed on the single canvas. + */ + void setCanvasModeToSplit() { + canvasPane.getChildren().clear(); + + GridPane canvasGrid = new GridPane(); + + canvasGrid.addColumn(0); + canvasGrid.addColumn(1); + canvasGrid.addRow(0); + canvasGrid.addRow(1); + + ColumnConstraints col1 = new ColumnConstraints(); + col1.setPercentWidth(50); + + RowConstraints row1 = new RowConstraints(); + row1.setPercentHeight(50); + + canvasGrid.getColumnConstraints().add(col1); + canvasGrid.getColumnConstraints().add(col1); + canvasGrid.getRowConstraints().add(row1); + canvasGrid.getRowConstraints().add(row1); + + ObservableList components = Ecdar.getProject().getComponents(); + int currentCompNum = 0, numComponents = components.size(); + + // Add the canvasPresentation at the top-left + CanvasPresentation canvasPresentation = initializeNewCanvasPresentation(); + canvasPresentation.getController().setActiveModel(getActiveCanvasPresentation().getController().getActiveModel()); + canvasGrid.add(canvasPresentation, 0, 0); + setActiveCanvasPresentation(canvasPresentation); + + // Add the canvasPresentation at the top-right + canvasPresentation = initializeNewCanvasPresentationWithActiveComponent(components, currentCompNum); + canvasPresentation.setOpacity(0.75); + canvasGrid.add(canvasPresentation, 1, 0); + + // Update the startIndex for the next canvasPresentation + for (int i = 0; i < numComponents; i++) { + if (canvasPresentation.getController().getActiveModel() != null && canvasPresentation.getController().getActiveModel().equals(components.get(i))) { + currentCompNum = i + 1; + } + } + + // Add the canvasPresentation at the bottom-left + canvasPresentation = initializeNewCanvasPresentationWithActiveComponent(components, currentCompNum); + canvasPresentation.setOpacity(0.75); + canvasGrid.add(canvasPresentation, 0, 1); + + // Update the startIndex for the next canvasPresentation + for (int i = 0; i < numComponents; i++) + if (canvasPresentation.getController().getActiveModel() != null && canvasPresentation.getController().getActiveModel().equals(components.get(i))) { + currentCompNum = i + 1; + } + + // Add the canvasPresentation at the bottom-right + canvasPresentation = initializeNewCanvasPresentationWithActiveComponent(components, currentCompNum); + canvasPresentation.setOpacity(0.75); + canvasGrid.add(canvasPresentation, 1, 1); + + canvasPane.getChildren().add(canvasGrid); + } + + /** + * Initialize a new CanvasShellPresentation and set its active component to the next component encountered from the startIndex and return it + * + * @param components the list of components for assigning active component of the CanvasPresentation + * @param startIndex the index to start at when trying to find the component to set as active + * @return new CanvasShellPresentation + */ + private CanvasPresentation initializeNewCanvasPresentationWithActiveComponent(ObservableList components, int startIndex) { + CanvasPresentation canvasPresentation = initializeNewCanvasPresentation(); + + int numComponents = components.size(); + canvasPresentation.getController().setActiveModel(null); + for (int currentCompNum = startIndex; currentCompNum < numComponents; currentCompNum++) { + if (getActiveCanvasPresentation().getController().getActiveModel() != components.get(currentCompNum)) { + canvasPresentation.getController().setActiveModel(components.get(currentCompNum)); + break; + } + } + + return canvasPresentation; + } + + /** + * Initialize a new CanvasPresentation and return it + * + * @return new CanvasPresentation + */ + private CanvasPresentation initializeNewCanvasPresentation() { + CanvasPresentation canvasPresentation = new CanvasPresentation(); + canvasPresentation.setBorder(new Border(new BorderStroke(Color.GREY.getColor(Color.Intensity.I500), BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderStroke.THIN))); + + // Set th clip of the zoomable pane to be half of the canvasPane, + // to ensure a 2 by 2 grid without overflowing borders + Rectangle clip = new Rectangle(); + clip.setArcWidth(1); + clip.setArcHeight(1); + clip.widthProperty().bind(canvasPane.widthProperty().divide(2)); + clip.heightProperty().bind(canvasPane.heightProperty().divide(2)); + canvasPresentation.getController().zoomablePane.setClip(clip); + + canvasPresentation.getController().zoomablePane.minWidthProperty().bind(canvasPane.widthProperty().divide(2)); + canvasPresentation.getController().zoomablePane.maxWidthProperty().bind(canvasPane.widthProperty().divide(2)); + canvasPresentation.getController().zoomablePane.minHeightProperty().bind(canvasPane.heightProperty().divide(2)); + canvasPresentation.getController().zoomablePane.maxHeightProperty().bind(canvasPane.heightProperty().divide(2)); + + return canvasPresentation; + } + + @FXML + private void undoClicked() { + UndoRedoStack.undo(); + } + + @FXML + private void redoClicked() { + UndoRedoStack.redo(); + } + + /** + * Switch to input edge mode + */ + @FXML + private void switchToInputClicked() { + setGlobalEdgeStatus(EdgeStatus.INPUT); + } + + /** + * Switch to output edge mode + */ + @FXML + private void switchToOutputClicked() { + setGlobalEdgeStatus(EdgeStatus.OUTPUT); + } + + /** + * Switch edge status. + */ + @FXML + private void switchEdgeStatusClicked() { + if (getGlobalEdgeStatus().equals(EdgeStatus.INPUT)) { + setGlobalEdgeStatus(EdgeStatus.OUTPUT); + } else { + setGlobalEdgeStatus(EdgeStatus.INPUT); + } + } + + @FXML + private void deleteSelectedClicked() { + if (SelectHelper.getSelectedElements().size() == 0) return; + + // Run through the selected elements and look for something that we can delete + SelectHelper.getSelectedElements().forEach(selectable -> { + if (selectable instanceof LocationController) { + ((LocationController) selectable).tryDelete(); + } else if (selectable instanceof EdgeController) { + final Component component = ((EdgeController) selectable).getComponent(); + final DisplayableEdge edge = ((EdgeController) selectable).getEdge(); + + // Dont delete edge if it is locked + if (edge.getIsLockedProperty().getValue()) { + return; + } + + UndoRedoStack.pushAndPerform(() -> { // Perform + // Remove the edge + component.removeEdge(edge); + }, () -> { // Undo + // Re-all the edge + component.addEdge(edge); + }, String.format("Deleted %s", selectable.toString()), "delete"); + } else if (selectable instanceof NailController) { + ((NailController) selectable).tryDelete(); + } + }); + + SelectHelper.clearSelectedElements(); + } +} diff --git a/src/main/java/ecdar/controllers/ProcessController.java b/src/main/java/ecdar/controllers/ProcessController.java index 84f9ff22..2934b54d 100755 --- a/src/main/java/ecdar/controllers/ProcessController.java +++ b/src/main/java/ecdar/controllers/ProcessController.java @@ -65,29 +65,19 @@ private void initializeValues() { * Highlights the edges and accompanying source/target locations in the process * @param edges The edges to highlight */ - public void highlightEdges(final SimulationEdge[] edges) { + public void highlightEdges(final Edge[] edges) { for (int i = 0; i < edges.length; i++) { - final SimulationEdge edge = edges[i]; + final Edge edge = edges[i]; // Note that SimulationEdge contains a Location type from the UPPAAL libraries // but we use a different Location class in our Maps - final Location source = edge.getSource(); - String sourceName = ""; - final Location target = edge.getTarget(); - String targetName = ""; - - // Match the Locations of the SimulationEdge with a SimulationLocation of the process, - // so we can get the source/target names (names are not available on a Location) - for (SimulationLocation sysloc : edge.getProcess().getLocations()) { - if (sysloc.getLocation() == source) { - sourceName = sysloc.getName(); - } else if (sysloc == target) { - targetName = sysloc.getName(); - } - } + final Location source = edge.getSourceLocation(); + String sourceName = source.getNickname(); + final Location target = edge.getTargetLocation(); + String targetName = target.getNickname(); // If target name is empty the edge is a self loop - if (targetName == "") { + if (Objects.equals(targetName, "")) { targetName = sourceName; } @@ -120,21 +110,19 @@ public void highlightEdges(final SimulationEdge[] edges) { } // The edge name may contain ! or ?, and we need to replace those so we can compare our stored edge - String edgeName = edge.getName(); - EdgeStatus edgeStatus = EdgeStatus.INPUT; - if (edgeName.contains("?")) { - edgeName = edgeName.replace("?", ""); - } else if (edgeName.contains("!")) { - edgeName = edgeName.replace("!", ""); - edgeStatus = EdgeStatus.OUTPUT; - } + String edgeName = edge.getId(); +// if (edgeName.contains("?")) { +// edgeName = edgeName.replace("?", ""); +// } else if (edgeName.contains("!")) { +// edgeName = edgeName.replace("!", ""); +// } // Self loop on a Universal locations means that the edge name should be mapped to * if (isSourceUniversal && sourceName.equals(targetName)) { edgeName = "*"; } - highlightEdge(edgeName, edgeStatus, sourceName, targetName); + highlightEdge(edgeName, edge.getStatus(), sourceName, targetName); } } diff --git a/src/main/java/ecdar/controllers/RightSimPaneController.java b/src/main/java/ecdar/controllers/RightSimPaneController.java index c7d5b6db..5b86fc1b 100755 --- a/src/main/java/ecdar/controllers/RightSimPaneController.java +++ b/src/main/java/ecdar/controllers/RightSimPaneController.java @@ -1,6 +1,5 @@ package ecdar.controllers; -import ecdar.presentations.QueryPaneElementPresentation; import javafx.fxml.Initializable; import javafx.scene.layout.*; @@ -13,7 +12,7 @@ public class RightSimPaneController implements Initializable { public StackPane root; public VBox scrollPaneVbox; - public QueryPaneElementPresentation queryPaneElement; +// public QueryPaneElementPresentation queryPaneElement; @Override public void initialize(URL location, ResourceBundle resources) { diff --git a/src/main/java/ecdar/controllers/SimNailController.java b/src/main/java/ecdar/controllers/SimNailController.java new file mode 100755 index 00000000..053456d3 --- /dev/null +++ b/src/main/java/ecdar/controllers/SimNailController.java @@ -0,0 +1,126 @@ +package ecdar.controllers; + +import ecdar.Debug; +import ecdar.abstractions.Component; +import ecdar.abstractions.Edge; +import ecdar.abstractions.Nail; +import ecdar.presentations.SimNailPresentation; +import ecdar.presentations.SimTagPresentation; +import ecdar.utility.colors.Color; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.fxml.Initializable; +import javafx.scene.Group; +import javafx.scene.control.Label; +import javafx.scene.shape.Circle; +import javafx.scene.shape.Line; + +import java.net.URL; +import java.util.ResourceBundle; + +public class SimNailController implements Initializable { + private final ObjectProperty component = new SimpleObjectProperty<>(); + private final ObjectProperty edge = new SimpleObjectProperty<>(); + private final ObjectProperty nail = new SimpleObjectProperty<>(); + + private SimEdgeController edgeController; + public SimNailPresentation root; + public Circle nailCircle; + public Circle dragCircle; + public Line propertyTagLine; + public SimTagPresentation propertyTag; + public Group dragGroup; + public Label propertyLabel; + + @Override + public void initialize(final URL location, final ResourceBundle resources) { + nail.addListener((obsNail, oldNail, newNail) -> { + + // The radius from the abstraction is the master and the view simply reflects what is in the model + nailCircle.radiusProperty().bind(newNail.radiusProperty()); + + // Draw the presentation based on the initial value from the abstraction + root.setLayoutX(newNail.getX()); + root.setLayoutY(newNail.getY()); + + // Reflect future updates from the presentation into the abstraction + newNail.xProperty().bindBidirectional(root.layoutXProperty()); + newNail.yProperty().bindBidirectional(root.layoutYProperty()); + + }); + + // Debug visuals + dragCircle.opacityProperty().bind(Debug.draggableAreaOpacity); + dragCircle.setFill(Debug.draggableAreaColor.getColor(Debug.draggableAreaColorIntensity)); + } + + /** + * Sets an edge controller. + * This should be called when adding a nail. + * @param controller the edge controller + */ + public void setEdgeController(final SimEdgeController controller) { + this.edgeController = controller; + } + + public Nail getNail() { + return nail.get(); + } + + public void setNail(final Nail nail) { + this.nail.set(nail); + } + + public ObjectProperty nailProperty() { + return nail; + } + + public Component getComponent() { + return component.get(); + } + + public void setComponent(final Component component) { + this.component.set(component); + } + + public ObjectProperty componentProperty() { + return component; + } + + public Edge getEdge() { + return edge.get(); + } + + public void setEdge(final Edge edge) { + this.edge.set(edge); + } + + public ObjectProperty edgeProperty() { + return edge; + } + + public Color getColor() { + return getComponent().getColor(); + } + + public Color.Intensity getColorIntensity() { + return getComponent().getColorIntensity(); + } + + public DoubleProperty xProperty() { + return root.layoutXProperty(); + } + + public DoubleProperty yProperty() { + return root.layoutYProperty(); + } + + public double getX() { + return xProperty().get(); + } + + public double getY() { + return yProperty().get(); + } +} diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java old mode 100755 new mode 100644 diff --git a/src/main/java/ecdar/controllers/SimulatorOverviewController.java b/src/main/java/ecdar/controllers/SimulatorOverviewController.java old mode 100755 new mode 100644 index 160e9e51..3a6dbc13 --- a/src/main/java/ecdar/controllers/SimulatorOverviewController.java +++ b/src/main/java/ecdar/controllers/SimulatorOverviewController.java @@ -326,13 +326,11 @@ public void highlightProcessTransition(final Transition transition) { // Processes are removed from this list, if they have an edge in the transition final ArrayList processesToHide = new ArrayList<>(processPresentations.values()); - for (final Edge edge : edges) { - final Process process = edge.getProcess(); + for (final ProcessPresentation processPresentation : processPresentations.values()) { // Find the processes that have edges involved in this transition - final ProcessPresentation presentation = processPresentations.get(process.getName()); - presentation.getController().highlightEdges(edges); - processesToHide.remove(presentation); + processPresentation.getController().highlightEdges(edges.toArray(new Edge[0])); + processesToHide.remove(processPresentation); } processesToHide.forEach(ProcessPresentation::showInactive); diff --git a/src/main/java/ecdar/presentations/EcdarPresentation.java b/src/main/java/ecdar/presentations/EcdarPresentation.java index f8fb5f25..4884f95f 100644 --- a/src/main/java/ecdar/presentations/EcdarPresentation.java +++ b/src/main/java/ecdar/presentations/EcdarPresentation.java @@ -7,10 +7,6 @@ import ecdar.controllers.EcdarController; import ecdar.utility.UndoRedoStack; import ecdar.utility.colors.Color; -import ecdar.utility.colors.EnabledColor; -import ecdar.utility.helpers.SelectHelper; -import com.jfoenix.controls.JFXPopup; -import com.jfoenix.controls.JFXRippler; import com.jfoenix.controls.JFXSnackbar; import ecdar.utility.keyboard.Keybind; import ecdar.utility.keyboard.KeyboardTracker; @@ -22,30 +18,21 @@ import javafx.beans.property.SimpleDoubleProperty; import javafx.collections.ListChangeListener; import javafx.geometry.Insets; -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; import javafx.scene.layout.*; -import javafx.scene.shape.Circle; import javafx.util.Duration; -import javafx.util.Pair; - -import java.util.ArrayList; -import java.util.List; - -import static ecdar.utility.colors.EnabledColor.enabledColors; public class EcdarPresentation extends StackPane { private final EcdarController controller; private final BooleanProperty filePaneOpen = new SimpleBooleanProperty(false); - private final SimpleDoubleProperty filePaneAnimationProperty = new SimpleDoubleProperty(0); + private final SimpleDoubleProperty leftPaneAnimationProperty = new SimpleDoubleProperty(0); private final BooleanProperty queryPaneOpen = new SimpleBooleanProperty(false); - private final SimpleDoubleProperty queryPaneAnimationProperty = new SimpleDoubleProperty(0); + private final SimpleDoubleProperty rightPaneAnimationProperty = new SimpleDoubleProperty(0); private Timeline openQueryPaneAnimation; private Timeline closeQueryPaneAnimation; private Timeline openFilePaneAnimation; @@ -54,48 +41,33 @@ public class EcdarPresentation extends StackPane { public EcdarPresentation() { controller = new EcdarFXMLLoader().loadAndGetController("EcdarPresentation.fxml", this); initializeTopBar(); - initializeToolbar(); initializeQueryDetailsDialog(); - initializeColorSelector(); - initializeToggleQueryPaneFunctionality(); initializeToggleFilePaneFunctionality(); - - initializeSelectDependentToolbarButton(controller.colorSelected); - Tooltip.install(controller.colorSelected, new Tooltip("Colour")); - - initializeSelectDependentToolbarButton(controller.deleteSelected); - Tooltip.install(controller.deleteSelected, new Tooltip("Delete")); - - initializeToolbarButton(controller.undo); - initializeToolbarButton(controller.redo); - initializeUndoRedoButtons(); initializeSnackbar(); // Open the file and query panel initially Platform.runLater(() -> { // Bind sizing of sides and center panes to ensure correct sizing - controller.canvasPane.minWidthProperty().bind(controller.root.widthProperty().subtract(filePaneAnimationProperty.add(queryPaneAnimationProperty))); - controller.canvasPane.maxWidthProperty().bind(controller.root.widthProperty().subtract(filePaneAnimationProperty.add(queryPaneAnimationProperty))); + controller.getEditorPresentation().getController().canvasPane.minWidthProperty().bind(controller.root.widthProperty().subtract(leftPaneAnimationProperty.add(rightPaneAnimationProperty))); + controller.getEditorPresentation().getController().canvasPane.maxWidthProperty().bind(controller.root.widthProperty().subtract(leftPaneAnimationProperty.add(rightPaneAnimationProperty))); // Bind the height to ensure that both the top and bottom panes are shown // The height of the top pane is multiplied by 4 as the UI does not account for the height otherwise - controller.canvasPane.minHeightProperty().bind(controller.root.heightProperty().subtract(controller.topPane.heightProperty().multiply(4).add(controller.bottomFillerElement.heightProperty()))); - controller.canvasPane.maxHeightProperty().bind(controller.root.heightProperty().subtract(controller.topPane.heightProperty().multiply(4).add(controller.bottomFillerElement.heightProperty()))); + controller.getEditorPresentation().getController().canvasPane.minHeightProperty().bind(controller.root.heightProperty().subtract(controller.topPane.heightProperty().multiply(4).add(controller.bottomFillerElement.heightProperty()))); + controller.getEditorPresentation().getController().canvasPane.maxHeightProperty().bind(controller.root.heightProperty().subtract(controller.topPane.heightProperty().multiply(4).add(controller.bottomFillerElement.heightProperty()))); - controller.leftPane.minWidthProperty().bind(filePaneAnimationProperty); - controller.leftPane.maxWidthProperty().bind(filePaneAnimationProperty); + controller.leftPane.minWidthProperty().bind(leftPaneAnimationProperty); + controller.leftPane.maxWidthProperty().bind(leftPaneAnimationProperty); - controller.rightPane.minWidthProperty().bind(queryPaneAnimationProperty); - controller.rightPane.maxWidthProperty().bind(queryPaneAnimationProperty); + controller.rightPane.minWidthProperty().bind(rightPaneAnimationProperty); + controller.rightPane.maxWidthProperty().bind(rightPaneAnimationProperty); controller.topPane.minHeightProperty().bind(controller.menuBar.heightProperty()); controller.topPane.maxHeightProperty().bind(controller.menuBar.heightProperty()); - Platform.runLater(() -> { - toggleFilePane(); - toggleQueryPane(); - }); + toggleFilePane(); + toggleQueryPane(); Ecdar.getPresentation().controller.scalingProperty.addListener((observable, oldValue, newValue) -> { // If the scaling has changed trigger animations for open panes to update width @@ -130,147 +102,6 @@ private void initializeSnackbar() { controller.snackbar.autosize(); } - private void initializeUndoRedoButtons() { - UndoRedoStack.canUndoProperty().addListener((obs, oldState, newState) -> { - if (newState) { - // Enable the undo button - controller.undo.setEnabled(true); - controller.undo.setOpacity(1); - } else { - // Disable the undo button - controller.undo.setEnabled(false); - controller.undo.setOpacity(0.3); - } - }); - - UndoRedoStack.canRedoProperty().addListener((obs, oldState, newState) -> { - if (newState) { - // Enable the redo button - controller.redo.setEnabled(true); - controller.redo.setOpacity(1); - } else { - // Disable the redo button - controller.redo.setEnabled(false); - controller.redo.setOpacity(0.3); - } - }); - - // Disable the undo button - controller.undo.setEnabled(false); - controller.undo.setOpacity(0.3); - - // Disable the redo button - controller.redo.setEnabled(false); - controller.redo.setOpacity(0.3); - - // Set tooltips - Tooltip.install(controller.undo, new Tooltip("Undo")); - Tooltip.install(controller.redo, new Tooltip("Redo")); - } - - private void initializeColorSelector() { - final JFXPopup popup = new JFXPopup(); - - final double listWidth = 136; - final FlowPane list = new FlowPane(); - for (final EnabledColor color : enabledColors) { - final Circle circle = new Circle(16, color.color.getColor(color.intensity)); - circle.setStroke(color.color.getColor(color.intensity.next(2))); - circle.setStrokeWidth(1); - - final Label label = new Label(color.keyCode.getName()); - label.getStyleClass().add("subhead"); - label.setTextFill(color.color.getTextColor(color.intensity)); - - final StackPane child = new StackPane(circle, label); - child.setMinSize(40, 40); - child.setMaxSize(40, 40); - - child.setOnMouseEntered(event -> { - final ScaleTransition scaleTransition = new ScaleTransition(Duration.millis(100), circle); - scaleTransition.setFromX(circle.getScaleX()); - scaleTransition.setFromY(circle.getScaleY()); - scaleTransition.setToX(1.1); - scaleTransition.setToY(1.1); - scaleTransition.play(); - }); - - child.setOnMouseExited(event -> { - final ScaleTransition scaleTransition = new ScaleTransition(Duration.millis(100), circle); - scaleTransition.setFromX(circle.getScaleX()); - scaleTransition.setFromY(circle.getScaleY()); - scaleTransition.setToX(1.0); - scaleTransition.setToY(1.0); - scaleTransition.play(); - }); - - child.setOnMouseClicked(event -> { - final List> previousColor = new ArrayList<>(); - - SelectHelper.getSelectedElements().forEach(selectable -> { - previousColor.add(new Pair<>(selectable, new EnabledColor(selectable.getColor(), selectable.getColorIntensity()))); - }); - - controller.changeColorOnSelectedElements(color, previousColor); - - popup.hide(); - SelectHelper.clearSelectedElements(); - }); - - list.getChildren().add(child); - } - list.setMinWidth(listWidth); - list.setMaxWidth(listWidth); - list.setStyle("-fx-background-color: white; -fx-padding: 8;"); - - popup.setPopupContent(list); - - controller.colorSelected.setOnMouseClicked((e) -> { - // If nothing is selected - if (SelectHelper.getSelectedElements().size() == 0) return; - popup.show(controller.colorSelected, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, -10, 15); - }); - } - - private void initializeSelectDependentToolbarButton(final JFXRippler button) { - initializeToolbarButton(button); - - // The color button should only be enabled when an element is selected - SelectHelper.getSelectedElements().addListener(new ListChangeListener() { - @Override - public void onChanged(final Change c) { - if (SelectHelper.getSelectedElements().size() > 0) { - button.setEnabled(true); - - final FadeTransition fadeAnimation = new FadeTransition(Duration.millis(100), button); - fadeAnimation.setFromValue(button.getOpacity()); - fadeAnimation.setToValue(1); - fadeAnimation.play(); - } else { - button.setEnabled(false); - - final FadeTransition fadeAnimation = new FadeTransition(Duration.millis(100), button); - fadeAnimation.setFromValue(1); - fadeAnimation.setToValue(0.3); - fadeAnimation.play(); - } - } - }); - - // Disable the button - button.setEnabled(false); - button.setOpacity(0.3); - } - - private void initializeToolbarButton(final JFXRippler button) { - final Color color = Color.GREY_BLUE; - final Color.Intensity colorIntensity = Color.Intensity.I800; - - button.setMaskType(JFXRippler.RipplerMask.CIRCLE); - button.setRipplerFill(color.getTextColor(colorIntensity)); - button.setPosition(JFXRippler.RipplerPos.BACK); - } - private void initializeQueryDetailsDialog() { final Color modalBarColor = Color.GREY_BLUE; final Color.Intensity modalBarColorIntensity = Color.Intensity.I500; @@ -288,7 +119,7 @@ private void initializeToggleFilePaneFunctionality() { initializeCloseFilePaneAnimation(); // Translate the x coordinate to create the open/close animations - controller.filePane.translateXProperty().bind(filePaneAnimationProperty.subtract(controller.filePane.widthProperty())); + controller.filePane.translateXProperty().bind(leftPaneAnimationProperty.subtract(controller.filePane.widthProperty())); // Whenever the width of the file pane is updated, update the animations controller.filePane.widthProperty().addListener((observable) -> { @@ -302,8 +133,8 @@ private void initializeCloseFilePaneAnimation() { closeFilePaneAnimation = new Timeline(); - final KeyValue open = new KeyValue(filePaneAnimationProperty, controller.filePane.getWidth(), interpolator); - final KeyValue closed = new KeyValue(filePaneAnimationProperty, 0, interpolator); + final KeyValue open = new KeyValue(leftPaneAnimationProperty, controller.filePane.getWidth(), interpolator); + final KeyValue closed = new KeyValue(leftPaneAnimationProperty, 0, interpolator); final KeyFrame kf1 = new KeyFrame(Duration.millis(0), open); final KeyFrame kf2 = new KeyFrame(Duration.millis(200), closed); @@ -316,8 +147,8 @@ private void initializeOpenFilePaneAnimation() { openFilePaneAnimation = new Timeline(); - final KeyValue closed = new KeyValue(filePaneAnimationProperty, 0, interpolator); - final KeyValue open = new KeyValue(filePaneAnimationProperty, controller.filePane.getWidth(), interpolator); + final KeyValue closed = new KeyValue(leftPaneAnimationProperty, 0, interpolator); + final KeyValue open = new KeyValue(leftPaneAnimationProperty, controller.filePane.getWidth(), interpolator); final KeyFrame kf1 = new KeyFrame(Duration.millis(0), closed); final KeyFrame kf2 = new KeyFrame(Duration.millis(200), open); @@ -330,7 +161,7 @@ private void initializeToggleQueryPaneFunctionality() { initializeCloseQueryPaneAnimation(); // Translate the x coordinate to create the open/close animations - controller.queryPane.translateXProperty().bind(queryPaneAnimationProperty.multiply(-1).add(controller.queryPane.widthProperty())); + controller.queryPane.translateXProperty().bind(rightPaneAnimationProperty.multiply(-1).add(controller.queryPane.widthProperty())); // Whenever the width of the query pane is updated, update the animations controller.queryPane.widthProperty().addListener((observable) -> { @@ -362,8 +193,8 @@ private void initializeCloseQueryPaneAnimation() { closeQueryPaneAnimation = new Timeline(); - final KeyValue open = new KeyValue(queryPaneAnimationProperty, controller.queryPane.getWidth(), interpolator); - final KeyValue closed = new KeyValue(queryPaneAnimationProperty, 0, interpolator); + final KeyValue open = new KeyValue(rightPaneAnimationProperty, controller.queryPane.getWidth(), interpolator); + final KeyValue closed = new KeyValue(rightPaneAnimationProperty, 0, interpolator); final KeyFrame kf1 = new KeyFrame(Duration.millis(0), open); final KeyFrame kf2 = new KeyFrame(Duration.millis(200), closed); @@ -376,8 +207,8 @@ private void initializeOpenQueryPaneAnimation() { openQueryPaneAnimation = new Timeline(); - final KeyValue closed = new KeyValue(queryPaneAnimationProperty, 0, interpolator); - final KeyValue open = new KeyValue(queryPaneAnimationProperty, controller.queryPane.getWidth(), interpolator); + final KeyValue closed = new KeyValue(rightPaneAnimationProperty, 0, interpolator); + final KeyValue open = new KeyValue(rightPaneAnimationProperty, controller.queryPane.getWidth(), interpolator); final KeyFrame kf1 = new KeyFrame(Duration.millis(0), closed); final KeyFrame kf2 = new KeyFrame(Duration.millis(200), open); @@ -405,18 +236,6 @@ private void initializeTopBar() { ))); } - private void initializeToolbar() { - final Color color = Color.GREY_BLUE; - final Color.Intensity intensity = Color.Intensity.I700; - - // Set the background for the top toolbar - controller.toolbar.setBackground( - new Background(new BackgroundFill(color.getColor(intensity), - CornerRadii.EMPTY, - Insets.EMPTY) - )); - } - /** * Initialize help image views. */ @@ -451,13 +270,12 @@ private void initializeResizeQueryPane() { // Set bounds for resizing to be between 280px and half the screen width final double newWidth = Math.min(Math.max(prevWidth.get() + diff, 280), controller.root.getWidth() / 2); - queryPaneAnimationProperty.set(newWidth); + rightPaneAnimationProperty.set(newWidth); controller.queryPane.setMaxWidth(newWidth); controller.queryPane.setMinWidth(newWidth); }); } - public BooleanProperty toggleFilePane() { if (filePaneOpen.get()) { closeFilePaneAnimation.play(); diff --git a/src/main/java/ecdar/presentations/EditorPresentation.java b/src/main/java/ecdar/presentations/EditorPresentation.java new file mode 100644 index 00000000..d17a4ab0 --- /dev/null +++ b/src/main/java/ecdar/presentations/EditorPresentation.java @@ -0,0 +1,201 @@ +package ecdar.presentations; + +import com.jfoenix.controls.JFXPopup; +import com.jfoenix.controls.JFXRippler; +import ecdar.controllers.EditorController; +import ecdar.utility.UndoRedoStack; +import ecdar.utility.colors.Color; +import ecdar.utility.colors.EnabledColor; +import ecdar.utility.helpers.SelectHelper; +import javafx.animation.FadeTransition; +import javafx.animation.ScaleTransition; +import javafx.collections.ListChangeListener; +import javafx.geometry.Insets; +import javafx.scene.control.Label; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.*; +import javafx.scene.shape.Circle; +import javafx.util.Duration; +import javafx.util.Pair; + +import java.util.ArrayList; +import java.util.List; + +import static ecdar.utility.colors.EnabledColor.enabledColors; + +public class EditorPresentation extends VBox { + private final EditorController controller; + + public EditorPresentation() { + this.controller = new EcdarFXMLLoader().loadAndGetController("EditorPresentation.fxml", this); + initializeToolbar(); + initializeColorSelector(); + + initializeSelectDependentToolbarButton(controller.colorSelected); + Tooltip.install(controller.colorSelected, new Tooltip("Colour")); + + initializeSelectDependentToolbarButton(controller.deleteSelected); + Tooltip.install(controller.deleteSelected, new Tooltip("Delete")); + + initializeToolbarButton(controller.undo); + initializeToolbarButton(controller.redo); + initializeUndoRedoButtons(); + } + + public EditorController getController() { + return controller; + } + + private void initializeSelectDependentToolbarButton(final JFXRippler button) { + initializeToolbarButton(button); + + // The color button should only be enabled when an element is selected + SelectHelper.getSelectedElements().addListener(new ListChangeListener() { + @Override + public void onChanged(final Change c) { + if (SelectHelper.getSelectedElements().size() > 0) { + button.setEnabled(true); + + final FadeTransition fadeAnimation = new FadeTransition(Duration.millis(100), button); + fadeAnimation.setFromValue(button.getOpacity()); + fadeAnimation.setToValue(1); + fadeAnimation.play(); + } else { + button.setEnabled(false); + + final FadeTransition fadeAnimation = new FadeTransition(Duration.millis(100), button); + fadeAnimation.setFromValue(1); + fadeAnimation.setToValue(0.3); + fadeAnimation.play(); + } + } + }); + + // Disable the button + button.setEnabled(false); + button.setOpacity(0.3); + } + + private void initializeToolbarButton(final JFXRippler button) { + final Color color = Color.GREY_BLUE; + final Color.Intensity colorIntensity = Color.Intensity.I800; + + button.setMaskType(JFXRippler.RipplerMask.CIRCLE); + button.setRipplerFill(color.getTextColor(colorIntensity)); + button.setPosition(JFXRippler.RipplerPos.BACK); + } + + private void initializeUndoRedoButtons() { + UndoRedoStack.canUndoProperty().addListener((obs, oldState, newState) -> { + if (newState) { + // Enable the undo button + controller.undo.setEnabled(true); + controller.undo.setOpacity(1); + } else { + // Disable the undo button + controller.undo.setEnabled(false); + controller.undo.setOpacity(0.3); + } + }); + + UndoRedoStack.canRedoProperty().addListener((obs, oldState, newState) -> { + if (newState) { + // Enable the redo button + controller.redo.setEnabled(true); + controller.redo.setOpacity(1); + } else { + // Disable the redo button + controller.redo.setEnabled(false); + controller.redo.setOpacity(0.3); + } + }); + + // Disable the undo button + controller.undo.setEnabled(false); + controller.undo.setOpacity(0.3); + + // Disable the redo button + controller.redo.setEnabled(false); + controller.redo.setOpacity(0.3); + + // Set tooltips + Tooltip.install(controller.undo, new Tooltip("Undo")); + Tooltip.install(controller.redo, new Tooltip("Redo")); + } + + private void initializeColorSelector() { + final JFXPopup popup = new JFXPopup(); + + final double listWidth = 136; + final FlowPane list = new FlowPane(); + for (final EnabledColor color : enabledColors) { + final Circle circle = new Circle(16, color.color.getColor(color.intensity)); + circle.setStroke(color.color.getColor(color.intensity.next(2))); + circle.setStrokeWidth(1); + + final Label label = new Label(color.keyCode.getName()); + label.getStyleClass().add("subhead"); + label.setTextFill(color.color.getTextColor(color.intensity)); + + final StackPane child = new StackPane(circle, label); + child.setMinSize(40, 40); + child.setMaxSize(40, 40); + + child.setOnMouseEntered(event -> { + final ScaleTransition scaleTransition = new ScaleTransition(Duration.millis(100), circle); + scaleTransition.setFromX(circle.getScaleX()); + scaleTransition.setFromY(circle.getScaleY()); + scaleTransition.setToX(1.1); + scaleTransition.setToY(1.1); + scaleTransition.play(); + }); + + child.setOnMouseExited(event -> { + final ScaleTransition scaleTransition = new ScaleTransition(Duration.millis(100), circle); + scaleTransition.setFromX(circle.getScaleX()); + scaleTransition.setFromY(circle.getScaleY()); + scaleTransition.setToX(1.0); + scaleTransition.setToY(1.0); + scaleTransition.play(); + }); + + child.setOnMouseClicked(event -> { + final List> previousColor = new ArrayList<>(); + + SelectHelper.getSelectedElements().forEach(selectable -> { + previousColor.add(new Pair<>(selectable, new EnabledColor(selectable.getColor(), selectable.getColorIntensity()))); + }); + + controller.changeColorOnSelectedElements(color, previousColor); + + popup.hide(); + SelectHelper.clearSelectedElements(); + }); + + list.getChildren().add(child); + } + list.setMinWidth(listWidth); + list.setMaxWidth(listWidth); + list.setStyle("-fx-background-color: white; -fx-padding: 8;"); + + popup.setPopupContent(list); + + controller.colorSelected.setOnMouseClicked((e) -> { + // If nothing is selected + if (SelectHelper.getSelectedElements().size() == 0) return; + popup.show(controller.colorSelected, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, -10, 15); + }); + } + + private void initializeToolbar() { + final Color color = Color.GREY_BLUE; + final Color.Intensity intensity = Color.Intensity.I700; + + // Set the background for the top toolbar + controller.toolbar.setBackground( + new Background(new BackgroundFill(color.getColor(intensity), + CornerRadii.EMPTY, + Insets.EMPTY) + )); + } +} diff --git a/src/main/java/ecdar/presentations/FilePresentation.java b/src/main/java/ecdar/presentations/FilePresentation.java index 3592e951..fb21d4ba 100644 --- a/src/main/java/ecdar/presentations/FilePresentation.java +++ b/src/main/java/ecdar/presentations/FilePresentation.java @@ -132,7 +132,7 @@ private void initializeColors() { private ArrayList getActiveComponents() { ArrayList activeComponents = new ArrayList<>(); - Node canvasPaneFirstChild = Ecdar.getPresentation().getController().canvasPane.getChildren().get(0); + Node canvasPaneFirstChild = Ecdar.getPresentation().getController().getEditorPresentation().getController().canvasPane.getChildren().get(0); if(canvasPaneFirstChild instanceof GridPane) { for (Node child : ((GridPane) canvasPaneFirstChild).getChildren()) { activeComponents.add(((CanvasPresentation) child).getController().getActiveModel()); diff --git a/src/main/java/ecdar/presentations/RightSimPanePresentation.java b/src/main/java/ecdar/presentations/RightSimPanePresentation.java index 729b8829..48c79f1c 100755 --- a/src/main/java/ecdar/presentations/RightSimPanePresentation.java +++ b/src/main/java/ecdar/presentations/RightSimPanePresentation.java @@ -34,7 +34,7 @@ private void initializeBackground() { * Initializes the thin border on the left side of the querypane toolbar */ private void initializeLeftBorder() { - controller.queryPaneElement.getController().toolbar.setBorder(new Border(new BorderStroke( + setBorder(new Border(new BorderStroke( Color.GREY_BLUE.getColor(Color.Intensity.I900), BorderStrokeStyle.SOLID, CornerRadii.EMPTY, diff --git a/src/main/java/ecdar/controllers/SimEdgePresentation.java b/src/main/java/ecdar/presentations/SimEdgePresentation.java similarity index 100% rename from src/main/java/ecdar/controllers/SimEdgePresentation.java rename to src/main/java/ecdar/presentations/SimEdgePresentation.java diff --git a/src/main/java/ecdar/controllers/SimLocationPresentation.java b/src/main/java/ecdar/presentations/SimLocationPresentation.java similarity index 97% rename from src/main/java/ecdar/controllers/SimLocationPresentation.java rename to src/main/java/ecdar/presentations/SimLocationPresentation.java index 7d87e377..e250433a 100755 --- a/src/main/java/ecdar/controllers/SimLocationPresentation.java +++ b/src/main/java/ecdar/presentations/SimLocationPresentation.java @@ -9,7 +9,9 @@ import ecdar.utility.helpers.SelectHelper; import javafx.animation.*; import javafx.beans.binding.DoubleBinding; -import javafx.beans.property.*; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleDoubleProperty; import javafx.scene.Group; import javafx.scene.control.Label; import javafx.scene.effect.DropShadow; diff --git a/src/main/java/ecdar/presentations/SimNailPresentation.java b/src/main/java/ecdar/presentations/SimNailPresentation.java new file mode 100755 index 00000000..250f2f9d --- /dev/null +++ b/src/main/java/ecdar/presentations/SimNailPresentation.java @@ -0,0 +1,258 @@ +package ecdar.presentations; + +import ecdar.abstractions.Component; +import ecdar.abstractions.Edge; +import ecdar.abstractions.EdgeStatus; +import ecdar.abstractions.Nail; +import ecdar.controllers.SimEdgeController; +import ecdar.controllers.SimNailController; +import ecdar.utility.Highlightable; +import ecdar.utility.colors.Color; +import ecdar.utility.helpers.BindingHelper; +import javafx.animation.Interpolator; +import javafx.animation.KeyFrame; +import javafx.animation.KeyValue; +import javafx.animation.Timeline; +import javafx.scene.Group; +import javafx.scene.control.Label; +import javafx.scene.shape.Line; + +import java.util.function.Consumer; + +import static javafx.util.Duration.millis; + +/** + * The presentation for the nail shown on a {@link SimEdgePresentation} in the {@link SimulatorOverviewPresentation}
+ * This class should be refactored such that code which are duplicated from {@link NailPresentation} + * have its own base class. + */ +public class SimNailPresentation extends Group implements Highlightable { + + public static final double COLLAPSED_RADIUS = 2d; + public static final double HOVERED_RADIUS = 7d; + + private final SimNailController controller; + private final Timeline shakeAnimation = new Timeline(); + + /** + * Constructs a nail which is ready to be displayed in the simulator + * @param nail the nail which should be presented + * @param edge the edge where the nail belongs to + * @param component the component where the nail belongs + * @param simEdgeController the controller of the edge where the nail belongs to + */ + public SimNailPresentation(final Nail nail, final Edge edge, final Component component, final SimEdgeController simEdgeController) { + controller = new EcdarFXMLLoader().loadAndGetController("SimNailPresentation.fxml", this); + + // Bind the component with the one of the controller + controller.setComponent(component); + + // Bind the edge with the one of the controller + controller.setEdge(edge); + // Bind the nail with the one of the controller + controller.setNail(nail); + + controller.setEdgeController(simEdgeController); + + initializeNailCircleColor(); + initializePropertyTag(); + initializeRadius(); + initializeShakeAnimation(); + } + + /** + * Sets the radius of the nail + */ + private void initializeRadius() { + final Consumer radiusUpdater = (propertyType) -> { + if(!propertyType.equals(Edge.PropertyType.NONE)) { + controller.getNail().setRadius(SimNailPresentation.HOVERED_RADIUS); + } + }; + controller.getNail().propertyTypeProperty().addListener((observable, oldValue, newValue) -> { + radiusUpdater.accept(newValue); + }); + radiusUpdater.accept(controller.getNail().getPropertyType()); + } + + /** + * Initializes the text / PropertyTag shown on a {@link SimTagPresentation} on the nail. + */ + private void initializePropertyTag() { + final SimTagPresentation propertyTag = controller.propertyTag; + final Line propertyTagLine = controller.propertyTagLine; + propertyTag.setComponent(controller.getComponent()); + propertyTag.setLocationAware(controller.getNail()); + + // Bind the line to the tag + BindingHelper.bind(propertyTagLine, propertyTag); + + // Bind the color of the tag to the color of the component + propertyTag.bindToColor(controller.getComponent().colorProperty(), controller.getComponent().colorIntensityProperty()); + + // Updates visibility and placeholder of the tag depending on the type of nail + final Consumer updatePropertyType = (propertyType) -> { + + // If it is not a property nail hide the tag otherwise show it and write proper placeholder + if(propertyType.equals(Edge.PropertyType.NONE)) { + propertyTag.setVisible(false); + } else { + + // Show the property tag since the nail is a property nail + propertyTag.setVisible(true); + + // Set and bind the location of the property tag + if((controller.getNail().getPropertyX() != 0) && (controller.getNail().getPropertyY() != 0)) { + propertyTag.setTranslateX(controller.getNail().getPropertyX()); + propertyTag.setTranslateY(controller.getNail().getPropertyY()); + } + controller.getNail().propertyXProperty().bind(propertyTag.translateXProperty()); + controller.getNail().propertyYProperty().bind(propertyTag.translateYProperty()); + + final Label propertyLabel = controller.propertyLabel; + + if(propertyType.equals(Edge.PropertyType.SELECTION)) { + propertyLabel.setText(":"); + propertyLabel.setTranslateX(-3); + propertyLabel.setTranslateY(-8); + propertyTag.setAndBindString(controller.getEdge().selectProperty()); + } else if(propertyType.equals(Edge.PropertyType.GUARD)) { + propertyLabel.setText("<"); + propertyLabel.setTranslateX(-3); + propertyLabel.setTranslateY(-7); + propertyTag.setAndBindString(controller.getEdge().guardProperty()); + } else if(propertyType.equals(Edge.PropertyType.SYNCHRONIZATION)) { + updateSyncLabel(); + propertyLabel.setTranslateX(-3); + propertyLabel.setTranslateY(-7); + propertyTag.setAndBindString(controller.getEdge().syncProperty()); + } else if(propertyType.equals(Edge.PropertyType.UPDATE)) { + propertyLabel.setText("="); + propertyLabel.setTranslateX(-3); + propertyLabel.setTranslateY(-7); + propertyTag.setAndBindString(controller.getEdge().updateProperty()); + } + + //Disable the ability to edit the tag if the nails edge is locked + if(controller.getEdge().getIsLockedProperty().getValue()){ + propertyTag.setDisabledText(true); + } + } + }; + + // Whenever the property type updates, update the tag + controller.getNail().propertyTypeProperty().addListener((obs, oldPropertyType, newPropertyType) -> { + updatePropertyType.accept(newPropertyType); + }); + + // Whenever the edge changes I/O status, if sync nail then update its label + controller.getEdge().ioStatus.addListener((observable, oldValue, newValue) -> { + if (controller.getNail().getPropertyType().equals(Edge.PropertyType.SYNCHRONIZATION)) + updateSyncLabel(); + }); + + // Update the tag initially + updatePropertyType.accept(controller.getNail().getPropertyType()); + } + + /** + * Updates the synchronization label and tag. + * The update depends on the edge I/O status. + */ + private void updateSyncLabel() { + final Label propertyLabel = controller.propertyLabel; + + // show ? or ! dependent on edge I/O status + if (controller.getEdge().ioStatus.get().equals(EdgeStatus.INPUT)) { + propertyLabel.setText("?"); + } else { + propertyLabel.setText("!"); + } + } + + /** + * Set up Listeners for updating the color of the nail, set the color initially + */ + private void initializeNailCircleColor() { + final Runnable updateNailColor = () -> { + final Color color = controller.getComponent().getColor(); + final Color.Intensity colorIntensity = controller.getComponent().getColorIntensity(); + + if(!controller.getNail().getPropertyType().equals(Edge.PropertyType.NONE)) { + controller.nailCircle.setFill(color.getColor(colorIntensity)); + controller.nailCircle.setStroke(color.getColor(colorIntensity.next(2))); + } else { + controller.nailCircle.setFill(Color.GREY_BLUE.getColor(Color.Intensity.I800)); + controller.nailCircle.setStroke(Color.GREY_BLUE.getColor(Color.Intensity.I900)); + } + }; + + // When the color of the component updates, update the nail indicator as well + controller.getComponent().colorProperty().addListener((observable) -> updateNailColor.run()); + + // When the color intensity of the component updates, update the nail indicator + controller.getComponent().colorIntensityProperty().addListener((observable) -> updateNailColor.run()); + + // Initialize the color of the nail + updateNailColor.run(); + } + + /** + * Initializes a shake animation found in {@link SimNailPresentation#shakeAnimation} which can + * be played using {@link SimNailPresentation#shake()} + */ + private void initializeShakeAnimation() { + final Interpolator interpolator = Interpolator.SPLINE(0.645, 0.045, 0.355, 1); + + final double startX = controller.root.getTranslateX(); + final KeyValue kv1 = new KeyValue(controller.root.translateXProperty(), startX - 3, interpolator); + final KeyValue kv2 = new KeyValue(controller.root.translateXProperty(), startX + 3, interpolator); + final KeyValue kv3 = new KeyValue(controller.root.translateXProperty(), startX, interpolator); + + final KeyFrame kf1 = new KeyFrame(millis(50), kv1); + final KeyFrame kf2 = new KeyFrame(millis(100), kv2); + final KeyFrame kf3 = new KeyFrame(millis(150), kv1); + final KeyFrame kf4 = new KeyFrame(millis(200), kv2); + final KeyFrame kf5 = new KeyFrame(millis(250), kv3); + + shakeAnimation.getKeyFrames().addAll(kf1, kf2, kf3, kf4, kf5); + } + + /** + * Plays the {@link SimNailPresentation#shakeAnimation} + */ + public void shake() { + shakeAnimation.play(); + } + + /** + * Highlights the nail + */ + @Override + public void highlight() { + final Color color = Color.DEEP_ORANGE; + final Color.Intensity intensity = Color.Intensity.I500; + + // Set the color + controller.nailCircle.setFill(color.getColor(intensity)); + controller.nailCircle.setStroke(color.getColor(intensity.next(2))); + } + + /** + * Removes the highlight from the nail + */ + @Override + public void unhighlight() { + Color color = Color.GREY_BLUE; + Color.Intensity intensity = Color.Intensity.I800; + + // Set the color + if(!controller.getNail().getPropertyType().equals(Edge.PropertyType.NONE)) { + color = controller.getComponent().getColor(); + intensity = controller.getComponent().getColorIntensity(); + } + + controller.nailCircle.setFill(color.getColor(intensity)); + controller.nailCircle.setStroke(color.getColor(intensity.next(2))); + } +} diff --git a/src/main/java/ecdar/presentations/SimTagPresentation.java b/src/main/java/ecdar/presentations/SimTagPresentation.java new file mode 100755 index 00000000..d2916ee1 --- /dev/null +++ b/src/main/java/ecdar/presentations/SimTagPresentation.java @@ -0,0 +1,186 @@ +package ecdar.presentations; + +import ecdar.abstractions.Component; +import ecdar.utility.colors.Color; +import ecdar.utility.helpers.LocationAware; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.StringProperty; +import javafx.geometry.Insets; +import javafx.scene.control.Label; +import javafx.scene.layout.StackPane; +import javafx.scene.shape.LineTo; +import javafx.scene.shape.MoveTo; +import javafx.scene.shape.Path; + +import java.util.function.BiConsumer; + +import static ecdar.presentations.Grid.GRID_SIZE; + +/** + * The presentation for the tag shown on a {@link SimEdgePresentation} in the {@link SimulatorOverviewPresentation}
+ * This class should be refactored such that code which are duplicated from {@link TagPresentation} + * have its own base class. + */ +public class SimTagPresentation extends StackPane { + + private final static Color backgroundColor = Color.GREY; + private final static Color.Intensity backgroundColorIntensity = Color.Intensity.I50; + + private final ObjectProperty component = new SimpleObjectProperty<>(null); + private final ObjectProperty locationAware = new SimpleObjectProperty<>(null); + + private LineTo l2; + private LineTo l3; + + private static double TAG_HEIGHT = 1.6 * GRID_SIZE; + + /** + * Constructs the {@link SimTagPresentation} + */ + public SimTagPresentation() { + new EcdarFXMLLoader().loadAndGetController("SimTagPresentation.fxml", this); + initializeShape(); + initializeLabel(); + initializeMouseTransparency(); + } + + private void initializeMouseTransparency() { + mouseTransparentProperty().bind(opacityProperty().isEqualTo(0, 0.00f)); + } + + /** + * Initializes the label which shows the property + */ + private void initializeLabel() { + final Label label = (Label) lookup("#label"); + final Path shape = (Path) lookup("#shape"); + + final Insets insets = new Insets(0,2,0,2); + label.setPadding(insets); + + final int padding = 0; + + label.layoutBoundsProperty().addListener((obs, oldBounds, newBounds) -> { + double newWidth = Math.max(newBounds.getWidth(), 10); + final double res = GRID_SIZE * 2 - (newWidth % (GRID_SIZE * 2)); + newWidth += res; + + l2.setX(newWidth + padding); + l3.setX(newWidth + padding); + + setMinWidth(newWidth + padding); + setMaxWidth(newWidth + padding); + + label.focusedProperty().addListener((observable, oldFocused, newFocused) -> { + if (newFocused) { + shape.setTranslateY(2); + label.setTranslateY(2); + } + }); + + if (getWidth() >= 1000) { + setWidth(newWidth); + setHeight(TAG_HEIGHT); + shape.setTranslateY(-1); + } + + // Fixes the jumping of the shape when the text field is empty + if (label.getText().isEmpty()) { + shape.setLayoutX(0); + } + }); + } + + /** + * Initialize the shape which is around the property label + */ + private void initializeShape() { + final int WIDTH = 5000; + final double HEIGHT = TAG_HEIGHT; + + final Path shape = (Path) lookup("#shape"); + + final MoveTo start = new MoveTo(0, 0); + + l2 = new LineTo(WIDTH, 0); + l3 = new LineTo(WIDTH, HEIGHT); + final LineTo l4 = new LineTo(0, HEIGHT); + final LineTo l6 = new LineTo(0, 0); + + shape.getElements().addAll(start, l2, l3, l4, l6); + shape.setFill(backgroundColor.getColor(backgroundColorIntensity)); + shape.setStroke(backgroundColor.getColor(backgroundColorIntensity.next(4))); + } + + public void bindToColor(final ObjectProperty color, final ObjectProperty intensity) { + bindToColor(color, intensity, false); + } + + public void bindToColor(final ObjectProperty color, final ObjectProperty intensity, final boolean doColorBackground) { + final BiConsumer recolor = (newColor, newIntensity) -> { + if (doColorBackground) { + final Path shape = (Path) lookup("#shape"); + shape.setFill(newColor.getColor(newIntensity.next(-1))); + shape.setStroke(newColor.getColor(newIntensity.next(-1).next(2))); + } + }; + + color.addListener(observable -> recolor.accept(color.get(), intensity.get())); + intensity.addListener(observable -> recolor.accept(color.get(), intensity.get())); + recolor.accept(color.get(), intensity.get()); + } + + /** + * Updates the label with the given string and binds updates from the label on the given {@link StringProperty} + * @param string the string to set and bind to + */ + public void setAndBindString(final StringProperty string) { + final Label label = (Label) lookup("#label"); + + label.textProperty().unbind(); + label.setText(string.get()); + string.bind(label.textProperty()); + } + + /** + * Replaces spaces with underscores in the label + */ +/* public void replaceSpace() { + initializeTextAid(); + }*/ + + public Component getComponent() { + return component.get(); + } + + public void setComponent(final Component component) { + this.component.set(component); + } + + public ObjectProperty componentProperty() { + return component; + } + + public LocationAware getLocationAware() { + return locationAware.get(); + } + + public ObjectProperty locationAwareProperty() { + return locationAware; + } + + public void setLocationAware(LocationAware locationAware) { + this.locationAware.set(locationAware); + } + + /** + * Sets the disabled property in the label + * @param bool true --- the label will be disabled + * false --- the label will be enabled + */ + public void setDisabledText(boolean bool){ + final Label label = (Label) lookup("#label"); + label.setDisable(true); + } +} diff --git a/src/main/java/ecdar/utility/helpers/BindingHelper.java b/src/main/java/ecdar/utility/helpers/BindingHelper.java index afbddec2..89d1a5e6 100644 --- a/src/main/java/ecdar/utility/helpers/BindingHelper.java +++ b/src/main/java/ecdar/utility/helpers/BindingHelper.java @@ -5,6 +5,7 @@ import ecdar.model_canvas.arrow_heads.ChannelReceiverArrowHead; import ecdar.model_canvas.arrow_heads.ChannelSenderArrowHead; import ecdar.presentations.Link; +import ecdar.presentations.SimTagPresentation; import ecdar.presentations.TagPresentation; import ecdar.utility.mouse.MouseTracker; import javafx.beans.binding.DoubleBinding; @@ -24,6 +25,15 @@ public static void bind(final Line subject, final TagPresentation target) { subject.visibleProperty().bind(target.visibleProperty()); } + public static void bind(final Line subject, final SimTagPresentation target) { + subject.startXProperty().set(0); + subject.startYProperty().set(0); + subject.endXProperty().bind(target.translateXProperty().add(target.minWidthProperty().divide(2))); + subject.endYProperty().bind(target.translateYProperty().add(target.heightProperty().divide(2))); + subject.opacityProperty().bind(target.opacityProperty()); + subject.visibleProperty().bind(target.visibleProperty()); + } + public static void bind(final Circular subject, final ObservableDoubleValue x, final ObservableDoubleValue y) { subject.xProperty().bind(EcdarController.getActiveCanvasPresentation().mouseTracker.gridXProperty().subtract(x)); subject.yProperty().bind(EcdarController.getActiveCanvasPresentation().mouseTracker.gridYProperty().subtract(y)); diff --git a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml index 29bb1938..97de6ab5 100644 --- a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml +++ b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml @@ -10,7 +10,6 @@ -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
@@ -205,7 +139,7 @@ - + @@ -245,6 +179,18 @@ + + + + + + + + + + + + diff --git a/src/main/resources/ecdar/presentations/EditorPresentation.fxml b/src/main/resources/ecdar/presentations/EditorPresentation.fxml new file mode 100644 index 00000000..bf3faac1 --- /dev/null +++ b/src/main/resources/ecdar/presentations/EditorPresentation.fxml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml b/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml index de2b3c4c..3ed05348 100755 --- a/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml +++ b/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml @@ -2,7 +2,6 @@ - - + diff --git a/src/main/resources/ecdar/presentations/SimEdgePresentation.fxml b/src/main/resources/ecdar/presentations/SimEdgePresentation.fxml new file mode 100755 index 00000000..1c72435f --- /dev/null +++ b/src/main/resources/ecdar/presentations/SimEdgePresentation.fxml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/ecdar/presentations/SimLocationPresentation.fxml b/src/main/resources/ecdar/presentations/SimLocationPresentation.fxml new file mode 100755 index 00000000..eb6b4543 --- /dev/null +++ b/src/main/resources/ecdar/presentations/SimLocationPresentation.fxml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/ecdar/presentations/SimNailPresentation.fxml b/src/main/resources/ecdar/presentations/SimNailPresentation.fxml new file mode 100755 index 00000000..17a63895 --- /dev/null +++ b/src/main/resources/ecdar/presentations/SimNailPresentation.fxml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/ecdar/presentations/SimTagPresentation.fxml b/src/main/resources/ecdar/presentations/SimTagPresentation.fxml new file mode 100755 index 00000000..f9e40d71 --- /dev/null +++ b/src/main/resources/ecdar/presentations/SimTagPresentation.fxml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file From e0a510911cb4b520ba2c90f967365b66e5a2544b Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Wed, 13 Jul 2022 12:14:11 +0200 Subject: [PATCH 003/120] WIP: Design updated for simulator and side panes logic/naming updated to account for having either the editor or the simulator view active --- src/main/java/ecdar/Ecdar.java | 6 +- .../ecdar/controllers/EcdarController.java | 51 +++++--- .../controllers/ProjectPaneController.java | 3 +- .../controllers/SimulatorController.java | 12 +- .../TracePaneElementController.java | 9 +- .../TransitionPaneElementController.java | 20 ++- .../presentations/EcdarPresentation.java | 122 ++++++++++-------- .../TracePaneElementPresentation.java | 2 +- .../TransitionPaneElementPresentation.java | 2 +- .../presentations/EcdarPresentation.fxml | 14 +- .../LeftSimPanePresentation.fxml | 16 +-- .../ProjectPanePresentation.fxml | 3 +- .../presentations/QueryPanePresentation.fxml | 3 +- .../presentations/SimulatorPresentation.fxml | 64 ++------- .../TracePaneElementPresentation.fxml | 66 +++++----- .../TransitionPaneElementPresentation.fxml | 80 +++++------- 16 files changed, 208 insertions(+), 265 deletions(-) diff --git a/src/main/java/ecdar/Ecdar.java b/src/main/java/ecdar/Ecdar.java index 6a6801f0..d4d7ee18 100644 --- a/src/main/java/ecdar/Ecdar.java +++ b/src/main/java/ecdar/Ecdar.java @@ -148,8 +148,8 @@ public static void showHelp() { presentation.showHelp(); } - public static BooleanProperty toggleFilePane() { - return presentation.toggleFilePane(); + public static BooleanProperty toggleLeftPane() { + return presentation.toggleLeftPane(); } /** @@ -174,7 +174,7 @@ public static BooleanProperty toggleRunBackgroundQueries() { } public static BooleanProperty toggleQueryPane() { - return presentation.toggleQueryPane(); + return presentation.toggleRightPane(); } /** diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index 806b040c..041485cb 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -60,8 +60,6 @@ public class EcdarController implements Initializable { public StackPane leftPane; public StackPane rightPane; public Rectangle bottomFillerElement; - public QueryPanePresentation queryPane; - public ProjectPanePresentation filePane; public MessageTabPanePresentation messageTabPane; public StackPane dialogContainer; public JFXDialog dialog; @@ -91,7 +89,7 @@ public class EcdarController implements Initializable { // The program top menu public MenuBar menuBar; - public MenuItem menuBarViewFilePanel; + public MenuItem menuBarViewProjectPanel; public MenuItem menuBarViewQueryPanel; public MenuItem menuBarViewGrid; public MenuItem menuBarViewAutoscaling; @@ -162,7 +160,12 @@ private enum Mode { private static final ObjectProperty currentMode = new SimpleObjectProperty<>(Mode.Editor); private static final EditorPresentation editorPresentation = new EditorPresentation(); + public final ProjectPanePresentation projectPane = new ProjectPanePresentation(); + public final QueryPanePresentation queryPane = new QueryPanePresentation(); + private static final SimulatorPresentation simulatorPresentation = new SimulatorPresentation(); + public final LeftSimPanePresentation leftSimPane = new LeftSimPanePresentation(); + public final RightSimPanePresentation rightSimPane = new RightSimPanePresentation(); @Override public void initialize(final URL location, final ResourceBundle resources) { @@ -173,14 +176,18 @@ public void initialize(final URL location, final ResourceBundle resources) { startBackgroundQueriesThread(); // Will terminate immediately if background queries are turned off bottomFillerElement.heightProperty().bind(messageTabPane.maxHeightProperty()); - messageTabPane.getController().setRunnableForOpeningAndClosingMessageTabPane(this::changeInsetsOfFileAndQueryPanes); + messageTabPane.getController().setRunnableForOpeningAndClosingMessageTabPane(this::changeInsetsOfProjectAndQueryPanes); // Update file coloring when active model changes - editorPresentation.getController().getActiveCanvasPresentation().getController().activeComponentProperty().addListener(observable -> filePane.getController().updateColorsOnFilePresentations()); - editorPresentation.getController().activeCanvasPresentationProperty().addListener(observable -> filePane.getController().updateColorsOnFilePresentations()); + editorPresentation.getController().getActiveCanvasPresentation().getController().activeComponentProperty().addListener(observable -> projectPane.getController().updateColorsOnFilePresentations()); + editorPresentation.getController().activeCanvasPresentationProperty().addListener(observable -> projectPane.getController().updateColorsOnFilePresentations()); - borderPane.centerProperty().addListener((observable, oldValue, newValue) -> System.out.println(newValue.getClass())); - borderPane.setCenter(editorPresentation); + leftSimPane.minWidthProperty().bind(projectPane.minWidthProperty()); + leftSimPane.maxWidthProperty().bind(projectPane.maxWidthProperty()); + rightSimPane.minWidthProperty().bind(queryPane.minWidthProperty()); + rightSimPane.maxWidthProperty().bind(queryPane.maxWidthProperty()); + + enterEditorMode(); } public StackPane getCenter() { @@ -304,7 +311,7 @@ private void initializeDialog(JFXDialog dialog, StackPane dialogContainer) { dialogContainer.setMouseTransparent(false); }); - filePane.getStyleClass().add("responsive-pane-sizing"); + projectPane.getStyleClass().add("responsive-pane-sizing"); queryPane.getStyleClass().add("responsive-pane-sizing"); initializeKeybindings(); @@ -594,11 +601,11 @@ private void initializeEditMenu() { * Initialize the View menu. */ private void initializeViewMenu() { - menuBarViewFilePanel.getGraphic().setOpacity(1); - menuBarViewFilePanel.setAccelerator(new KeyCodeCombination(KeyCode.P, KeyCodeCombination.SHORTCUT_DOWN)); - menuBarViewFilePanel.setOnAction(event -> { - final BooleanProperty isOpen = Ecdar.toggleFilePane(); - menuBarViewFilePanel.getGraphic().opacityProperty().bind(new When(isOpen).then(1).otherwise(0)); + menuBarViewProjectPanel.getGraphic().setOpacity(1); + menuBarViewProjectPanel.setAccelerator(new KeyCodeCombination(KeyCode.P, KeyCodeCombination.SHORTCUT_DOWN)); + menuBarViewProjectPanel.setOnAction(event -> { + final BooleanProperty isOpen = Ecdar.toggleLeftPane(); + menuBarViewProjectPanel.getGraphic().opacityProperty().bind(new When(isOpen).then(1).otherwise(0)); }); menuBarViewQueryPanel.getGraphic().setOpacity(0); @@ -975,6 +982,10 @@ private void enterEditorMode() { // simulatorPresentation.getController().willHide(); borderPane.setCenter(editorPresentation); + leftPane.getChildren().clear(); + leftPane.getChildren().add(projectPane); + rightPane.getChildren().clear(); + rightPane.getChildren().add(queryPane); // Enable or disable the menu items that can be used when in the simulator // updateMenuItems(); @@ -990,6 +1001,10 @@ private void enterSimulatorMode() { // simulatorPresentation.getController().willShow(); borderPane.setCenter(simulatorPresentation); + leftPane.getChildren().clear(); + leftPane.getChildren().add(leftSimPane); + rightPane.getChildren().clear(); + rightPane.getChildren().add(rightSimPane); // Enable or disable the menu items that can be used when in the simulator // updateMenuItems(); @@ -1157,15 +1172,15 @@ private static int getAutoCropRightX(final BufferedImage image) { } /** - * This method is used to push the contents of the file and query panes when the tab pane is opened + * This method is used to push the contents of the project and query panes when the tab pane is opened */ - private void changeInsetsOfFileAndQueryPanes() { + private void changeInsetsOfProjectAndQueryPanes() { if (messageTabPane.getController().isOpen()) { - filePane.showBottomInset(false); + projectPane.showBottomInset(false); queryPane.showBottomInset(false); CanvasPresentation.showBottomInset(false); } else { - filePane.showBottomInset(true); + projectPane.showBottomInset(true); queryPane.showBottomInset(true); CanvasPresentation.showBottomInset(true); } diff --git a/src/main/java/ecdar/controllers/ProjectPaneController.java b/src/main/java/ecdar/controllers/ProjectPaneController.java index 0e339900..dde161ae 100644 --- a/src/main/java/ecdar/controllers/ProjectPaneController.java +++ b/src/main/java/ecdar/controllers/ProjectPaneController.java @@ -12,6 +12,7 @@ import com.jfoenix.controls.JFXPopup; import com.jfoenix.controls.JFXRippler; import com.jfoenix.controls.JFXTextArea; +import javafx.application.Platform; import javafx.collections.ListChangeListener; import javafx.fxml.FXML; import javafx.fxml.Initializable; @@ -265,7 +266,7 @@ private void handleRemovedModel(final HighLevelModelObject model) { public void updateColorsOnFilePresentations() { for (Node child : filesList.getChildren()) { if (child instanceof FilePresentation) { - ((FilePresentation) child).updateColors(); + Platform.runLater(() -> ((FilePresentation) child).updateColors()); } } } diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index 56cc1b3f..1f00fcd3 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -19,22 +19,16 @@ import java.net.URL; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.ResourceBundle; public class SimulatorController implements Initializable { - public StackPane root; public SimulatorOverviewPresentation overviewPresentation; public StackPane toolbar; - public Label rightPaneFillerElement; - public Label leftPaneFillerElement; - public Rectangle bottomFillerElement; - public RightSimPanePresentation rightSimPane; - public LeftSimPanePresentation leftSimPane; - private Declarations systemDeclarations; - private boolean firstTimeInSimulator; + private boolean firstTimeInSimulator; private final static DoubleProperty width = new SimpleDoubleProperty(), height = new SimpleDoubleProperty(); private static ObjectProperty selectedTransition = new SimpleObjectProperty<>(); @@ -62,7 +56,7 @@ public void willShow() { shouldSimulationBeReset = false; } - if (!firstTimeInSimulator && !overviewPresentation.getController().getComponentObservableList() + if (!firstTimeInSimulator && !new HashSet<>(overviewPresentation.getController().getComponentObservableList()) .containsAll(findComponentsInCurrentSimulation())) { shouldSimulationBeReset = true; } diff --git a/src/main/java/ecdar/controllers/TracePaneElementController.java b/src/main/java/ecdar/controllers/TracePaneElementController.java index 6f0bf4ec..a733b15f 100755 --- a/src/main/java/ecdar/controllers/TracePaneElementController.java +++ b/src/main/java/ecdar/controllers/TracePaneElementController.java @@ -14,6 +14,7 @@ import javafx.fxml.Initializable; import javafx.scene.control.Label; import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import org.kordamp.ikonli.javafx.FontIcon; @@ -26,11 +27,11 @@ * The controller class for the trace pane element that can be inserted into a simulator pane */ public class TracePaneElementController implements Initializable { - public AnchorPane toolbar; + public VBox root; + public HBox toolbar; public Label traceTitle; public JFXRippler expandTrace; public VBox traceList; - public VBox traceVbox; public FontIcon expandTraceIcon; public AnchorPane traceSummary; public Label summaryTitleLabel; @@ -87,7 +88,7 @@ private void initializeTraceExpand() { */ private void hideTrace() { traceList.getChildren().clear(); - traceVbox.getChildren().add(traceSummary); + root.getChildren().add(traceSummary); } /** @@ -98,7 +99,7 @@ private void showTrace() { transitionPresentationMap.forEach((state, presentation) -> { insertTraceState(state, false); }); - traceVbox.getChildren().remove(traceSummary); + root.getChildren().remove(traceSummary); } /** diff --git a/src/main/java/ecdar/controllers/TransitionPaneElementController.java b/src/main/java/ecdar/controllers/TransitionPaneElementController.java index 5dc6dbde..8a985a92 100755 --- a/src/main/java/ecdar/controllers/TransitionPaneElementController.java +++ b/src/main/java/ecdar/controllers/TransitionPaneElementController.java @@ -15,7 +15,7 @@ import javafx.fxml.Initializable; import javafx.scene.control.Label; import javafx.scene.control.Tooltip; -import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import org.kordamp.ikonli.javafx.FontIcon; @@ -29,15 +29,14 @@ * The controller class for the transition pane element that can be inserted into the simulator panes */ public class TransitionPaneElementController implements Initializable { - public AnchorPane root; - public VBox paneElementVbox; + public VBox root; public VBox transitionList; - public AnchorPane toolbar; + public HBox toolbar; public Label toolbarTitle; public JFXRippler refreshRippler; public JFXRippler expandTransition; public FontIcon expandTransitionIcon; - public AnchorPane delayChooser; + public VBox delayChooser; public JFXTextField delayTextField; private SimpleBooleanProperty isTransitionExpanded = new SimpleBooleanProperty(false); @@ -48,9 +47,7 @@ public class TransitionPaneElementController implements Initializable { public void initialize(URL location, ResourceBundle resources) { Ecdar.getSimulationHandler().availableTransitions.addListener((ListChangeListener) c -> { while (c.next()) { - for (Transition trans : c.getAddedSubList()) { - insertTransition(trans); - } + for (Transition trans : c.getAddedSubList()) insertTransition(trans); for (final Transition trans: c.getRemoved()) { transitionList.getChildren().remove(transitionPresentationMap.get(trans)); @@ -92,15 +89,15 @@ private void initializeDelayChooser() { private void initializeTransitionExpand() { isTransitionExpanded.addListener((obs, oldVal, newVal) -> { if(newVal) { - if(!paneElementVbox.getChildren().contains(delayChooser)) { + if(!root.getChildren().contains(delayChooser)) { // Add the delay chooser just below the toolbar - paneElementVbox.getChildren().add(1, delayChooser); + root.getChildren().add(1, delayChooser); } showTransitions(); expandTransitionIcon.setIconLiteral("gmi-expand-less"); expandTransitionIcon.setIconSize(24); } else { - paneElementVbox.getChildren().remove(delayChooser); + root.getChildren().remove(delayChooser); hideTransitions(); expandTransitionIcon.setIconLiteral("gmi-expand-more"); expandTransitionIcon.setIconSize(24); @@ -236,7 +233,6 @@ private void delayTextChanged(String oldValue, String newValue) { } catch (NumberFormatException ex) { // If the conversion was not possible, show the old value this.delayTextField.setText(oldValue); - this.delay.set(new BigDecimal(oldValue)); } } diff --git a/src/main/java/ecdar/presentations/EcdarPresentation.java b/src/main/java/ecdar/presentations/EcdarPresentation.java index 4884f95f..7fa6b343 100644 --- a/src/main/java/ecdar/presentations/EcdarPresentation.java +++ b/src/main/java/ecdar/presentations/EcdarPresentation.java @@ -29,24 +29,24 @@ public class EcdarPresentation extends StackPane { private final EcdarController controller; - private final BooleanProperty filePaneOpen = new SimpleBooleanProperty(false); + private final BooleanProperty leftPaneOpen = new SimpleBooleanProperty(false); private final SimpleDoubleProperty leftPaneAnimationProperty = new SimpleDoubleProperty(0); - private final BooleanProperty queryPaneOpen = new SimpleBooleanProperty(false); + private final BooleanProperty rightPaneOpen = new SimpleBooleanProperty(false); private final SimpleDoubleProperty rightPaneAnimationProperty = new SimpleDoubleProperty(0); - private Timeline openQueryPaneAnimation; - private Timeline closeQueryPaneAnimation; - private Timeline openFilePaneAnimation; - private Timeline closeFilePaneAnimation; + private Timeline openLeftPaneAnimation; + private Timeline closeLeftPaneAnimation; + private Timeline openRightPaneAnimation; + private Timeline closeRightPaneAnimation; public EcdarPresentation() { controller = new EcdarFXMLLoader().loadAndGetController("EcdarPresentation.fxml", this); initializeTopBar(); initializeQueryDetailsDialog(); - initializeToggleQueryPaneFunctionality(); - initializeToggleFilePaneFunctionality(); + initializeToggleLeftPaneFunctionality(); + initializeToggleRightPaneFunctionality(); initializeSnackbar(); - // Open the file and query panel initially + // Open the left and right panes initially Platform.runLater(() -> { // Bind sizing of sides and center panes to ensure correct sizing controller.getEditorPresentation().getController().canvasPane.minWidthProperty().bind(controller.root.widthProperty().subtract(leftPaneAnimationProperty.add(rightPaneAnimationProperty))); @@ -66,17 +66,17 @@ public EcdarPresentation() { controller.topPane.minHeightProperty().bind(controller.menuBar.heightProperty()); controller.topPane.maxHeightProperty().bind(controller.menuBar.heightProperty()); - toggleFilePane(); - toggleQueryPane(); + toggleLeftPane(); + toggleRightPane(); Ecdar.getPresentation().controller.scalingProperty.addListener((observable, oldValue, newValue) -> { // If the scaling has changed trigger animations for open panes to update width Platform.runLater(() -> { - if (filePaneOpen.get()) { - openFilePaneAnimation.play(); + if (leftPaneOpen.get()) { + openLeftPaneAnimation.play(); } - if (queryPaneOpen.get()) { - openQueryPaneAnimation.play(); + if (rightPaneOpen.get()) { + openRightPaneAnimation.play(); } }); @@ -114,84 +114,92 @@ private void initializeQueryDetailsDialog() { ))); } - private void initializeToggleFilePaneFunctionality() { - initializeOpenFilePaneAnimation(); - initializeCloseFilePaneAnimation(); + private void initializeToggleLeftPaneFunctionality() { + initializeOpenLeftPaneAnimation(); + initializeCloseLeftPaneAnimation(); // Translate the x coordinate to create the open/close animations - controller.filePane.translateXProperty().bind(leftPaneAnimationProperty.subtract(controller.filePane.widthProperty())); + controller.projectPane.translateXProperty().bind(leftPaneAnimationProperty.subtract(controller.projectPane.widthProperty())); + controller.leftSimPane.translateXProperty().bind(leftPaneAnimationProperty.subtract(controller.leftSimPane.widthProperty())); // Whenever the width of the file pane is updated, update the animations - controller.filePane.widthProperty().addListener((observable) -> { - initializeOpenFilePaneAnimation(); - initializeCloseFilePaneAnimation(); + controller.projectPane.widthProperty().addListener((observable) -> { + initializeOpenLeftPaneAnimation(); + initializeCloseLeftPaneAnimation(); + }); + + // Whenever the width of the file pane is updated, update the animations + controller.leftPane.widthProperty().addListener((observable) -> { + initializeOpenLeftPaneAnimation(); + initializeCloseLeftPaneAnimation(); }); } - private void initializeCloseFilePaneAnimation() { + private void initializeCloseLeftPaneAnimation() { final Interpolator interpolator = Interpolator.SPLINE(0.645, 0.045, 0.355, 1); - closeFilePaneAnimation = new Timeline(); + closeLeftPaneAnimation = new Timeline(); - final KeyValue open = new KeyValue(leftPaneAnimationProperty, controller.filePane.getWidth(), interpolator); + final KeyValue open = new KeyValue(leftPaneAnimationProperty, controller.projectPane.getWidth(), interpolator); final KeyValue closed = new KeyValue(leftPaneAnimationProperty, 0, interpolator); final KeyFrame kf1 = new KeyFrame(Duration.millis(0), open); final KeyFrame kf2 = new KeyFrame(Duration.millis(200), closed); - closeFilePaneAnimation.getKeyFrames().addAll(kf1, kf2); + closeLeftPaneAnimation.getKeyFrames().addAll(kf1, kf2); } - private void initializeOpenFilePaneAnimation() { + private void initializeOpenLeftPaneAnimation() { final Interpolator interpolator = Interpolator.SPLINE(0.645, 0.045, 0.355, 1); - openFilePaneAnimation = new Timeline(); + openLeftPaneAnimation = new Timeline(); final KeyValue closed = new KeyValue(leftPaneAnimationProperty, 0, interpolator); - final KeyValue open = new KeyValue(leftPaneAnimationProperty, controller.filePane.getWidth(), interpolator); + final KeyValue open = new KeyValue(leftPaneAnimationProperty, controller.projectPane.getWidth(), interpolator); final KeyFrame kf1 = new KeyFrame(Duration.millis(0), closed); final KeyFrame kf2 = new KeyFrame(Duration.millis(200), open); - openFilePaneAnimation.getKeyFrames().addAll(kf1, kf2); + openLeftPaneAnimation.getKeyFrames().addAll(kf1, kf2); } - private void initializeToggleQueryPaneFunctionality() { - initializeOpenQueryPaneAnimation(); - initializeCloseQueryPaneAnimation(); + private void initializeToggleRightPaneFunctionality() { + initializeOpenRightPaneAnimation(); + initializeCloseRightPaneAnimation(); // Translate the x coordinate to create the open/close animations controller.queryPane.translateXProperty().bind(rightPaneAnimationProperty.multiply(-1).add(controller.queryPane.widthProperty())); + controller.rightSimPane.translateXProperty().bind(rightPaneAnimationProperty.multiply(-1).add(controller.rightSimPane.widthProperty())); // Whenever the width of the query pane is updated, update the animations controller.queryPane.widthProperty().addListener((observable) -> { - initializeOpenQueryPaneAnimation(); - initializeCloseQueryPaneAnimation(); + initializeOpenRightPaneAnimation(); + initializeCloseRightPaneAnimation(); }); // When new queries are added, make sure that the query pane is open Ecdar.getProject().getQueries().addListener((ListChangeListener) c -> { - if (closeQueryPaneAnimation == null) + if (closeRightPaneAnimation == null) return; // The query pane is not yet initialized while (c.next()) { c.getAddedSubList().forEach(o -> { - if (!queryPaneOpen.get()) { + if (!rightPaneOpen.get()) { // Open the pane - openQueryPaneAnimation.play(); + openRightPaneAnimation.play(); // Toggle the open state - queryPaneOpen.set(queryPaneOpen.not().get()); + rightPaneOpen.set(rightPaneOpen.not().get()); } }); } }); } - private void initializeCloseQueryPaneAnimation() { + private void initializeCloseRightPaneAnimation() { final Interpolator interpolator = Interpolator.SPLINE(0.645, 0.045, 0.355, 1); - closeQueryPaneAnimation = new Timeline(); + closeRightPaneAnimation = new Timeline(); final KeyValue open = new KeyValue(rightPaneAnimationProperty, controller.queryPane.getWidth(), interpolator); final KeyValue closed = new KeyValue(rightPaneAnimationProperty, 0, interpolator); @@ -199,13 +207,13 @@ private void initializeCloseQueryPaneAnimation() { final KeyFrame kf1 = new KeyFrame(Duration.millis(0), open); final KeyFrame kf2 = new KeyFrame(Duration.millis(200), closed); - closeQueryPaneAnimation.getKeyFrames().addAll(kf1, kf2); + closeRightPaneAnimation.getKeyFrames().addAll(kf1, kf2); } - private void initializeOpenQueryPaneAnimation() { + private void initializeOpenRightPaneAnimation() { final Interpolator interpolator = Interpolator.SPLINE(0.645, 0.045, 0.355, 1); - openQueryPaneAnimation = new Timeline(); + openRightPaneAnimation = new Timeline(); final KeyValue closed = new KeyValue(rightPaneAnimationProperty, 0, interpolator); final KeyValue open = new KeyValue(rightPaneAnimationProperty, controller.queryPane.getWidth(), interpolator); @@ -213,7 +221,7 @@ private void initializeOpenQueryPaneAnimation() { final KeyFrame kf1 = new KeyFrame(Duration.millis(0), closed); final KeyFrame kf2 = new KeyFrame(Duration.millis(200), open); - openQueryPaneAnimation.getKeyFrames().addAll(kf1, kf2); + openRightPaneAnimation.getKeyFrames().addAll(kf1, kf2); } private void initializeTopBar() { @@ -276,30 +284,30 @@ private void initializeResizeQueryPane() { }); } - public BooleanProperty toggleFilePane() { - if (filePaneOpen.get()) { - closeFilePaneAnimation.play(); + public BooleanProperty toggleLeftPane() { + if (leftPaneOpen.get()) { + closeLeftPaneAnimation.play(); } else { - openFilePaneAnimation.play(); + openLeftPaneAnimation.play(); } // Toggle the open state - filePaneOpen.set(filePaneOpen.not().get()); + leftPaneOpen.set(leftPaneOpen.not().get()); - return filePaneOpen; + return leftPaneOpen; } - public BooleanProperty toggleQueryPane() { - if (queryPaneOpen.get()) { - closeQueryPaneAnimation.play(); + public BooleanProperty toggleRightPane() { + if (rightPaneOpen.get()) { + closeRightPaneAnimation.play(); } else { - openQueryPaneAnimation.play(); + openRightPaneAnimation.play(); } // Toggle the open state - queryPaneOpen.set(queryPaneOpen.not().get()); + rightPaneOpen.set(rightPaneOpen.not().get()); - return queryPaneOpen; + return rightPaneOpen; } public static void fitSizeWhenAvailable(final ImageView imageView, final StackPane pane) { diff --git a/src/main/java/ecdar/presentations/TracePaneElementPresentation.java b/src/main/java/ecdar/presentations/TracePaneElementPresentation.java index 4e9a15b3..0e8be627 100755 --- a/src/main/java/ecdar/presentations/TracePaneElementPresentation.java +++ b/src/main/java/ecdar/presentations/TracePaneElementPresentation.java @@ -12,7 +12,7 @@ /** * The presentation class for the trace element that can be inserted into the simulator panes */ -public class TracePaneElementPresentation extends AnchorPane { +public class TracePaneElementPresentation extends VBox { final private TracePaneElementController controller; public TracePaneElementPresentation() { diff --git a/src/main/java/ecdar/presentations/TransitionPaneElementPresentation.java b/src/main/java/ecdar/presentations/TransitionPaneElementPresentation.java index 9dbac052..8453af64 100755 --- a/src/main/java/ecdar/presentations/TransitionPaneElementPresentation.java +++ b/src/main/java/ecdar/presentations/TransitionPaneElementPresentation.java @@ -9,7 +9,7 @@ /** * The presentation class for the transition pane element that can be inserted into the simulator panes */ -public class TransitionPaneElementPresentation extends AnchorPane { +public class TransitionPaneElementPresentation extends VBox { final private TransitionPaneElementController controller; public TransitionPaneElementPresentation() { diff --git a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml index 97de6ab5..be278b8e 100644 --- a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml +++ b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml @@ -22,20 +22,12 @@ - - - + - - - + @@ -124,7 +116,7 @@ - + diff --git a/src/main/resources/ecdar/presentations/LeftSimPanePresentation.fxml b/src/main/resources/ecdar/presentations/LeftSimPanePresentation.fxml index 28ed941f..ea343221 100755 --- a/src/main/resources/ecdar/presentations/LeftSimPanePresentation.fxml +++ b/src/main/resources/ecdar/presentations/LeftSimPanePresentation.fxml @@ -1,8 +1,5 @@ - - - @@ -12,8 +9,7 @@ xmlns="http://javafx.com/javafx/8.0.76-ea" type="StackPane" fx:id="root" - fx:controller="ecdar.controllers.LeftSimPaneController" - minWidth="300"> + fx:controller="ecdar.controllers.LeftSimPaneController"> + styleClass="edge-to-edge"> - - - - - - + + diff --git a/src/main/resources/ecdar/presentations/ProjectPanePresentation.fxml b/src/main/resources/ecdar/presentations/ProjectPanePresentation.fxml index 392095c9..58621901 100644 --- a/src/main/resources/ecdar/presentations/ProjectPanePresentation.fxml +++ b/src/main/resources/ecdar/presentations/ProjectPanePresentation.fxml @@ -9,7 +9,8 @@ + fx:controller="ecdar.controllers.ProjectPaneController" + StackPane.alignment="TOP_LEFT"> diff --git a/src/main/resources/ecdar/presentations/QueryPanePresentation.fxml b/src/main/resources/ecdar/presentations/QueryPanePresentation.fxml index 6a984c1b..e50fc7bd 100644 --- a/src/main/resources/ecdar/presentations/QueryPanePresentation.fxml +++ b/src/main/resources/ecdar/presentations/QueryPanePresentation.fxml @@ -9,7 +9,8 @@ + fx:controller="ecdar.controllers.QueryPaneController" + StackPane.alignment="TOP_RIGHT"> diff --git a/src/main/resources/ecdar/presentations/SimulatorPresentation.fxml b/src/main/resources/ecdar/presentations/SimulatorPresentation.fxml index 4f56d4fd..7524a380 100755 --- a/src/main/resources/ecdar/presentations/SimulatorPresentation.fxml +++ b/src/main/resources/ecdar/presentations/SimulatorPresentation.fxml @@ -1,55 +1,19 @@ - - - - - - - + - -
- - - - - - - - - - - - -
- - - - - - - - - - - - - -
+ fx:controller="ecdar.controllers.SimulatorController"> + + + + + + +
diff --git a/src/main/resources/ecdar/presentations/TracePaneElementPresentation.fxml b/src/main/resources/ecdar/presentations/TracePaneElementPresentation.fxml index 49cb83da..c61aced7 100755 --- a/src/main/resources/ecdar/presentations/TracePaneElementPresentation.fxml +++ b/src/main/resources/ecdar/presentations/TracePaneElementPresentation.fxml @@ -6,44 +6,40 @@ + xmlns:fx="http://javafx.com/fxml" + type="VBox" fx:id="root" AnchorPane.leftAnchor="0" AnchorPane.rightAnchor="0" + fx:controller="ecdar.controllers.TracePaneElementController"> + + + + + + - - - - - - - + - + + - - - - - - - - + + + + + + + diff --git a/src/main/resources/ecdar/presentations/TransitionPaneElementPresentation.fxml b/src/main/resources/ecdar/presentations/TransitionPaneElementPresentation.fxml index aeab56de..d308e5a2 100755 --- a/src/main/resources/ecdar/presentations/TransitionPaneElementPresentation.fxml +++ b/src/main/resources/ecdar/presentations/TransitionPaneElementPresentation.fxml @@ -6,61 +6,43 @@ - + - - - - - - - + fx:id="root" type="VBox" + AnchorPane.leftAnchor="0" AnchorPane.rightAnchor="0"> + + + + + + - - - - - + - - - - - + + + + + +
+ + + + + - + -
From 7319c9ba1da449322893cf833572d0a8c746650a Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Wed, 13 Jul 2022 15:21:35 +0200 Subject: [PATCH 004/120] WIP: ProtoBuf messages updated and implementation of these into the GUI started --- src/main/java/ecdar/abstractions/Edge.java | 16 +++++ .../java/ecdar/abstractions/Location.java | 16 +++++ src/main/java/ecdar/abstractions/Nail.java | 7 ++ .../java/ecdar/abstractions/Transition.java | 18 +++++ .../ecdar/controllers/ProcessController.java | 69 ++++++++----------- .../controllers/SimulatorController.java | 1 + .../SimulatorOverviewController.java | 61 ++++++++++------ .../TracePaneElementController.java | 34 +++++---- .../ecdar/simulation/SimulationHandler.java | 6 +- .../ecdar/simulation/SimulationState.java | 16 ++++- .../simulation/SimulationStateSuccessor.java | 2 +- src/main/proto | 2 +- .../presentations/ProcessPresentation.fxml | 55 +++++++++++++++ .../RightSimPanePresentation.fxml | 2 +- 14 files changed, 219 insertions(+), 86 deletions(-) create mode 100644 src/main/java/ecdar/abstractions/Transition.java create mode 100755 src/main/resources/ecdar/presentations/ProcessPresentation.fxml diff --git a/src/main/java/ecdar/abstractions/Edge.java b/src/main/java/ecdar/abstractions/Edge.java index d985c15f..0229d51b 100644 --- a/src/main/java/ecdar/abstractions/Edge.java +++ b/src/main/java/ecdar/abstractions/Edge.java @@ -1,5 +1,6 @@ package ecdar.abstractions; +import EcdarProtoBuf.ComponentProtos; import com.google.gson.JsonPrimitive; import ecdar.Ecdar; import ecdar.controllers.EcdarController; @@ -48,6 +49,21 @@ public Edge(final JsonObject jsonObject, final Component component) { bindReachabilityAnalysis(); } + public Edge(ComponentProtos.Edge protoBufEdge) { + setId(protoBufEdge.getId()); + setSourceLocation(new Location(protoBufEdge.getSourceLocation())); + setTargetLocation(new Location(protoBufEdge.getTargetLocation())); + setStatus(protoBufEdge.getStatus().equals("INPUT") ? EdgeStatus.INPUT : EdgeStatus.OUTPUT); + setSelect(protoBufEdge.getSelect()); + setGuard(protoBufEdge.getGuard()); + setUpdate(protoBufEdge.getUpdate()); + setSync(protoBufEdge.getSync()); + + for (ComponentProtos.Nail protoBufNail : protoBufEdge.getNailList()) { + getNails().add(new Nail(protoBufNail)); + } + } + public String getSync() { return sync.get(); } diff --git a/src/main/java/ecdar/abstractions/Location.java b/src/main/java/ecdar/abstractions/Location.java index 4e7a08ec..1d0ca6ab 100644 --- a/src/main/java/ecdar/abstractions/Location.java +++ b/src/main/java/ecdar/abstractions/Location.java @@ -1,5 +1,6 @@ package ecdar.abstractions; +import EcdarProtoBuf.ComponentProtos; import ecdar.Ecdar; import ecdar.code_analysis.Nearable; import ecdar.controllers.EcdarController; @@ -87,6 +88,21 @@ public Location(final JsonObject jsonObject) { bindReachabilityAnalysis(); } + public Location(ComponentProtos.Location protoBufLocation) { + setId(protoBufLocation.getId()); + setNickname(protoBufLocation.getNickname()); + setInvariant(protoBufLocation.getInvariant()); + setType(Type.valueOf(protoBufLocation.getType())); + setUrgency(Urgency.valueOf(protoBufLocation.getUrgency())); + setX(protoBufLocation.getX()); + setY(protoBufLocation.getY()); + setColor(Color.valueOf(protoBufLocation.getColor())); + setNicknameX(protoBufLocation.getNicknameX()); + setNicknameY(protoBufLocation.getNicknameY()); + setInvariantX(protoBufLocation.getInvariantX()); + setInvariantY(protoBufLocation.getInvariantY()); + } + /** * Generates an id for this, and binds reachability analysis. */ diff --git a/src/main/java/ecdar/abstractions/Nail.java b/src/main/java/ecdar/abstractions/Nail.java index 5ed84d45..8c7cebbe 100644 --- a/src/main/java/ecdar/abstractions/Nail.java +++ b/src/main/java/ecdar/abstractions/Nail.java @@ -1,5 +1,6 @@ package ecdar.abstractions; +import EcdarProtoBuf.ComponentProtos; import ecdar.utility.helpers.Circular; import ecdar.utility.serialize.Serializable; import com.google.gson.Gson; @@ -39,6 +40,12 @@ public Nail(final JsonObject jsonObject) { deserialize(jsonObject); } + public Nail(ComponentProtos.Nail protoBufNail) { + setPropertyType(DisplayableEdge.PropertyType.valueOf(protoBufNail.getPropertyType())); + setPropertyX(protoBufNail.getPropertyX()); + setPropertyY(protoBufNail.getPropertyY()); + } + public double getX() { return x.get(); } diff --git a/src/main/java/ecdar/abstractions/Transition.java b/src/main/java/ecdar/abstractions/Transition.java new file mode 100644 index 00000000..829d2300 --- /dev/null +++ b/src/main/java/ecdar/abstractions/Transition.java @@ -0,0 +1,18 @@ +package ecdar.abstractions; + +import EcdarProtoBuf.ComponentProtos; +import EcdarProtoBuf.ObjectProtos; + +import java.util.ArrayList; +import java.util.List; + +public class Transition { + public final ArrayList edges = new ArrayList<>(); + + public Transition(ObjectProtos.Transition protoBufTransition) { + List protoBufEdges = (List) protoBufTransition.getField(ObjectProtos.Transition.getDescriptor().findFieldByName("edges")); + for (ComponentProtos.Edge protoBufEdge : protoBufEdges) { + edges.add(new Edge(protoBufEdge)); + } + } +} diff --git a/src/main/java/ecdar/controllers/ProcessController.java b/src/main/java/ecdar/controllers/ProcessController.java index 2934b54d..76661131 100755 --- a/src/main/java/ecdar/controllers/ProcessController.java +++ b/src/main/java/ecdar/controllers/ProcessController.java @@ -69,16 +69,14 @@ public void highlightEdges(final Edge[] edges) { for (int i = 0; i < edges.length; i++) { final Edge edge = edges[i]; - // Note that SimulationEdge contains a Location type from the UPPAAL libraries - // but we use a different Location class in our Maps final Location source = edge.getSourceLocation(); - String sourceName = source.getNickname(); + String sourceId = source.getId(); final Location target = edge.getTargetLocation(); - String targetName = target.getNickname(); + String targetId = target.getId(); // If target name is empty the edge is a self loop - if (Objects.equals(targetName, "")) { - targetName = sourceName; + if (Objects.equals(targetId, "")) { + targetId = sourceId; } boolean isSourceUniversal = false; @@ -88,41 +86,34 @@ public void highlightEdges(final Edge[] edges) { // This loop maps "Universal" to for example "U2" for (Map.Entry locEntry: locationPresentationMap.entrySet()) { if(locEntry.getKey().getType() == Location.Type.UNIVERSAL) { - if(sourceName.equals("Universal")) { - sourceName = locEntry.getKey().getId(); + if(sourceId.equals("Universal")) { + sourceId = locEntry.getKey().getId(); isSourceUniversal = true; } - if(targetName.equals("Universal")) { - targetName = locEntry.getKey().getId(); + if(targetId.equals("Universal")) { + targetId = locEntry.getKey().getId(); } } if(locEntry.getKey().getType() == Location.Type.INCONSISTENT) { - if(sourceName.equals("Inconsistent")) { - sourceName = locEntry.getKey().getId(); + if(sourceId.equals("Inconsistent")) { + sourceId = locEntry.getKey().getId(); } - if(targetName.equals("Inconsistent")) { - targetName = locEntry.getKey().getId(); + if(targetId.equals("Inconsistent")) { + targetId = locEntry.getKey().getId(); } } } - // The edge name may contain ! or ?, and we need to replace those so we can compare our stored edge - String edgeName = edge.getId(); -// if (edgeName.contains("?")) { -// edgeName = edgeName.replace("?", ""); -// } else if (edgeName.contains("!")) { -// edgeName = edgeName.replace("!", ""); -// } - // Self loop on a Universal locations means that the edge name should be mapped to * - if (isSourceUniversal && sourceName.equals(targetName)) { - edgeName = "*"; + String edgeId = edge.getId(); + if (isSourceUniversal && sourceId.equals(targetId)) { + edgeId = "*"; } - highlightEdge(edgeName, edge.getStatus(), sourceName, targetName); + highlightEdge(edgeId, edge.getStatus(), sourceId, targetId); } } @@ -145,32 +136,32 @@ public void unhighlightProcess() { private void highlightEdge(final String edgeName, final EdgeStatus edgeStatus, final String sourceName, final String targetName) { for (Map.Entry entry: edgePresentationMap.entrySet()) { final String keyName = entry.getKey().getSync(); - final String keySourceName = entry.getKey().getSourceLocation().getId(); - final String keyTargetName = entry.getKey().getTargetLocation().getId(); + final String keySourceId = entry.getKey().getSourceLocation().getId(); + final String keyTargetId = entry.getKey().getTargetLocation().getId(); // Multiple edges may have the same name, so we also check that the source and target match this edge if(keyName.equals(edgeName) && - keySourceName.equals(sourceName) && - keyTargetName.equals(targetName) && + keySourceId.equals(sourceName) && + keyTargetId.equals(targetName) && entry.getKey().ioStatus.get() == edgeStatus) { entry.getValue().getController().highlight(); - highlightEdgeLocations(keySourceName, keyTargetName); + highlightEdgeLocations(keySourceId, keyTargetId); } } } /** * Helper method that finds the source/target {@link SimLocationPresentation} and highlights it - * @param sourceName The name of the source location - * @param targetName The name of the target location + * @param sourceId The name of the source location + * @param targetId The name of the target location */ - private void highlightEdgeLocations(final String sourceName, final String targetName) { + private void highlightEdgeLocations(final String sourceId, final String targetId) { for (Map.Entry locEntry: locationPresentationMap.entrySet()) { - final String locName = locEntry.getKey().getId(); + final String locationId = locEntry.getKey().getId(); // Check if location is either source or target and highlight it - if(locName.equals(sourceName) || locName.equals(targetName)) { + if(locationId.equals(sourceId) || locationId.equals(targetId)) { locEntry.getValue().highlight(); } } @@ -178,13 +169,11 @@ private void highlightEdgeLocations(final String sourceName, final String target /** * Method that highlights all locations with the same name as the input {@link SimulationLocation} - * @param location The locations to highlight + * @param locationId The locations to highlight */ - public void highlightLocation(final SimulationLocation location) { + public void highlightLocation(final String locationId) { for (Map.Entry locEntry: locationPresentationMap.entrySet()) { - final String locName = locEntry.getKey().getId(); - - if(locName.equals(location.getName())) { + if(locEntry.getKey().getId().equals(locationId)) { locEntry.getValue().highlight(); } } diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index 1f00fcd3..4e2b31aa 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -94,6 +94,7 @@ private List findComponentsInCurrentSimulation() { //Show components from the system final SimulationHandler sm = Ecdar.getSimulationHandler(); List components = new ArrayList<>(); + components = Ecdar.getProject().getComponents(); // for (int i = 0; i < sm.getSystem().getNoOfProcesses(); i++) { // final int finalI = i; // when using a var in lambda it has to be final // final List filteredList = Ecdar.getProject().getComponents().filtered(component -> { diff --git a/src/main/java/ecdar/controllers/SimulatorOverviewController.java b/src/main/java/ecdar/controllers/SimulatorOverviewController.java index 3a6dbc13..8d02ad89 100644 --- a/src/main/java/ecdar/controllers/SimulatorOverviewController.java +++ b/src/main/java/ecdar/controllers/SimulatorOverviewController.java @@ -15,6 +15,7 @@ import javafx.scene.Group; import javafx.scene.control.ScrollPane; import javafx.scene.layout.*; +import javafx.util.Pair; import java.net.URL; import java.util.ArrayList; @@ -84,7 +85,7 @@ public void initialize(final URL location, final ResourceBundle resources) { * {@link Component}s which are needed to the processContainer. */ private void initializeProcessContainer() { - processContainer.translateXProperty().addListener((observable, oldValue, newValue)-> { + processContainer.translateXProperty().addListener((observable, oldValue, newValue) -> { processContainer.setTranslateX(0); }); //Sets the space between the processes @@ -96,7 +97,7 @@ private void initializeProcessContainer() { componentArrayList.addListener((ListChangeListener) c -> { final Map processes = new HashMap<>(); - while (c.next()){ + while (c.next()) { if (c.wasRemoved()) { clearOverview(); } else { @@ -107,7 +108,15 @@ private void initializeProcessContainer() { highlightProcessState(Ecdar.getSimulationHandler().getCurrentState()); processContainer.getChildren().addAll(processes.values()); processPresentations.putAll(processes); - }); + }); + + final Map processes = new HashMap<>(); + componentArrayList.forEach(o -> processes.put(o.getName(), new ProcessPresentation(o))); + + // Highlight the current state when the processes change + highlightProcessState(Ecdar.getSimulationHandler().getCurrentState()); + processContainer.getChildren().addAll(processes.values()); + processPresentations.putAll(processes); } /** @@ -134,7 +143,7 @@ private void initializeSimulationVariables() { }); }); Ecdar.getSimulationHandler().getSimulationClocks().addListener((InvalidationListener) obs -> { - if(processPresentations.size() == 0) return; + if (processPresentations.size() == 0) return; Ecdar.getSimulationHandler().getSimulationClocks().forEach((s, bigDecimal) -> { if (!s.equals("t(0)")) {// As t(0) does not belong to any process final String[] spittedString = s.split("\\."); @@ -151,9 +160,10 @@ private void initializeSimulationVariables() { * Removes {@link #processContainer} from the {@link #groupContainer}.
* In this way the {@link Component}s in the processContainer will then again be resizable, * as the class {@link Group} makes its children not resizeable. + * * @see Group */ - void removeProcessesFromGroup(){ + void removeProcessesFromGroup() { groupContainer.getChildren().removeAll(processContainer); } @@ -165,10 +175,11 @@ void removeProcessesFromGroup(){ * which is needed to show the {@link ProcessPresentation}s in the {@link #scrollPane}. * If the processContainer is already contained in the groupContainer * the method does nothing but return. + * * @see #removeProcessesFromGroup() */ - void addProcessesToGroup(){ - if(groupContainer.getChildren().contains(processContainer)) return; + void addProcessesToGroup() { + if (groupContainer.getChildren().contains(processContainer)) return; groupContainer.getChildren().add(processContainer); } @@ -177,8 +188,8 @@ void addProcessesToGroup(){ */ private void initializeZoom() { processContainer.scaleXProperty().addListener((observable, oldValue, newValue) -> { - if(newValue.doubleValue() > MAX_ZOOM_IN) isMaxZoomInReached = true; - if(newValue.doubleValue() < MAX_ZOOM_OUT) isMaxZoomOutReached = true; + if (newValue.doubleValue() > MAX_ZOOM_IN) isMaxZoomInReached = true; + if (newValue.doubleValue() < MAX_ZOOM_OUT) isMaxZoomOutReached = true; handleWidthOnScale(oldValue, newValue); }); @@ -206,7 +217,7 @@ private void initializeZoom() { private void initializeWindowResizing() { scrollPane.widthProperty().addListener((observable, oldValue, newValue) -> { final double width = (newValue.doubleValue()) * (1 + (1 - processContainer.getScaleX())); - if(processContainer.getScaleX() > 1) { //Zoomed in + if (processContainer.getScaleX() > 1) { //Zoomed in processContainer.setMinWidth(width); processContainer.setMaxWidth(width); } else if (processContainer.getScaleX() < 1) { //Zoomed out @@ -225,6 +236,7 @@ private void initializeWindowResizing() { /** * Increments the {@link #processContainer} scaleX and scaleY properties * which creates the zoom-in feeling. Resizing of the view is handled by {@link #handleWidthOnScale(Number, Number)} + * * @see FlowPane#scaleXProperty() * @see FlowPane#scaleYProperty() */ @@ -239,11 +251,12 @@ void zoomIn() { /** * Decrements the {@link #processContainer} scaleX and scaleY properties * which creates the zoom-in feeling. Resizing of the view is handled by {@link #handleWidthOnScale(Number, Number)} + * * @see FlowPane#scaleXProperty() * @see FlowPane#scaleYProperty() */ void zoomOut() { - if(isMaxZoomOutReached) return; + if (isMaxZoomOutReached) return; isMaxZoomInReached = false; processContainer.setScaleX(processContainer.getScaleX() * (1 / SCALE_DELTA)); processContainer.setScaleY(processContainer.getScaleY() * (1 / SCALE_DELTA)); @@ -251,11 +264,12 @@ void zoomOut() { /** * Resets the scaling of the {@link #processContainer}, and hereby the zoom + * * @see FlowPane#scaleXProperty() * @see FlowPane#scaleYProperty() */ void resetZoom() { - if(processContainer.getScaleX() == 1) return; + if (processContainer.getScaleX() == 1) return; resetZoom = true; isMaxZoomInReached = false; isMaxZoomOutReached = false; @@ -265,15 +279,16 @@ void resetZoom() { /** * Handles the scaling of the width of the {@link #processContainer} + * * @param oldValue the width of {@link #scrollPane} before the change * @param newValue the width of {@link #scrollPane} after the change */ private void handleWidthOnScale(final Number oldValue, final Number newValue) { - if(resetZoom) { //Zoom reset + if (resetZoom) { //Zoom reset resetZoom = false; processContainer.setMinWidth(scrollPane.getWidth() - SUPER_SPECIAL_SCROLLPANE_OFFSET); processContainer.setMaxWidth(scrollPane.getWidth() - SUPER_SPECIAL_SCROLLPANE_OFFSET); - } else if(oldValue.doubleValue() > newValue.doubleValue()) { //Zoom in + } else if (oldValue.doubleValue() > newValue.doubleValue()) { //Zoom in resetZoom = false; processContainer.setMinWidth(Math.round(processContainer.getWidth() * SCALE_DELTA)); processContainer.setMaxWidth(Math.round(processContainer.getWidth() * SCALE_DELTA)); @@ -293,7 +308,7 @@ private void initializeHighlighting() { // If the new transition is not null, we want to highlight the locations and edges in the new value // otherwise we highlight the current state - if(newTransition != null) { + if (newTransition != null) { highlightProcessTransition(newTransition); } else { highlightProcessState(Ecdar.getSimulationHandler().getCurrentState()); @@ -305,7 +320,7 @@ private void initializeHighlighting() { // If the new state is not null, we want to highlight the locations in the new value // otherwise we highlight the current state - if(newState != null) { + if (newState != null) { highlightProcessState(newState); } else { highlightProcessState(Ecdar.getSimulationHandler().getCurrentState()); @@ -317,6 +332,7 @@ private void initializeHighlighting() { * Highlights all the processes involved in the transition. * Finds the processes involved in the transition (processes with edges in the transition) and highlights their edges * Also fades processes that are not active in the selected transition + * * @param transition The transition for which we highlight the involved processes */ public void highlightProcessTransition(final Transition transition) { @@ -340,7 +356,7 @@ public void highlightProcessTransition(final Transition transition) { * Unhighlights all processes */ public void unhighlightProcesses() { - for(final ProcessPresentation presentation: processPresentations.values()) { + for (final ProcessPresentation presentation : processPresentations.values()) { presentation.getController().unhighlightProcess(); presentation.showActive(); } @@ -348,18 +364,19 @@ public void unhighlightProcesses() { /** * Finds the processes for the input locations in the input {@link SimulationState} and highlights the locations. + * * @param state The state with the locations to highlight */ public void highlightProcessState(final SimulationState state) { for (int i = 0; i < state.getLocations().size(); i++) { - final Location loc = state.getLocations().get(i); + final Pair loc = state.getLocations().get(i); - for(final ProcessPresentation presentation: processPresentations.values()) { + for (final ProcessPresentation presentation : processPresentations.values()) { final String processName = presentation.getController().getComponent().getName(); -// if(processName.equals(loc.getProcess().getName())) { -// presentation.getController().highlightLocation(loc); -// } + if (processName.equals(loc.getKey())) { + presentation.getController().highlightLocation(loc.getValue()); + } } } } diff --git a/src/main/java/ecdar/controllers/TracePaneElementController.java b/src/main/java/ecdar/controllers/TracePaneElementController.java index a733b15f..6ace31df 100755 --- a/src/main/java/ecdar/controllers/TracePaneElementController.java +++ b/src/main/java/ecdar/controllers/TracePaneElementController.java @@ -45,11 +45,11 @@ public class TracePaneElementController implements Initializable { public void initialize(URL location, ResourceBundle resources) { Ecdar.getSimulationHandler().getTraceLog().addListener((ListChangeListener) c -> { while (c.next()) { - for(final SimulationState state: c.getAddedSubList()) { + for (final SimulationState state : c.getAddedSubList()) { insertTraceState(state, true); } - for(final SimulationState state: c.getRemoved()) { + for (final SimulationState state : c.getRemoved()) { traceList.getChildren().remove(transitionPresentationMap.get(state)); transitionPresentationMap.remove(state); } @@ -67,7 +67,7 @@ public void initialize(URL location, ResourceBundle resources) { */ private void initializeTraceExpand() { isTraceExpanded.addListener((obs, oldVal, newVal) -> { - if(newVal) { + if (newVal) { showTrace(); expandTraceIcon.setIconLiteral("gmi-expand-less"); expandTraceIcon.setIconSize(24); @@ -104,7 +104,8 @@ private void showTrace() { /** * Instantiates a {@link TransitionPresentation} for a {@link SimulationState} and adds it to the view - * @param state The state the should be inserted into the trace log + * + * @param state The state the should be inserted into the trace log * @param shouldAnimate A boolean that indicates whether the trace should fade in when added to the view */ private void insertTraceState(final SimulationState state, final boolean shouldAnimate) { @@ -135,9 +136,9 @@ private void insertTraceState(final SimulationState state, final boolean shouldA transitionPresentation.getController().setTitle(title); // Only insert the presentation into the view if the trace is expanded - if(isTraceExpanded.get()) { + if (isTraceExpanded.get()) { traceList.getChildren().add(transitionPresentation); - if(shouldAnimate) { + if (shouldAnimate) { transitionPresentation.playFadeAnimation(); } } @@ -145,24 +146,27 @@ private void insertTraceState(final SimulationState state, final boolean shouldA /** * A helper method that returns a string representing a state in the trace log + * * @param state The SimulationState to represent * @return A string representing the state */ private String traceString(SimulationState state) { - String title = "("; + StringBuilder title = new StringBuilder("("); int length = state.getLocations().size(); - for (int i = 0; i < length ; i++) { - Location loc = state.getLocations().get(i); + for (int i = 0; i < length; i++) { + Location loc = Ecdar.getProject() + .findComponent(state.getLocations().get(i).getKey()) + .findLocation(state.getLocations().get(i).getValue()); String locationName = loc.getNickname(); - if (i == length-1) { - title += locationName; + if (i == length - 1) { + title.append(locationName); } else { - title += locationName + ", "; + title.append(locationName).append(", "); } } - title += ")"; + title.append(")"); - return title; + return title.toString(); } /** @@ -170,7 +174,7 @@ private String traceString(SimulationState state) { */ @FXML private void expandTrace() { - if(isTraceExpanded.get()) { + if (isTraceExpanded.get()) { isTraceExpanded.set(false); } else { isTraceExpanded.set(true); diff --git a/src/main/java/ecdar/simulation/SimulationHandler.java b/src/main/java/ecdar/simulation/SimulationHandler.java index 65d2ea73..6305a5e4 100755 --- a/src/main/java/ecdar/simulation/SimulationHandler.java +++ b/src/main/java/ecdar/simulation/SimulationHandler.java @@ -18,9 +18,9 @@ */ public class SimulationHandler { public static final String QUERY_PREFIX = "Query: "; - private ObjectProperty currentConcreteState; - private ObjectProperty initialConcreteState; - private ObjectProperty currentTime; + private ObjectProperty currentConcreteState = new SimpleObjectProperty<>(new SimulationState(null)); + private ObjectProperty initialConcreteState = new SimpleObjectProperty<>(new SimulationState(null)); + private ObjectProperty currentTime = new SimpleObjectProperty<>(); private BigDecimal delay; private ArrayList edgesSelected; private EcdarSystem system; diff --git a/src/main/java/ecdar/simulation/SimulationState.java b/src/main/java/ecdar/simulation/SimulationState.java index 933864f1..f246ce5a 100644 --- a/src/main/java/ecdar/simulation/SimulationState.java +++ b/src/main/java/ecdar/simulation/SimulationState.java @@ -1,11 +1,22 @@ package ecdar.simulation; +import EcdarProtoBuf.ObjectProtos; import ecdar.abstractions.Location; +import javafx.util.Pair; import java.math.BigDecimal; import java.util.ArrayList; +import java.util.stream.Collectors; public class SimulationState { + private final ArrayList> locations; + + public SimulationState(ObjectProtos.StateTuple protoBufState) { + if (protoBufState != null) locations = protoBufState.getLocationsList().stream().map(lt -> new Pair<>(lt.getComponentName(), lt.getId())).collect(Collectors.toCollection(ArrayList::new)); + else locations = new ArrayList<>(); + // ToDo: Handle federations + } + public void setTime(BigDecimal value) { // ToDo: Implement } @@ -35,8 +46,7 @@ public double doubleValue() { }; } - public ArrayList getLocations() { - // ToDo: Implement - return new ArrayList<>(); + public ArrayList> getLocations() { + return locations; } } diff --git a/src/main/java/ecdar/simulation/SimulationStateSuccessor.java b/src/main/java/ecdar/simulation/SimulationStateSuccessor.java index 0c519adb..84370dd4 100644 --- a/src/main/java/ecdar/simulation/SimulationStateSuccessor.java +++ b/src/main/java/ecdar/simulation/SimulationStateSuccessor.java @@ -10,6 +10,6 @@ public ArrayList getTransitions() { public SimulationState getState() { // ToDo: Implement - return new SimulationState(); + return new SimulationState(null); } } diff --git a/src/main/proto b/src/main/proto index b4d83889..42084f76 160000 --- a/src/main/proto +++ b/src/main/proto @@ -1 +1 @@ -Subproject commit b4d83889935f6f6e6d0a0a5547c28b2ab1388cd1 +Subproject commit 42084f767e42c8c4b9285c04342ace57645448a3 diff --git a/src/main/resources/ecdar/presentations/ProcessPresentation.fxml b/src/main/resources/ecdar/presentations/ProcessPresentation.fxml new file mode 100755 index 00000000..e4ba6b20 --- /dev/null +++ b/src/main/resources/ecdar/presentations/ProcessPresentation.fxml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + + + + +
+
+
+ +
+
+ + + + + +
+
+
\ No newline at end of file diff --git a/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml b/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml index 3ed05348..4697fb97 100755 --- a/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml +++ b/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml @@ -17,7 +17,7 @@ AnchorPane.rightAnchor="0" styleClass="edge-to-edge"> - + From 260dd220fa96093e00d4c203c86698529269ff1c Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Wed, 13 Jul 2022 15:37:33 +0200 Subject: [PATCH 005/120] Minor clean up --- src/main/java/ecdar/controllers/ProcessController.java | 4 +--- src/main/java/ecdar/simulation/SimulationEdge.java | 8 -------- src/main/java/ecdar/simulation/SimulationLocation.java | 8 -------- 3 files changed, 1 insertion(+), 19 deletions(-) delete mode 100644 src/main/java/ecdar/simulation/SimulationEdge.java delete mode 100644 src/main/java/ecdar/simulation/SimulationLocation.java diff --git a/src/main/java/ecdar/controllers/ProcessController.java b/src/main/java/ecdar/controllers/ProcessController.java index 76661131..c80f319b 100755 --- a/src/main/java/ecdar/controllers/ProcessController.java +++ b/src/main/java/ecdar/controllers/ProcessController.java @@ -4,8 +4,6 @@ import ecdar.abstractions.*; import ecdar.presentations.SimEdgePresentation; import ecdar.presentations.SimLocationPresentation; -import ecdar.simulation.SimulationEdge; -import ecdar.simulation.SimulationLocation; import javafx.animation.Interpolator; import javafx.animation.Transition; import javafx.beans.property.ObjectProperty; @@ -168,7 +166,7 @@ private void highlightEdgeLocations(final String sourceId, final String targetId } /** - * Method that highlights all locations with the same name as the input {@link SimulationLocation} + * Method that highlights all locations with the input ID * @param locationId The locations to highlight */ public void highlightLocation(final String locationId) { diff --git a/src/main/java/ecdar/simulation/SimulationEdge.java b/src/main/java/ecdar/simulation/SimulationEdge.java deleted file mode 100644 index 696f573f..00000000 --- a/src/main/java/ecdar/simulation/SimulationEdge.java +++ /dev/null @@ -1,8 +0,0 @@ -package ecdar.simulation; - -public class SimulationEdge { - public String getName() { - // ToDo: Implement - return "Edge name"; - } -} diff --git a/src/main/java/ecdar/simulation/SimulationLocation.java b/src/main/java/ecdar/simulation/SimulationLocation.java deleted file mode 100644 index 5bcbba6a..00000000 --- a/src/main/java/ecdar/simulation/SimulationLocation.java +++ /dev/null @@ -1,8 +0,0 @@ -package ecdar.simulation; - -public class SimulationLocation { - public String getName() { - // ToDo: Implement - return "Location name"; - } -} From e08425072e2bf7e322c79fea59b069421da1e528 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Wed, 13 Jul 2022 15:42:30 +0200 Subject: [PATCH 006/120] WIP: Spacing for trace and transition headers added --- .../ecdar/presentations/LeftSimPanePresentation.fxml | 1 - .../ecdar/presentations/TracePaneElementPresentation.fxml | 4 ++++ .../presentations/TransitionPaneElementPresentation.fxml | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/resources/ecdar/presentations/LeftSimPanePresentation.fxml b/src/main/resources/ecdar/presentations/LeftSimPanePresentation.fxml index ea343221..e80df29e 100755 --- a/src/main/resources/ecdar/presentations/LeftSimPanePresentation.fxml +++ b/src/main/resources/ecdar/presentations/LeftSimPanePresentation.fxml @@ -10,7 +10,6 @@ type="StackPane" fx:id="root" fx:controller="ecdar.controllers.LeftSimPaneController"> - + + + + + + + Date: Thu, 14 Jul 2022 07:56:01 +0200 Subject: [PATCH 007/120] WIP: BackendDriver usage for simulation started --- .../java/ecdar/backend/BackendDriver.java | 29 +++++++++++++++---- .../ecdar/simulation/SimulationHandler.java | 7 ++--- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index 4e0aa568..1eb4f77a 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -8,6 +8,7 @@ import ecdar.abstractions.BackendInstance; import ecdar.abstractions.Component; import ecdar.abstractions.QueryState; +import ecdar.simulation.SimulationState; import io.grpc.*; import io.grpc.stub.StreamObserver; import org.springframework.util.SocketUtils; @@ -139,11 +140,7 @@ public void closeAllBackendConnections() throws IOException { private void executeQuery(ExecutableQuery executableQuery) { if (executableQuery.queryListener.getQuery().getQueryState() == QueryState.UNKNOWN) return; - // Get available connection or start new - final BackendConnection backendConnection = openBackendConnections.stream() - .filter((connection) -> connection.getBackendInstance() == null || connection.getBackendInstance().equals(executableQuery.backend)) - .findFirst() - .orElseGet(() -> startNewBackendConnection(executableQuery.backend)); + final BackendConnection backendConnection = getBackendConnection(executableQuery.backend); // If the connection is null, there are no available connections, // and it was not possible to start a new one @@ -224,6 +221,24 @@ public void onCompleted() { backendConnection.getStub().withDeadlineAfter(deadlineForResponses, TimeUnit.MILLISECONDS).updateComponents(componentsBuilder.build(), observer); } + /** + * Filters the list of open {@link BackendConnection}s to the specified {@link BackendInstance} and returns the + * first match or attempts to start a new connection if none is found. + * If a new connection is attempted to be started and no ports are free within the specified port range for the + * BackendInstance + * + * @param backend backend instance to get a connection to (e.g. Reveaal, j-Ecdar, custom_engine) + * @return a BackendConnection object linked to backend, either from the open backend connection list + * or a newly started connection. If no match is found in the list and the attempt to start a new connection failed, + * null is returned. + */ + private BackendConnection getBackendConnection(BackendInstance backend) { + return openBackendConnections.stream() + .filter((connection) -> connection.getBackendInstance() == null || connection.getBackendInstance().equals(backend)) + .findFirst() + .orElseGet(() -> startNewBackendConnection(backend)); + } + private BackendConnection startNewBackendConnection(BackendInstance backend) { Process p = null; String hostAddress = (backend.isLocal() ? "127.0.0.1" : backend.getBackendLocation()); @@ -349,6 +364,10 @@ public void run() { } } + public SimulationState getInitialSimulationState() { + return new SimulationState(null); + } + private class ExecutableQuery { private final String query; private final BackendInstance backend; diff --git a/src/main/java/ecdar/simulation/SimulationHandler.java b/src/main/java/ecdar/simulation/SimulationHandler.java index 6305a5e4..4e089f12 100755 --- a/src/main/java/ecdar/simulation/SimulationHandler.java +++ b/src/main/java/ecdar/simulation/SimulationHandler.java @@ -18,8 +18,8 @@ */ public class SimulationHandler { public static final String QUERY_PREFIX = "Query: "; - private ObjectProperty currentConcreteState = new SimpleObjectProperty<>(new SimulationState(null)); - private ObjectProperty initialConcreteState = new SimpleObjectProperty<>(new SimulationState(null)); + private ObjectProperty currentConcreteState; + private ObjectProperty initialConcreteState; private ObjectProperty currentTime = new SimpleObjectProperty<>(); private BigDecimal delay; private ArrayList edgesSelected; @@ -349,8 +349,7 @@ public ObservableMap getSimulationClocks() { * @return the initial {@link SimulationState} of this simulation */ public SimulationState getInitialConcreteState() { - // ToDo: Implement - return initialConcreteState.get(); + return Ecdar.getBackendDriver().getInitialSimulationState(); } public ObjectProperty initialConcreteStateProperty() { From 8a88c62d7cead2630212a931f0652d1a38d7f8af Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Thu, 14 Jul 2022 15:19:22 +0200 Subject: [PATCH 008/120] WIP: Components can be shown within the simulator --- .../java/ecdar/backend/BackendDriver.java | 6 +++++- .../ecdar/controllers/EcdarController.java | 2 +- .../ecdar/controllers/ProcessController.java | 20 +++++++++---------- .../controllers/SimulatorController.java | 6 ++---- .../SimulatorOverviewController.java | 6 ++---- .../presentations/SimulatorPresentation.java | 1 - .../ecdar/simulation/SimulationHandler.java | 8 ++++---- .../simulation/SimulationStateSuccessor.java | 4 +++- .../presentations/ProcessPresentation.fxml | 1 + 9 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index 1eb4f77a..f8e9bedc 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -2,6 +2,7 @@ import EcdarProtoBuf.ComponentProtos; import EcdarProtoBuf.EcdarBackendGrpc; +import EcdarProtoBuf.ObjectProtos; import EcdarProtoBuf.QueryProtos; import com.google.protobuf.Empty; import ecdar.Ecdar; @@ -11,6 +12,7 @@ import ecdar.simulation.SimulationState; import io.grpc.*; import io.grpc.stub.StreamObserver; +import javafx.util.Pair; import org.springframework.util.SocketUtils; import java.io.*; @@ -365,7 +367,9 @@ public void run() { } public SimulationState getInitialSimulationState() { - return new SimulationState(null); + SimulationState state = new SimulationState(ObjectProtos.StateTuple.newBuilder().getDefaultInstanceForType()); + state.getLocations().add(new Pair<>(Ecdar.getProject().getComponents().get(0).getName(), Ecdar.getProject().getComponents().get(0).getLocations().get(0).getId())); + return state; } private class ExecutableQuery { diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index 041485cb..39384a9d 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -998,7 +998,7 @@ private void enterEditorMode() { private void enterSimulatorMode() { // ToDo NIELS: Consider implementing willShow and willHide to handle general elements that should only be available for one of the modes // ecdarPresentation.getController().willHide(); -// simulatorPresentation.getController().willShow(); + simulatorPresentation.getController().willShow(); borderPane.setCenter(simulatorPresentation); leftPane.getChildren().clear(); diff --git a/src/main/java/ecdar/controllers/ProcessController.java b/src/main/java/ecdar/controllers/ProcessController.java index c80f319b..6ce42fc2 100755 --- a/src/main/java/ecdar/controllers/ProcessController.java +++ b/src/main/java/ecdar/controllers/ProcessController.java @@ -63,10 +63,8 @@ private void initializeValues() { * Highlights the edges and accompanying source/target locations in the process * @param edges The edges to highlight */ - public void highlightEdges(final Edge[] edges) { - for (int i = 0; i < edges.length; i++) { - final Edge edge = edges[i]; - + public void highlightEdges(final ArrayList edges) { + for (final Edge edge : edges) { final Location source = edge.getSourceLocation(); String sourceId = source.getId(); final Location target = edge.getTargetLocation(); @@ -82,24 +80,24 @@ public void highlightEdges(final Edge[] edges) { // Iterate through all locations to check for Universal and Inconsistent locations // The name of a Universal location may be "U2" in our system, but it is mapped to "Universal" in the engine // This loop maps "Universal" to for example "U2" - for (Map.Entry locEntry: locationPresentationMap.entrySet()) { - if(locEntry.getKey().getType() == Location.Type.UNIVERSAL) { - if(sourceId.equals("Universal")) { + for (Map.Entry locEntry : locationPresentationMap.entrySet()) { + if (locEntry.getKey().getType() == Location.Type.UNIVERSAL) { + if (sourceId.equals("Universal")) { sourceId = locEntry.getKey().getId(); isSourceUniversal = true; } - if(targetId.equals("Universal")) { + if (targetId.equals("Universal")) { targetId = locEntry.getKey().getId(); } } - if(locEntry.getKey().getType() == Location.Type.INCONSISTENT) { - if(sourceId.equals("Inconsistent")) { + if (locEntry.getKey().getType() == Location.Type.INCONSISTENT) { + if (sourceId.equals("Inconsistent")) { sourceId = locEntry.getKey().getId(); } - if(targetId.equals("Inconsistent")) { + if (targetId.equals("Inconsistent")) { targetId = locEntry.getKey().getId(); } } diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index 4e2b31aa..639c1ff1 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -3,8 +3,6 @@ import ecdar.Ecdar; import ecdar.abstractions.*; import ecdar.simulation.SimulationHandler; -import ecdar.presentations.LeftSimPanePresentation; -import ecdar.presentations.RightSimPanePresentation; import ecdar.presentations.SimulatorOverviewPresentation; import ecdar.simulation.SimulationState; import ecdar.simulation.Transition; @@ -13,9 +11,7 @@ import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.fxml.Initializable; -import javafx.scene.control.Label; import javafx.scene.layout.StackPane; -import javafx.scene.shape.Rectangle; import java.net.URL; import java.util.ArrayList; @@ -51,6 +47,8 @@ public void willShow() { final SimulationHandler sm = Ecdar.getSimulationHandler(); boolean shouldSimulationBeReset = true; + if (sm.getCurrentState() == null) sm.initialStep(); // ToDo NIELS: Find better solution + //Have the user left a trace or is he simulating a query if (sm.traceLog.size() >= 2 || sm.getCurrentSimulation().contains(SimulationHandler.QUERY_PREFIX)) { shouldSimulationBeReset = false; diff --git a/src/main/java/ecdar/controllers/SimulatorOverviewController.java b/src/main/java/ecdar/controllers/SimulatorOverviewController.java index 8d02ad89..9af4af76 100644 --- a/src/main/java/ecdar/controllers/SimulatorOverviewController.java +++ b/src/main/java/ecdar/controllers/SimulatorOverviewController.java @@ -105,7 +105,7 @@ private void initializeProcessContainer() { } } // Highlight the current state when the processes change - highlightProcessState(Ecdar.getSimulationHandler().getCurrentState()); + highlightProcessState(Ecdar.getSimulationHandler().getCurrentState()); // ToDo NIELS: Throws NullPointerException inside method due to currentState processContainer.getChildren().addAll(processes.values()); processPresentations.putAll(processes); }); @@ -113,8 +113,6 @@ private void initializeProcessContainer() { final Map processes = new HashMap<>(); componentArrayList.forEach(o -> processes.put(o.getName(), new ProcessPresentation(o))); - // Highlight the current state when the processes change - highlightProcessState(Ecdar.getSimulationHandler().getCurrentState()); processContainer.getChildren().addAll(processes.values()); processPresentations.putAll(processes); } @@ -345,7 +343,7 @@ public void highlightProcessTransition(final Transition transition) { for (final ProcessPresentation processPresentation : processPresentations.values()) { // Find the processes that have edges involved in this transition - processPresentation.getController().highlightEdges(edges.toArray(new Edge[0])); + processPresentation.getController().highlightEdges(edges); processesToHide.remove(processPresentation); } diff --git a/src/main/java/ecdar/presentations/SimulatorPresentation.java b/src/main/java/ecdar/presentations/SimulatorPresentation.java index 2d1a5105..255b7dd1 100755 --- a/src/main/java/ecdar/presentations/SimulatorPresentation.java +++ b/src/main/java/ecdar/presentations/SimulatorPresentation.java @@ -10,7 +10,6 @@ public SimulatorPresentation() { controller = new EcdarFXMLLoader().loadAndGetController("SimulatorPresentation.fxml", this); } - /** * The way to get the associated/linked controller of this presenter * @return the controller linked to this presenter diff --git a/src/main/java/ecdar/simulation/SimulationHandler.java b/src/main/java/ecdar/simulation/SimulationHandler.java index 4e089f12..395d5027 100755 --- a/src/main/java/ecdar/simulation/SimulationHandler.java +++ b/src/main/java/ecdar/simulation/SimulationHandler.java @@ -18,8 +18,8 @@ */ public class SimulationHandler { public static final String QUERY_PREFIX = "Query: "; - private ObjectProperty currentConcreteState; - private ObjectProperty initialConcreteState; + private ObjectProperty currentConcreteState = new SimpleObjectProperty<>(); + private ObjectProperty initialConcreteState = new SimpleObjectProperty<>(); private ObjectProperty currentTime = new SimpleObjectProperty<>(); private BigDecimal delay; private ArrayList edgesSelected; @@ -73,8 +73,8 @@ private void initializeSimulation() { this.simulationVariables.clear(); this.simulationClocks.clear(); this.traceLog.clear(); - this.currentConcreteState = new SimpleObjectProperty<>(getInitialConcreteState()); - this.initialConcreteState = new SimpleObjectProperty<>(getInitialConcreteState()); + this.currentConcreteState.set(getInitialConcreteState()); + this.initialConcreteState.set(getInitialConcreteState()); this.currentTime = new SimpleObjectProperty<>(BigDecimal.ZERO); //Preparation for the simulation diff --git a/src/main/java/ecdar/simulation/SimulationStateSuccessor.java b/src/main/java/ecdar/simulation/SimulationStateSuccessor.java index 84370dd4..1b0c4cba 100644 --- a/src/main/java/ecdar/simulation/SimulationStateSuccessor.java +++ b/src/main/java/ecdar/simulation/SimulationStateSuccessor.java @@ -1,5 +1,7 @@ package ecdar.simulation; +import ecdar.Ecdar; + import java.util.ArrayList; public class SimulationStateSuccessor { @@ -10,6 +12,6 @@ public ArrayList getTransitions() { public SimulationState getState() { // ToDo: Implement - return new SimulationState(null); + return Ecdar.getBackendDriver().getInitialSimulationState(); } } diff --git a/src/main/resources/ecdar/presentations/ProcessPresentation.fxml b/src/main/resources/ecdar/presentations/ProcessPresentation.fxml index e4ba6b20..6b4b8f77 100755 --- a/src/main/resources/ecdar/presentations/ProcessPresentation.fxml +++ b/src/main/resources/ecdar/presentations/ProcessPresentation.fxml @@ -50,6 +50,7 @@ + \ No newline at end of file From a22f5591213f7e67e77ef73fdb7d8a4efd2abe87 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Fri, 15 Jul 2022 07:55:33 +0200 Subject: [PATCH 009/120] ProtoBuf messages updated and no longer used messages commented out of implementation --- src/main/java/ecdar/abstractions/Edge.java | 30 +++++++++---------- .../java/ecdar/abstractions/Location.java | 29 +++++++++--------- src/main/java/ecdar/abstractions/Nail.java | 11 +++---- .../java/ecdar/abstractions/Transition.java | 22 ++++++++++---- src/main/proto | 2 +- .../java/ecdar/simulation/SimulationTest.java | 10 +++++++ 6 files changed, 64 insertions(+), 40 deletions(-) create mode 100644 src/test/java/ecdar/simulation/SimulationTest.java diff --git a/src/main/java/ecdar/abstractions/Edge.java b/src/main/java/ecdar/abstractions/Edge.java index 0229d51b..d3dc4ddf 100644 --- a/src/main/java/ecdar/abstractions/Edge.java +++ b/src/main/java/ecdar/abstractions/Edge.java @@ -48,21 +48,21 @@ public Edge(final JsonObject jsonObject, final Component component) { deserialize(jsonObject, component); bindReachabilityAnalysis(); } - - public Edge(ComponentProtos.Edge protoBufEdge) { - setId(protoBufEdge.getId()); - setSourceLocation(new Location(protoBufEdge.getSourceLocation())); - setTargetLocation(new Location(protoBufEdge.getTargetLocation())); - setStatus(protoBufEdge.getStatus().equals("INPUT") ? EdgeStatus.INPUT : EdgeStatus.OUTPUT); - setSelect(protoBufEdge.getSelect()); - setGuard(protoBufEdge.getGuard()); - setUpdate(protoBufEdge.getUpdate()); - setSync(protoBufEdge.getSync()); - - for (ComponentProtos.Nail protoBufNail : protoBufEdge.getNailList()) { - getNails().add(new Nail(protoBufNail)); - } - } + // ToDo NIELS: Comment in, when edges should be received through ProtoBuf +// public Edge(ComponentProtos.Edge protoBufEdge) { +// setId(protoBufEdge.getId()); +// setSourceLocation(new Location(protoBufEdge.getSourceLocation())); +// setTargetLocation(new Location(protoBufEdge.getTargetLocation())); +// setStatus(protoBufEdge.getStatus().equals("INPUT") ? EdgeStatus.INPUT : EdgeStatus.OUTPUT); +// setSelect(protoBufEdge.getSelect()); +// setGuard(protoBufEdge.getGuard()); +// setUpdate(protoBufEdge.getUpdate()); +// setSync(protoBufEdge.getSync()); +// +// for (ComponentProtos.Nail protoBufNail : protoBufEdge.getNailList()) { +// getNails().add(new Nail(protoBufNail)); +// } +// } public String getSync() { return sync.get(); diff --git a/src/main/java/ecdar/abstractions/Location.java b/src/main/java/ecdar/abstractions/Location.java index 1d0ca6ab..61ebc191 100644 --- a/src/main/java/ecdar/abstractions/Location.java +++ b/src/main/java/ecdar/abstractions/Location.java @@ -88,20 +88,21 @@ public Location(final JsonObject jsonObject) { bindReachabilityAnalysis(); } - public Location(ComponentProtos.Location protoBufLocation) { - setId(protoBufLocation.getId()); - setNickname(protoBufLocation.getNickname()); - setInvariant(protoBufLocation.getInvariant()); - setType(Type.valueOf(protoBufLocation.getType())); - setUrgency(Urgency.valueOf(protoBufLocation.getUrgency())); - setX(protoBufLocation.getX()); - setY(protoBufLocation.getY()); - setColor(Color.valueOf(protoBufLocation.getColor())); - setNicknameX(protoBufLocation.getNicknameX()); - setNicknameY(protoBufLocation.getNicknameY()); - setInvariantX(protoBufLocation.getInvariantX()); - setInvariantY(protoBufLocation.getInvariantY()); - } + // ToDo NIELS: Comment in, when location should be received through ProtoBuf +// public Location(ComponentProtos.Location protoBufLocation) { +// setId(protoBufLocation.getId()); +// setNickname(protoBufLocation.getNickname()); +// setInvariant(protoBufLocation.getInvariant()); +// setType(Type.valueOf(protoBufLocation.getType())); +// setUrgency(Urgency.valueOf(protoBufLocation.getUrgency())); +// setX(protoBufLocation.getX()); +// setY(protoBufLocation.getY()); +// setColor(Color.valueOf(protoBufLocation.getColor())); +// setNicknameX(protoBufLocation.getNicknameX()); +// setNicknameY(protoBufLocation.getNicknameY()); +// setInvariantX(protoBufLocation.getInvariantX()); +// setInvariantY(protoBufLocation.getInvariantY()); +// } /** * Generates an id for this, and binds reachability analysis. diff --git a/src/main/java/ecdar/abstractions/Nail.java b/src/main/java/ecdar/abstractions/Nail.java index 8c7cebbe..f7555432 100644 --- a/src/main/java/ecdar/abstractions/Nail.java +++ b/src/main/java/ecdar/abstractions/Nail.java @@ -40,11 +40,12 @@ public Nail(final JsonObject jsonObject) { deserialize(jsonObject); } - public Nail(ComponentProtos.Nail protoBufNail) { - setPropertyType(DisplayableEdge.PropertyType.valueOf(protoBufNail.getPropertyType())); - setPropertyX(protoBufNail.getPropertyX()); - setPropertyY(protoBufNail.getPropertyY()); - } + // ToDo NIELS: Comment in, when location should be received through ProtoBuf +// public Nail(ComponentProtos.Nail protoBufNail) { +// setPropertyType(DisplayableEdge.PropertyType.valueOf(protoBufNail.getPropertyType())); +// setPropertyX(protoBufNail.getPropertyX()); +// setPropertyY(protoBufNail.getPropertyY()); +// } public double getX() { return x.get(); diff --git a/src/main/java/ecdar/abstractions/Transition.java b/src/main/java/ecdar/abstractions/Transition.java index 829d2300..9b9d25be 100644 --- a/src/main/java/ecdar/abstractions/Transition.java +++ b/src/main/java/ecdar/abstractions/Transition.java @@ -1,18 +1,30 @@ package ecdar.abstractions; -import EcdarProtoBuf.ComponentProtos; import EcdarProtoBuf.ObjectProtos; +import ecdar.Ecdar; import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; public class Transition { public final ArrayList edges = new ArrayList<>(); public Transition(ObjectProtos.Transition protoBufTransition) { - List protoBufEdges = (List) protoBufTransition.getField(ObjectProtos.Transition.getDescriptor().findFieldByName("edges")); - for (ComponentProtos.Edge protoBufEdge : protoBufEdges) { - edges.add(new Edge(protoBufEdge)); - } + List protoBufEdges = (List) protoBufTransition.getField(ObjectProtos.Transition.getDescriptor().findFieldByName("edges")); + List componentNames = protoBufEdges.stream().map(ObjectProtos.Transition.EdgeTuple::getComponentName).collect(Collectors.toList()); + List edgeIds = protoBufEdges.stream().map(ObjectProtos.Transition.EdgeTuple::getId).collect(Collectors.toList()); + + // For each affected component, find each affected edge within that component, and add these edges to the edges list + List affectedComponents = Ecdar.getProject().getComponents().stream().filter(c -> componentNames.contains(c.getName())).collect(Collectors.toList()); + edges.addAll(affectedComponents.stream() + .map(c -> c.getEdges() + .stream() + .filter(e -> edgeIds.contains(e.getId())) + .collect(Collectors.toList())) + .flatMap(Collection::stream) + .collect(Collectors.toList()) + ); } } diff --git a/src/main/proto b/src/main/proto index 42084f76..661d35fb 160000 --- a/src/main/proto +++ b/src/main/proto @@ -1 +1 @@ -Subproject commit 42084f767e42c8c4b9285c04342ace57645448a3 +Subproject commit 661d35fb5d483b86c6cd60438b3282b4035e4136 diff --git a/src/test/java/ecdar/simulation/SimulationTest.java b/src/test/java/ecdar/simulation/SimulationTest.java new file mode 100644 index 00000000..5d0f8384 --- /dev/null +++ b/src/test/java/ecdar/simulation/SimulationTest.java @@ -0,0 +1,10 @@ +package ecdar.simulation; + +import org.junit.Test; + +public class SimulationTest { + @Test + public void intialStep() { + assert false; + } +} From 4f075560f928081c4db016aff8cb2a98036aa605 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Fri, 15 Jul 2022 10:44:59 +0200 Subject: [PATCH 010/120] TestFX added and protos updated --- build.gradle | 1 + src/main/proto | 2 +- src/test/java/ecdar/TestFXBase.java | 30 ++++++ .../java/ecdar/simulation/SimulationTest.java | 101 +++++++++++++++++- 4 files changed, 129 insertions(+), 5 deletions(-) create mode 100644 src/test/java/ecdar/TestFXBase.java diff --git a/build.gradle b/build.gradle index e8b3ecfb..efb9b4ca 100644 --- a/build.gradle +++ b/build.gradle @@ -48,6 +48,7 @@ dependencies { implementation fileTree(dir: 'lib', include: ['*.jar']) testImplementation group: 'junit', name: 'junit', version: '4.13.2' + testImplementation 'org.testfx:testfx-junit:4.0.15-alpha' implementation 'com.jfoenix:jfoenix:9.0.10' implementation group: 'de.codecentric.centerdevice', name: 'javafxsvg', version: '1.3.0' implementation group: 'com.github.jiconfont', name: 'jiconfont-javafx', version: '1.0.0' diff --git a/src/main/proto b/src/main/proto index 661d35fb..3e021fbf 160000 --- a/src/main/proto +++ b/src/main/proto @@ -1 +1 @@ -Subproject commit 661d35fb5d483b86c6cd60438b3282b4035e4136 +Subproject commit 3e021fbf9457d6f29b1af21d1468692ec4110748 diff --git a/src/test/java/ecdar/TestFXBase.java b/src/test/java/ecdar/TestFXBase.java new file mode 100644 index 00000000..8dc71b74 --- /dev/null +++ b/src/test/java/ecdar/TestFXBase.java @@ -0,0 +1,30 @@ +package ecdar; + +import javafx.scene.input.KeyCode; +import javafx.scene.input.MouseButton; +import javafx.stage.Stage; +import org.junit.After; +import org.junit.Before; +import org.testfx.api.FxToolkit; +import org.testfx.framework.junit.ApplicationTest; + +import java.util.concurrent.TimeoutException; + +public class TestFXBase extends ApplicationTest { + @Before + public void setUpClass() throws Exception { + ApplicationTest.launch(Ecdar.class); + } + + @Override + public void start(Stage stage) { + stage.show(); + } + + @After + public void afterEachTest() throws TimeoutException { + FxToolkit.hideStage(); + release(new KeyCode[]{}); + release(new MouseButton[]{}); + } +} diff --git a/src/test/java/ecdar/simulation/SimulationTest.java b/src/test/java/ecdar/simulation/SimulationTest.java index 5d0f8384..b25a1d61 100644 --- a/src/test/java/ecdar/simulation/SimulationTest.java +++ b/src/test/java/ecdar/simulation/SimulationTest.java @@ -1,10 +1,103 @@ package ecdar.simulation; -import org.junit.Test; +import EcdarProtoBuf.EcdarBackendGrpc; +import EcdarProtoBuf.ObjectProtos; +import EcdarProtoBuf.QueryProtos; +import ecdar.TestFXBase; +import ecdar.abstractions.Component; +import ecdar.abstractions.Location; +import io.grpc.BindableService; +import io.grpc.ManagedChannel; +import io.grpc.Server; +import io.grpc.Status; +import io.grpc.inprocess.InProcessChannelBuilder; +import io.grpc.inprocess.InProcessServerBuilder; +import io.grpc.stub.StreamObserver; +import io.grpc.testing.GrpcCleanupRule; +import org.junit.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.Assert.fail; + +public class SimulationTest extends TestFXBase { + @Rule + public GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); + private final String serverName = InProcessServerBuilder.generateName(); -public class SimulationTest { @Test - public void intialStep() { - assert false; + public void testGetInitialStateHighlightsTheInitialLocation() { + final List components = generateComponentsWithInitialLocations(); + final SimulationHandler sh = new SimulationHandler(); + + BindableService testService = new EcdarBackendGrpc.EcdarBackendImplBase() { + @Override + public void startSimulation(QueryProtos.SimulationStartRequest request, + StreamObserver responseObserver) { + try { + ObjectProtos.StateTuple state = ObjectProtos.StateTuple.newBuilder().addAllLocations(components.stream() + .map(c -> ObjectProtos.StateTuple.LocationTuple.newBuilder() + .setComponentName(c.getName()) + .setId(c.getInitialLocation().getId()) + .build()) + .collect(Collectors.toList())).build(); + + QueryProtos.SimulationStepResponse response = QueryProtos.SimulationStepResponse.newBuilder().setState(state).build(); + responseObserver.onNext(response); + responseObserver.onCompleted(); + } catch (Throwable e) { + responseObserver.onError(Status.INVALID_ARGUMENT.withDescription(e.getMessage()).asException()); + } + } + + @Override + public void takeSimulationStep(EcdarProtoBuf.QueryProtos.SimulationStepRequest request, + io.grpc.stub.StreamObserver responseObserver) { + } + }; + + final Server server; + final ManagedChannel channel; + final EcdarBackendGrpc.EcdarBackendBlockingStub stub; + try { + server = grpcCleanup.register(InProcessServerBuilder + .forName(serverName).directExecutor().addService(testService).build().start()); + channel = grpcCleanup.register(InProcessChannelBuilder + .forName(serverName).directExecutor().build()); + stub = EcdarBackendGrpc.newBlockingStub(channel); + QueryProtos.SimulationStartRequest request = QueryProtos.SimulationStartRequest.newBuilder().setSystem("(A || B)").build(); + + var expectedResponse = new ObjectProtos.StateTuple.LocationTuple[components.size()]; + + for (int i = 0; i < components.size(); i++) { + Component comp = components.get(i); + expectedResponse[i] = ObjectProtos.StateTuple.LocationTuple.newBuilder() + .setComponentName(comp.getName()) + .setId(comp.getInitialLocation().getId()).build(); + } + + var result = stub.startSimulation(request).getState().getLocationsList().toArray(); + + Assert.assertArrayEquals(expectedResponse, result); + } catch (IOException e) { + fail("Exception encountered: " + e.getMessage()); + } + } + + private List generateComponentsWithInitialLocations() { + List comps = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + var comp = new Component(); + comp.setName(comp + "_" + i); + var loc = new Location(comp + "_initial"); + loc.setType(Location.Type.INITIAL); + comp.addLocation(loc); + comps.add(comp); + } + + return comps; } } From e7d6f93c94f05de8b254e3ddc78005fc80885f55 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Fri, 15 Jul 2022 11:18:12 +0200 Subject: [PATCH 011/120] JUnit5 upgrade --- build.gradle | 2 +- src/test/java/ecdar/TestFXBase.java | 10 +++++----- src/test/java/ecdar/simulation/SimulationTest.java | 10 ++++------ 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index efb9b4ca..efaa8ceb 100644 --- a/build.gradle +++ b/build.gradle @@ -47,7 +47,7 @@ def protocVersion = protobufVersion dependencies { implementation fileTree(dir: 'lib', include: ['*.jar']) - testImplementation group: 'junit', name: 'junit', version: '4.13.2' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.8.2' testImplementation 'org.testfx:testfx-junit:4.0.15-alpha' implementation 'com.jfoenix:jfoenix:9.0.10' implementation group: 'de.codecentric.centerdevice', name: 'javafxsvg', version: '1.3.0' diff --git a/src/test/java/ecdar/TestFXBase.java b/src/test/java/ecdar/TestFXBase.java index 8dc71b74..e54eecec 100644 --- a/src/test/java/ecdar/TestFXBase.java +++ b/src/test/java/ecdar/TestFXBase.java @@ -3,16 +3,16 @@ import javafx.scene.input.KeyCode; import javafx.scene.input.MouseButton; import javafx.stage.Stage; -import org.junit.After; -import org.junit.Before; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.testfx.api.FxToolkit; import org.testfx.framework.junit.ApplicationTest; import java.util.concurrent.TimeoutException; public class TestFXBase extends ApplicationTest { - @Before - public void setUpClass() throws Exception { + @BeforeAll + static void setUp() throws Exception { ApplicationTest.launch(Ecdar.class); } @@ -21,7 +21,7 @@ public void start(Stage stage) { stage.show(); } - @After + @AfterAll public void afterEachTest() throws TimeoutException { FxToolkit.hideStage(); release(new KeyCode[]{}); diff --git a/src/test/java/ecdar/simulation/SimulationTest.java b/src/test/java/ecdar/simulation/SimulationTest.java index b25a1d61..2acb8136 100644 --- a/src/test/java/ecdar/simulation/SimulationTest.java +++ b/src/test/java/ecdar/simulation/SimulationTest.java @@ -14,24 +14,22 @@ import io.grpc.inprocess.InProcessServerBuilder; import io.grpc.stub.StreamObserver; import io.grpc.testing.GrpcCleanupRule; -import org.junit.*; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; -import static org.junit.Assert.fail; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; public class SimulationTest extends TestFXBase { - @Rule public GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); private final String serverName = InProcessServerBuilder.generateName(); @Test public void testGetInitialStateHighlightsTheInitialLocation() { final List components = generateComponentsWithInitialLocations(); - final SimulationHandler sh = new SimulationHandler(); BindableService testService = new EcdarBackendGrpc.EcdarBackendImplBase() { @Override @@ -81,9 +79,9 @@ public void takeSimulationStep(EcdarProtoBuf.QueryProtos.SimulationStepRequest r var result = stub.startSimulation(request).getState().getLocationsList().toArray(); - Assert.assertArrayEquals(expectedResponse, result); + Assertions.assertArrayEquals(expectedResponse, result); } catch (IOException e) { - fail("Exception encountered: " + e.getMessage()); + Assertions.fail("Exception encountered: " + e.getMessage()); } } From 17edbc61bd18c5c8556d6ab096da757fe99dd72e Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Tue, 9 Aug 2022 11:54:24 +0200 Subject: [PATCH 012/120] Comment updated in preparation for query implementation in simulator view --- .../resources/ecdar/presentations/RightSimPanePresentation.fxml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml b/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml index 4697fb97..6f3d588c 100755 --- a/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml +++ b/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml @@ -17,7 +17,7 @@ AnchorPane.rightAnchor="0" styleClass="edge-to-edge"> - + From 7a9a04e09312f66d758915864567edd0b3de9b22 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Fri, 19 Aug 2022 12:56:29 +0200 Subject: [PATCH 013/120] WIP: Toggle for GUI mode added (sizing of toggle not working) --- .../ecdar/controllers/EcdarController.java | 40 ++++++++----------- src/main/resources/ecdar/main.css | 20 +++++++++- .../presentations/EcdarPresentation.fxml | 31 +++++++++++--- 3 files changed, 62 insertions(+), 29 deletions(-) diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index 39384a9d..77237498 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -120,6 +120,7 @@ public class EcdarController implements Initializable { public MenuItem menuBarHelpAbout; public MenuItem menuBarHelpTest; + public JFXToggleButton switchGuiView; public Snackbar snackbar; public HBox statusBar; public Label statusLabel; @@ -255,6 +256,8 @@ private void scaleIcons(Node node, double size) { Set xSmallIcons = node.lookupAll(".icon-size-x-small"); for (Node icon : xSmallIcons) icon.setStyle("-fx-icon-size: " + Math.floor(size / 13.0 * 18) + "px;"); + + switchGuiView.setSize(Math.floor(size / 13.0 * 24)); } private double getNewCalculatedScale() { @@ -646,11 +649,13 @@ private void initializeViewMenu() { } }); - menuBarViewEditor.setAccelerator(new KeyCodeCombination(KeyCode.DIGIT1, KeyCombination.SHORTCUT_DOWN)); - menuBarViewEditor.setOnAction(event -> editorRipplerClicked()); - - menuBarViewSimulator.setAccelerator(new KeyCodeCombination(KeyCode.DIGIT2, KeyCombination.SHORTCUT_DOWN)); - menuBarViewSimulator.setOnAction(event -> simulatorRipplerClicked()); + switchGuiView.selectedProperty().addListener((observable, oldValue, newValue) -> { + if (newValue) { + currentMode.setValue(Mode.Simulator); + } else { + currentMode.setValue(Mode.Editor); + } + }); currentMode.addListener((obs, oldMode, newMode) -> { if (newMode == Mode.Editor && oldMode != newMode) { @@ -660,6 +665,13 @@ private void initializeViewMenu() { } }); + menuBarViewEditor.setAccelerator(new KeyCodeCombination(KeyCode.DIGIT1, KeyCombination.SHORTCUT_DOWN)); + menuBarViewEditor.setOnAction(event -> switchGuiView.setSelected(false)); + + menuBarViewSimulator.setAccelerator(new KeyCodeCombination(KeyCode.DIGIT2, KeyCombination.SHORTCUT_DOWN)); + menuBarViewSimulator.setOnAction(event -> switchGuiView.setSelected(true)); + + // On startup, set the scaling to the values saved in preferences Platform.runLater(() -> { Ecdar.autoScalingEnabled.setValue(Ecdar.preferences.getBoolean("autoscaling", true)); @@ -954,24 +966,6 @@ private void initializeFileExportAsPng() { }); } - /** - * Method for click on the Editor rippler. Changes mode to the editor - */ - private void editorRipplerClicked() { - if (currentMode.get() != Mode.Editor) { - currentMode.setValue(Mode.Editor); - } - } - - /** - * Method for click on the Simulator rippler. Changes mode to the simulator - */ - private void simulatorRipplerClicked() { - if (currentMode.get() != Mode.Simulator) { - currentMode.setValue(Mode.Simulator); - } - } - /** * Changes the view and mode to the editor * Only enter if the mode is not already Editor diff --git a/src/main/resources/ecdar/main.css b/src/main/resources/ecdar/main.css index 5a29ca08..eb0baccc 100644 --- a/src/main/resources/ecdar/main.css +++ b/src/main/resources/ecdar/main.css @@ -290,6 +290,24 @@ -fx-font-size: 1em; } +.large-toggle-button { + -jfx-toggle-color:#dddddd; -jfx-untoggle-color:#dddddd; + -jfx-toggle-line-color:#7C8B92; -jfx-untoggle-line-color:#7C8B92; + -fx-opacity: 0.5; + -jfx-size: 1.6em; + -fx-font-size: 1em; +} + +.editor-simulator-toggle { + -fx-min-width: 8em; + -fx-max-width: 8em; + -fx-min-height: 4em; + -fx-max-height: 4em; + -fx-background-color: -blue-grey-800; + -fx-border-radius: 0 0 10 10; + -fx-background-radius: 0 0 10 10; +} + /* Response sizing for elements (icons with these classes will be scaled when scaling changes) */ .icon-size-medium { -fx-icon-size: 24; @@ -344,4 +362,4 @@ .responsive-circle-radius { -fx-scale-x: 1em; -fx-scale-y: 1em; -} \ No newline at end of file +} diff --git a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml index be278b8e..6b30f2b5 100644 --- a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml +++ b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml @@ -10,6 +10,7 @@ + - + @@ -94,12 +96,14 @@ - + - + @@ -110,7 +114,8 @@ - + @@ -180,7 +185,8 @@ - +
@@ -230,6 +236,21 @@
+ + + + + + + + + + + + + + + From 49d216f6694ccf08b60fb2166e4de42bfe833768 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Mon, 22 Aug 2022 10:49:32 +0200 Subject: [PATCH 014/120] WIP: Dialog for initializing simulation added --- .../ecdar/controllers/EcdarController.java | 56 +++++++++++++++---- ...ulationInitializationDialogController.java | 18 ++++++ .../controllers/SimulatorController.java | 3 +- .../presentations/EcdarPresentation.java | 4 +- ...ationInitializationDialogPresentation.java | 17 ++++++ .../ecdar/simulation/SimulationHandler.java | 4 ++ .../presentations/EcdarPresentation.fxml | 9 ++- ...ationInitializationDialogPresentation.fxml | 43 ++++++++++++++ 8 files changed, 139 insertions(+), 15 deletions(-) create mode 100644 src/main/java/ecdar/controllers/SimulationInitializationDialogController.java create mode 100644 src/main/java/ecdar/presentations/SimulationInitializationDialogPresentation.java create mode 100644 src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index 77237498..62fd0b49 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -46,6 +46,7 @@ import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.stream.Collectors; public class EcdarController implements Initializable { // Reachability analysis @@ -61,8 +62,8 @@ public class EcdarController implements Initializable { public StackPane rightPane; public Rectangle bottomFillerElement; public MessageTabPanePresentation messageTabPane; - public StackPane dialogContainer; - public JFXDialog dialog; + public StackPane modellingHelpDialogContainer; + public JFXDialog modellingHelpDialog; public StackPane modalBar; public JFXTextField queryTextField; public JFXTextField commentTextField; @@ -135,6 +136,9 @@ public class EcdarController implements Initializable { public StackPane backendOptionsDialogContainer; public BackendOptionsDialogPresentation backendOptionsDialog; + public StackPane simulationInitializationDialogContainer; + public SimulationInitializationDialogPresentation simulationInitializationDialog; + public final DoubleProperty scalingProperty = new SimpleDoubleProperty(); private static JFXDialog _queryDialog; @@ -265,30 +269,44 @@ private double getNewCalculatedScale() { } private void initializeDialogs() { - dialog.setDialogContainer(dialogContainer); - dialogContainer.opacityProperty().bind(dialog.getChildren().get(0).scaleXProperty()); - dialog.setOnDialogClosed(event -> dialogContainer.setVisible(false)); + modellingHelpDialog.setDialogContainer(modellingHelpDialogContainer); + modellingHelpDialogContainer.opacityProperty().bind(modellingHelpDialog.getChildren().get(0).scaleXProperty()); + modellingHelpDialog.setOnDialogClosed(event -> modellingHelpDialogContainer.setVisible(false)); _queryDialog = queryDialog; _queryTextResult = queryTextResult; _queryTextQuery = queryTextQuery; initializeDialog(queryDialog, queryDialogContainer); - initializeDialog(backendOptionsDialog, backendOptionsDialogContainer); + initializeBackendOptionsDialog(); + + initializeDialog(simulationInitializationDialog, simulationInitializationDialogContainer); + simulationInitializationDialog.getController().cancelButton.setOnMouseClicked(event -> { + switchGuiView.setSelected(false); + simulationInitializationDialog.close(); + }); + + simulationInitializationDialog.getController().startButton.setOnMouseClicked(event -> { + // ToDo NIELS: Start simulation of selected query + currentMode.setValue(Mode.Simulator); + simulationInitializationDialog.close(); + }); + } + + private void initializeBackendOptionsDialog() { + initializeDialog(backendOptionsDialog, backendOptionsDialogContainer); backendOptionsDialog.getController().resetBackendsButton.setOnMouseClicked(event -> { backendOptionsDialog.getController().resetBackendsToDefault(); }); backendOptionsDialog.getController().closeButton.setOnMouseClicked(event -> { backendOptionsDialog.getController().cancelBackendOptionsChanges(); - dialog.close(); backendOptionsDialog.close(); }); backendOptionsDialog.getController().saveButton.setOnMouseClicked(event -> { if (backendOptionsDialog.getController().saveChangesToBackendOptions()) { - dialog.close(); backendOptionsDialog.close(); } }); @@ -651,7 +669,25 @@ private void initializeViewMenu() { switchGuiView.selectedProperty().addListener((observable, oldValue, newValue) -> { if (newValue) { - currentMode.setValue(Mode.Simulator); + if (Ecdar.getProject().getQueries().isEmpty()) { + Ecdar.showToast("Please add a query to simulate before entering the simulator"); + switchGuiView.setSelected(false); + return; + } + + if (!Ecdar.getSimulationHandler().isSimulationRunning()) { + ArrayList queryOptions = Ecdar.getProject().getQueries().stream().map(Query::getQuery).collect(Collectors.toCollection(ArrayList::new)); + + if (!simulationInitializationDialog.getController().simulationComboBox.getItems().equals(queryOptions)) { + simulationInitializationDialog.getController().simulationComboBox.getItems().setAll(queryOptions); + } + + simulationInitializationDialogContainer.setVisible(true); + simulationInitializationDialog.show(simulationInitializationDialogContainer); + simulationInitializationDialog.setMouseTransparent(false); + } else { + currentMode.setValue(Mode.Simulator); + } } else { currentMode.setValue(Mode.Editor); } @@ -1214,7 +1250,7 @@ private void nudgeSelected(final NudgeDirection direction) { @FXML private void closeQueryDialog() { - dialog.close(); + modellingHelpDialog.close(); queryDialog.close(); } diff --git a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java new file mode 100644 index 00000000..1eb1529c --- /dev/null +++ b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java @@ -0,0 +1,18 @@ +package ecdar.controllers; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXComboBox; +import javafx.fxml.Initializable; + +import java.net.URL; +import java.util.ResourceBundle; + +public class SimulationInitializationDialogController implements Initializable { + public JFXComboBox simulationComboBox; + public JFXButton cancelButton; + public JFXButton startButton; + + public void initialize(URL location, ResourceBundle resources) { + + } +} diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index 639c1ff1..bdba66b4 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -40,7 +40,7 @@ public void initialize(URL location, ResourceBundle resources) { /** * Prepares the simulator to be shown.
* It also prepares the processes to be shown in the {@link SimulatorOverviewPresentation} by:
- * - Building the system if it has been updated or never have been created.
+ * - Building the system if it has been updated or have never been created.
* - Adding the components which are going to be used in the simulation to */ public void willShow() { @@ -60,6 +60,7 @@ public void willShow() { } if (shouldSimulationBeReset || firstTimeInSimulator) { + resetSimulation(); sm.resetToInitialLocation(); } diff --git a/src/main/java/ecdar/presentations/EcdarPresentation.java b/src/main/java/ecdar/presentations/EcdarPresentation.java index 7fa6b343..0648b67d 100644 --- a/src/main/java/ecdar/presentations/EcdarPresentation.java +++ b/src/main/java/ecdar/presentations/EcdarPresentation.java @@ -332,8 +332,8 @@ public void showSnackbarMessage(final String message) { } public void showHelp() { - controller.dialogContainer.setVisible(true); - controller.dialog.show(controller.dialogContainer); + controller.modellingHelpDialogContainer.setVisible(true); + controller.modellingHelpDialog.show(controller.modellingHelpDialogContainer); } public EcdarController getController() { diff --git a/src/main/java/ecdar/presentations/SimulationInitializationDialogPresentation.java b/src/main/java/ecdar/presentations/SimulationInitializationDialogPresentation.java new file mode 100644 index 00000000..677217bd --- /dev/null +++ b/src/main/java/ecdar/presentations/SimulationInitializationDialogPresentation.java @@ -0,0 +1,17 @@ +package ecdar.presentations; + +import com.jfoenix.controls.JFXDialog; +import ecdar.controllers.BackendOptionsDialogController; +import ecdar.controllers.SimulationInitializationDialogController; + +public class SimulationInitializationDialogPresentation extends JFXDialog { + private final SimulationInitializationDialogController controller; + + public SimulationInitializationDialogPresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("SimulationInitializationDialogPresentation.fxml", this); + } + + public SimulationInitializationDialogController getController() { + return controller; + } +} diff --git a/src/main/java/ecdar/simulation/SimulationHandler.java b/src/main/java/ecdar/simulation/SimulationHandler.java index 395d5027..bb894b62 100755 --- a/src/main/java/ecdar/simulation/SimulationHandler.java +++ b/src/main/java/ecdar/simulation/SimulationHandler.java @@ -403,4 +403,8 @@ public EcdarSystem getSystem() { public String getCurrentSimulation() { return currentSimulation; } + + public boolean isSimulationRunning() { + return false; // ToDo NIELS: Handle logic for determining whether a simulation is currently running + } } \ No newline at end of file diff --git a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml index 6b30f2b5..429ea99f 100644 --- a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml +++ b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml @@ -311,8 +311,8 @@ - - + + @@ -494,4 +494,9 @@ + + + + +
diff --git a/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml b/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml new file mode 100644 index 00000000..332e082d --- /dev/null +++ b/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + Simulation Initialization + + + + + + + + + + + + + + + + + + + From 848fc69fbefc14e5ecd373ef9ce1079a5bf69876 Mon Sep 17 00:00:00 2001 From: APaludan Date: Thu, 22 Sep 2022 11:32:26 +0200 Subject: [PATCH 015/120] Show tooltip when query was unsuccessful --- src/main/java/ecdar/presentations/QueryPresentation.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/ecdar/presentations/QueryPresentation.java b/src/main/java/ecdar/presentations/QueryPresentation.java index db1d9179..b3d2138c 100644 --- a/src/main/java/ecdar/presentations/QueryPresentation.java +++ b/src/main/java/ecdar/presentations/QueryPresentation.java @@ -196,7 +196,11 @@ private void initializeStateIndicator() { } else { this.tooltip.setText("The component has been created (can be accessed in the project pane)"); } - } else if (queryState.getStatusCode() == 3) { + } + else if (queryState.getStatusCode() == 2){ + this.tooltip.setText("This query was not a success!"); + } + else if (queryState.getStatusCode() == 3) { this.tooltip.setText("The query has not been executed yet"); } else { this.tooltip.setText(controller.getQuery().getCurrentErrors()); From 28fb8f797de9e11338d5de5b6abd3e721f147401 Mon Sep 17 00:00:00 2001 From: jhbengtsson Date: Thu, 22 Sep 2022 11:41:51 +0200 Subject: [PATCH 016/120] replacement af >= <= med deres unicode --- src/main/java/ecdar/abstractions/Query.java | 21 +++++++++++++---- .../ecdar/controllers/QueryController.java | 23 ++++++++++++++++++- .../presentations/QueryPresentation.fxml | 2 +- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index 2fd0b7f3..2294a3a4 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -33,11 +33,10 @@ public class Query implements Serializable { private Runnable runQuery; public Query(final String query, final String comment, final QueryState queryState) { - this.query.set(query); + this.setQuery(query); this.comment.set(comment); this.queryState.set(queryState); setBackend(BackendHelper.getDefaultBackendInstance()); - initializeRunQuery(); } @@ -64,7 +63,22 @@ public String getQuery() { } public void setQuery(final String query) { - this.query.set(query); + String newQuery = ""; + boolean hasBeenTrimmed = false; + if(query.contains("\u2264")){ + newQuery = query.replace("\u2264","<="); + hasBeenTrimmed = true; + this.query.setValue(newQuery); + } + if(query.contains("\u2265")){ + newQuery = query.replace("\u2265",">="); + hasBeenTrimmed = true; + this.query.setValue(newQuery); + } + if(!hasBeenTrimmed){ + this.query.set(query); + } + } public StringProperty queryProperty() { @@ -124,7 +138,6 @@ private void initializeRunQuery() { setQueryState(QueryState.RUNNING); forcedCancel = false; errors.set(""); - if (getQuery().isEmpty()) { setQueryState(QueryState.SYNTAX_ERROR); this.addError("Query is empty"); diff --git a/src/main/java/ecdar/controllers/QueryController.java b/src/main/java/ecdar/controllers/QueryController.java index a3f78280..ae5521bb 100644 --- a/src/main/java/ecdar/controllers/QueryController.java +++ b/src/main/java/ecdar/controllers/QueryController.java @@ -2,6 +2,7 @@ import com.jfoenix.controls.JFXComboBox; import com.jfoenix.controls.JFXRippler; +import com.jfoenix.controls.JFXTextField; import ecdar.abstractions.BackendInstance; import ecdar.abstractions.Query; import ecdar.abstractions.QueryType; @@ -9,11 +10,13 @@ import ecdar.utility.colors.Color; import javafx.application.Platform; import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.value.ObservableValue; +import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Tooltip; import javafx.scene.text.Text; import org.kordamp.ikonli.javafx.FontIcon; - +import javafx.beans.value.ChangeListener; import java.net.URL; import java.util.HashMap; import java.util.Map; @@ -27,10 +30,28 @@ public class QueryController implements Initializable { private Query query; private final Map queryTypeListElementsSelectedState = new HashMap<>(); private final Tooltip noQueryTypeSetTooltip = new Tooltip("Please select a query type beneath the status icon"); + @FXML + private JFXTextField queryText; @Override public void initialize(URL location, ResourceBundle resources) { initializeActionButton(); + + queryText.textProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, String oldValue, String newValue) { + String tmpValue = ""; + if(newValue.contains("<=")){ + tmpValue = newValue.replace("<=","\u2264"); + queryText.setText(tmpValue); + } + if(newValue.contains(">=")){ + tmpValue = newValue.replace(">=","\u2265"); + queryText.setText(tmpValue); + } + } + }); + } public void setQuery(Query query) { diff --git a/src/main/resources/ecdar/presentations/QueryPresentation.fxml b/src/main/resources/ecdar/presentations/QueryPresentation.fxml index 223e15df..94e1687d 100644 --- a/src/main/resources/ecdar/presentations/QueryPresentation.fxml +++ b/src/main/resources/ecdar/presentations/QueryPresentation.fxml @@ -33,7 +33,7 @@ - Date: Thu, 22 Sep 2022 13:53:11 +0200 Subject: [PATCH 017/120] Modify getter of query, such that it complies with the backend expected format --- src/main/java/ecdar/abstractions/Query.java | 23 +++++++++++---------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index 2294a3a4..d59c9b84 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -59,26 +59,27 @@ public ObjectProperty queryStateProperty() { } public String getQuery() { - return query.get(); - } - - public void setQuery(final String query) { String newQuery = ""; + String oldQuery = this.query.get(); boolean hasBeenTrimmed = false; - if(query.contains("\u2264")){ - newQuery = query.replace("\u2264","<="); + + if(oldQuery.contains("\u2264")){ + newQuery = oldQuery.replace("\u2264","<="); hasBeenTrimmed = true; - this.query.setValue(newQuery); } - if(query.contains("\u2265")){ - newQuery = query.replace("\u2265",">="); + if(oldQuery.contains("\u2265")){ + newQuery = oldQuery.replace("\u2265",">="); hasBeenTrimmed = true; - this.query.setValue(newQuery); } if(!hasBeenTrimmed){ - this.query.set(query); + return oldQuery; } + return newQuery; + } + + public void setQuery(final String query) { + this.query.set(query); } public StringProperty queryProperty() { From 4ded5e7a0920ce98f3110db0642b52beaa640bde Mon Sep 17 00:00:00 2001 From: jhbengtsson Date: Thu, 22 Sep 2022 16:01:53 +0200 Subject: [PATCH 018/120] Refinement symbol replaced with unicode in dropdown --- .../java/ecdar/abstractions/QueryType.java | 2 +- .../java/ecdar/abstractions/QueryTest.java | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/test/java/ecdar/abstractions/QueryTest.java diff --git a/src/main/java/ecdar/abstractions/QueryType.java b/src/main/java/ecdar/abstractions/QueryType.java index 98af6fe1..0ea4b1f2 100644 --- a/src/main/java/ecdar/abstractions/QueryType.java +++ b/src/main/java/ecdar/abstractions/QueryType.java @@ -2,7 +2,7 @@ public enum QueryType { REACHABILITY("reachability", "E<>"), - REFINEMENT("refinement", "<="), + REFINEMENT("refinement", "\u2264"), QUOTIENT("quotient", "\\"), SPECIFICATION("specification", "Spec"), IMPLEMENTATION("implementation", "Imp"), diff --git a/src/test/java/ecdar/abstractions/QueryTest.java b/src/test/java/ecdar/abstractions/QueryTest.java new file mode 100644 index 00000000..645a61ec --- /dev/null +++ b/src/test/java/ecdar/abstractions/QueryTest.java @@ -0,0 +1,19 @@ +package ecdar.abstractions; + +import ecdar.Ecdar; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; + +public class QueryTest { + @BeforeAll + static void setup() { + Ecdar.setUpForTest(); + } + + @Test + public void testGetQuery() { + + + } +} From b0028ecacb22be360842223f205da45f97fb2ae2 Mon Sep 17 00:00:00 2001 From: APaludan Date: Mon, 26 Sep 2022 14:39:24 +0200 Subject: [PATCH 019/120] fix typo --- src/main/java/ecdar/backend/BackendDriver.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index 8d6c7c09..a3ab0e1a 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -91,9 +91,9 @@ public void run() { backendConnection, componentsBuilder, QueryProtos.IgnoredInputOutputs.newBuilder().getDefaultInstanceForType(), - (reponse) -> { - if (reponse.hasQuery() && reponse.getQuery().hasIgnoredInputOutputs()) { - var ignoredInputOutputs = reponse.getQuery().getIgnoredInputOutputs(); + (response) -> { + if (response.hasQuery() && response.getQuery().hasIgnoredInputOutputs()) { + var ignoredInputOutputs = response.getQuery().getIgnoredInputOutputs(); query.addNewElementsToMap(new ArrayList<>(ignoredInputOutputs.getIgnoredInputsList()), new ArrayList<>(ignoredInputOutputs.getIgnoredOutputsList())); } else { // Response is unexpected, maybe just ignore From a1bed78289b67b3053093acca764b373d0698e6c Mon Sep 17 00:00:00 2001 From: jhbengtsson Date: Mon, 26 Sep 2022 14:40:02 +0200 Subject: [PATCH 020/120] unittest af getQuery(); --- .../ecdar/controllers/QueryController.java | 23 +++++++++++-------- .../java/ecdar/abstractions/QueryTest.java | 5 ++++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/java/ecdar/controllers/QueryController.java b/src/main/java/ecdar/controllers/QueryController.java index ae5521bb..dec3b0a6 100644 --- a/src/main/java/ecdar/controllers/QueryController.java +++ b/src/main/java/ecdar/controllers/QueryController.java @@ -40,20 +40,25 @@ public void initialize(URL location, ResourceBundle resources) { queryText.textProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, String oldValue, String newValue) { - String tmpValue = ""; - if(newValue.contains("<=")){ - tmpValue = newValue.replace("<=","\u2264"); - queryText.setText(tmpValue); - } - if(newValue.contains(">=")){ - tmpValue = newValue.replace(">=","\u2265"); - queryText.setText(tmpValue); - } + updateQueryTextFieldString(newValue); } }); } + //Method for replacing "<=" and ">=" with its unicode character + public void updateQueryTextFieldString(String newValue) { + String tmpValue = ""; + if(newValue.contains("<=")){ + tmpValue = newValue.replace("<=","\u2264"); + queryText.setText(tmpValue); + } + if(newValue.contains(">=")){ + tmpValue = newValue.replace(">=","\u2265"); + queryText.setText(tmpValue); + } + } + public void setQuery(Query query) { this.query = query; this.query.getTypeProperty().addListener(((observable, oldValue, newValue) -> { diff --git a/src/test/java/ecdar/abstractions/QueryTest.java b/src/test/java/ecdar/abstractions/QueryTest.java index 645a61ec..88d1e9fa 100644 --- a/src/test/java/ecdar/abstractions/QueryTest.java +++ b/src/test/java/ecdar/abstractions/QueryTest.java @@ -13,7 +13,12 @@ static void setup() { @Test public void testGetQuery() { + //Test that the query string from the query textfield is decoded correctly for the backend to use it + final Query query = new Query("(Administration || Machine || Researcher) \u2264 Spec)", "comment", QueryState.RUNNING); + String expected = "(Administration || Machine || Researcher) <= Spec)"; + String result = query.getQuery(); + Assertions.assertEquals(expected, result); } } From 03aac31e0cac5175ec4ee418e5032315aee2fdcf Mon Sep 17 00:00:00 2001 From: jhbengtsson Date: Thu, 29 Sep 2022 11:17:32 +0200 Subject: [PATCH 021/120] changeRefinementSymbols added to display >= and <= correctly --- .../ecdar/presentations/QueryPresentation.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/ecdar/presentations/QueryPresentation.java b/src/main/java/ecdar/presentations/QueryPresentation.java index db1d9179..17edd1d7 100644 --- a/src/main/java/ecdar/presentations/QueryPresentation.java +++ b/src/main/java/ecdar/presentations/QueryPresentation.java @@ -254,12 +254,27 @@ private void initializeStateIndicator() { if (controller.getQuery().getQuery().isEmpty()) return; Label label = new Label(tooltip.getText()); - JFXDialog dialog = new InformationDialogPresentation("Result from query: " + controller.getQuery().getQuery(), label); + //Make sure that certain symbols in the query are displayed correctly + String newString = changeRefinementSymbols(); + + JFXDialog dialog = new InformationDialogPresentation("Result from query: " + newString, label); dialog.show(Ecdar.getPresentation()); }); }); } + private String changeRefinementSymbols() { + String queryText = controller.getQuery().getQuery(); + String newString = ""; + if(queryText.contains("<=")){ + newString = queryText.replace("<=","\u2264"); + } + if(queryText.contains(">=")){ + newString = queryText.replace("<=","\u2265"); + } + return newString; + } + private void setStatusIndicatorContentColor(javafx.scene.paint.Color color, FontIcon statusIcon, FontIcon queryTypeExpandIcon, QueryState queryState) { statusIcon.setIconColor(color); controller.queryTypeSymbol.setFill(color); From 4238fb69990634b1ad2aab01089f488b03418ce9 Mon Sep 17 00:00:00 2001 From: APaludan Date: Fri, 30 Sep 2022 10:44:33 +0200 Subject: [PATCH 022/120] refactoring --- src/main/java/ecdar/abstractions/Query.java | 18 +----------------- .../ecdar/controllers/QueryController.java | 11 ++--------- .../ecdar/presentations/QueryPresentation.java | 10 +--------- 3 files changed, 4 insertions(+), 35 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index d59c9b84..04bf9d2a 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -59,23 +59,7 @@ public ObjectProperty queryStateProperty() { } public String getQuery() { - String newQuery = ""; - String oldQuery = this.query.get(); - boolean hasBeenTrimmed = false; - - if(oldQuery.contains("\u2264")){ - newQuery = oldQuery.replace("\u2264","<="); - hasBeenTrimmed = true; - } - if(oldQuery.contains("\u2265")){ - newQuery = oldQuery.replace("\u2265",">="); - hasBeenTrimmed = true; - } - if(!hasBeenTrimmed){ - return oldQuery; - } - - return newQuery; + return this.query.get().replace("\u2264","<=").replace("\u2265",">="); } public void setQuery(final String query) { diff --git a/src/main/java/ecdar/controllers/QueryController.java b/src/main/java/ecdar/controllers/QueryController.java index dec3b0a6..36a99909 100644 --- a/src/main/java/ecdar/controllers/QueryController.java +++ b/src/main/java/ecdar/controllers/QueryController.java @@ -48,15 +48,8 @@ public void changed(ObservableValue observable, String oldValu //Method for replacing "<=" and ">=" with its unicode character public void updateQueryTextFieldString(String newValue) { - String tmpValue = ""; - if(newValue.contains("<=")){ - tmpValue = newValue.replace("<=","\u2264"); - queryText.setText(tmpValue); - } - if(newValue.contains(">=")){ - tmpValue = newValue.replace(">=","\u2265"); - queryText.setText(tmpValue); - } + String tmpValue = newValue.replace(">=","\u2265").replace("<=","\u2264"); + queryText.setText(tmpValue); } public void setQuery(Query query) { diff --git a/src/main/java/ecdar/presentations/QueryPresentation.java b/src/main/java/ecdar/presentations/QueryPresentation.java index 17edd1d7..e3d9a4e3 100644 --- a/src/main/java/ecdar/presentations/QueryPresentation.java +++ b/src/main/java/ecdar/presentations/QueryPresentation.java @@ -264,15 +264,7 @@ private void initializeStateIndicator() { } private String changeRefinementSymbols() { - String queryText = controller.getQuery().getQuery(); - String newString = ""; - if(queryText.contains("<=")){ - newString = queryText.replace("<=","\u2264"); - } - if(queryText.contains(">=")){ - newString = queryText.replace("<=","\u2265"); - } - return newString; + return controller.getQuery().getQuery().replace("<=","\u2264").replace("<=","\u2265"); } private void setStatusIndicatorContentColor(javafx.scene.paint.Color color, FontIcon statusIcon, FontIcon queryTypeExpandIcon, QueryState queryState) { From 6297e2edfb7f39783206b4382fe7d20f6ee1781c Mon Sep 17 00:00:00 2001 From: jhbengtsson Date: Mon, 3 Oct 2022 11:03:24 +0200 Subject: [PATCH 023/120] =?UTF-8?q?ikke=20l=C3=A6ngere=20i=20en=20funktion?= =?UTF-8?q?=20men=20direkte=20i=20changelistener?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/ecdar/controllers/QueryController.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/main/java/ecdar/controllers/QueryController.java b/src/main/java/ecdar/controllers/QueryController.java index 36a99909..35342291 100644 --- a/src/main/java/ecdar/controllers/QueryController.java +++ b/src/main/java/ecdar/controllers/QueryController.java @@ -40,16 +40,10 @@ public void initialize(URL location, ResourceBundle resources) { queryText.textProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, String oldValue, String newValue) { - updateQueryTextFieldString(newValue); + String tmpValue = newValue.replace(">=","\u2265").replace("<=","\u2264"); + queryText.setText(tmpValue); } }); - - } - - //Method for replacing "<=" and ">=" with its unicode character - public void updateQueryTextFieldString(String newValue) { - String tmpValue = newValue.replace(">=","\u2265").replace("<=","\u2264"); - queryText.setText(tmpValue); } public void setQuery(Query query) { From 568df225ae2182f6a1ff3c67742f6292ad230f1b Mon Sep 17 00:00:00 2001 From: jhbengtsson Date: Wed, 5 Oct 2022 08:52:42 +0200 Subject: [PATCH 024/120] =?UTF-8?q?to=20static=20metoder=20p=C3=A5=20query?= =?UTF-8?q?.java=20til=20at=20replace=20refinement=20og=20unicode=20->=20s?= =?UTF-8?q?orry=20man=20kunne=20godt=20lave=20en=20static=20metode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/ecdar/abstractions/Query.java | 10 +++++++++- src/main/java/ecdar/controllers/QueryController.java | 3 +-- .../java/ecdar/presentations/QueryPresentation.java | 8 +------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index 04bf9d2a..c156e9b5 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -46,6 +46,14 @@ public Query(final JsonObject jsonElement) { initializeRunQuery(); } + public static String RefinementSymbolToUnicode(String stringToReplace){ + return stringToReplace.replace(">=","\u2265").replace("<=","\u2264"); + } + + public static String UnicodeToRefinementSymbol(String stringToReplace){ + return stringToReplace.replace("\u2264","<=").replace("\u2265",">="); + } + public QueryState getQueryState() { return queryState.get(); } @@ -59,7 +67,7 @@ public ObjectProperty queryStateProperty() { } public String getQuery() { - return this.query.get().replace("\u2264","<=").replace("\u2265",">="); + return UnicodeToRefinementSymbol(this.query.get()); } public void setQuery(final String query) { diff --git a/src/main/java/ecdar/controllers/QueryController.java b/src/main/java/ecdar/controllers/QueryController.java index 35342291..b1da784f 100644 --- a/src/main/java/ecdar/controllers/QueryController.java +++ b/src/main/java/ecdar/controllers/QueryController.java @@ -40,8 +40,7 @@ public void initialize(URL location, ResourceBundle resources) { queryText.textProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, String oldValue, String newValue) { - String tmpValue = newValue.replace(">=","\u2265").replace("<=","\u2264"); - queryText.setText(tmpValue); + queryText.setText(Query.RefinementSymbolToUnicode(newValue)); } }); } diff --git a/src/main/java/ecdar/presentations/QueryPresentation.java b/src/main/java/ecdar/presentations/QueryPresentation.java index e3d9a4e3..934b7484 100644 --- a/src/main/java/ecdar/presentations/QueryPresentation.java +++ b/src/main/java/ecdar/presentations/QueryPresentation.java @@ -254,19 +254,13 @@ private void initializeStateIndicator() { if (controller.getQuery().getQuery().isEmpty()) return; Label label = new Label(tooltip.getText()); - //Make sure that certain symbols in the query are displayed correctly - String newString = changeRefinementSymbols(); - JFXDialog dialog = new InformationDialogPresentation("Result from query: " + newString, label); + JFXDialog dialog = new InformationDialogPresentation("Result from query: " + Query.RefinementSymbolToUnicode(controller.getQuery().getQuery()), label); dialog.show(Ecdar.getPresentation()); }); }); } - private String changeRefinementSymbols() { - return controller.getQuery().getQuery().replace("<=","\u2264").replace("<=","\u2265"); - } - private void setStatusIndicatorContentColor(javafx.scene.paint.Color color, FontIcon statusIcon, FontIcon queryTypeExpandIcon, QueryState queryState) { statusIcon.setIconColor(color); controller.queryTypeSymbol.setFill(color); From 5e25f84b6be17355cfefa3e4553830f89c3cc807 Mon Sep 17 00:00:00 2001 From: WassawRoki <56611129+WassawRoki@users.noreply.github.com> Date: Wed, 5 Oct 2022 10:16:05 +0200 Subject: [PATCH 025/120] Update QueryPresentation.fxml --- src/main/resources/ecdar/presentations/QueryPresentation.fxml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/ecdar/presentations/QueryPresentation.fxml b/src/main/resources/ecdar/presentations/QueryPresentation.fxml index 94e1687d..3cca97b1 100644 --- a/src/main/resources/ecdar/presentations/QueryPresentation.fxml +++ b/src/main/resources/ecdar/presentations/QueryPresentation.fxml @@ -16,7 +16,7 @@ - + From 4a95a24532ddcc90a97637c300038647a2422b56 Mon Sep 17 00:00:00 2001 From: APaludan Date: Wed, 5 Oct 2022 11:00:37 +0200 Subject: [PATCH 026/120] center query icons --- src/main/resources/ecdar/presentations/QueryPresentation.fxml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/ecdar/presentations/QueryPresentation.fxml b/src/main/resources/ecdar/presentations/QueryPresentation.fxml index 3cca97b1..8aa2c391 100644 --- a/src/main/resources/ecdar/presentations/QueryPresentation.fxml +++ b/src/main/resources/ecdar/presentations/QueryPresentation.fxml @@ -12,7 +12,7 @@ HBox.hgrow="ALWAYS" alignment="CENTER" fx:controller="ecdar.controllers.QueryController"> - + From baa1b684cc0e34af770f9b8467ee820cd892d4ba Mon Sep 17 00:00:00 2001 From: Emilie Steinmann Date: Wed, 5 Oct 2022 20:52:21 +0200 Subject: [PATCH 027/120] When ENTER is pressed, we check if a query type has been selected before running query. --- src/main/java/ecdar/presentations/QueryPresentation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ecdar/presentations/QueryPresentation.java b/src/main/java/ecdar/presentations/QueryPresentation.java index 1f0d8409..48d49609 100644 --- a/src/main/java/ecdar/presentations/QueryPresentation.java +++ b/src/main/java/ecdar/presentations/QueryPresentation.java @@ -73,7 +73,7 @@ private void initializeTextFields() { queryTextField.setOnKeyPressed(EcdarController.getActiveCanvasPresentation().getController().getLeaveTextAreaKeyHandler(keyEvent -> { Platform.runLater(() -> { - if (keyEvent.getCode().equals(KeyCode.ENTER)) { + if (keyEvent.getCode().equals(KeyCode.ENTER) && controller.getQuery().getType().getQueryName() != null) { runQuery(); } }); From 8149c9346baeae42e43ecd46caf96252d481e243 Mon Sep 17 00:00:00 2001 From: "Niels F. S. Vistisen" <42961494+Nielswps@users.noreply.github.com> Date: Thu, 13 Oct 2022 10:20:12 +0200 Subject: [PATCH 028/120] Simulator (#35) Fundament fra Niels --- build.gradle | 1 - src/main/java/ecdar/Ecdar.java | 27 +- src/main/java/ecdar/abstractions/Edge.java | 1 + .../java/ecdar/abstractions/Location.java | 17 + src/main/java/ecdar/abstractions/Nail.java | 8 + .../java/ecdar/abstractions/Transition.java | 30 + .../java/ecdar/backend/BackendDriver.java | 13 +- .../controllers/DeclarationsController.java | 9 +- .../ecdar/controllers/EcdarController.java | 577 +++++++----------- .../ecdar/controllers/EditorController.java | 380 ++++++++++++ .../controllers/LeftSimPaneController.java | 25 + .../ecdar/controllers/ProcessController.java | 252 ++++++++ .../controllers/ProjectPaneController.java | 3 +- .../controllers/RightSimPaneController.java | 20 + .../ecdar/controllers/SimEdgeController.java | 421 +++++++++++++ .../controllers/SimLocationController.java | 124 ++++ .../ecdar/controllers/SimNailController.java | 126 ++++ ...ulationInitializationDialogController.java | 18 + .../controllers/SimulatorController.java | 149 +++++ .../SimulatorOverviewController.java | 385 ++++++++++++ .../TracePaneElementController.java | 187 ++++++ .../controllers/TransitionController.java | 45 ++ .../TransitionPaneElementController.java | 241 ++++++++ .../presentations/EcdarPresentation.java | 344 +++-------- .../presentations/EditorPresentation.java | 201 ++++++ .../ecdar/presentations/FilePresentation.java | 2 +- .../LeftSimPanePresentation.java | 37 ++ .../presentations/ProcessPresentation.java | 282 +++++++++ .../RightSimPanePresentation.java | 45 ++ .../presentations/SimEdgePresentation.java | 32 + .../SimLocationPresentation.java | 492 +++++++++++++++ .../presentations/SimNailPresentation.java | 258 ++++++++ .../presentations/SimTagPresentation.java | 186 ++++++ ...ationInitializationDialogPresentation.java | 17 + .../SimulatorOverviewPresentation.java | 20 + .../presentations/SimulatorPresentation.java | 20 + .../TracePaneElementPresentation.java | 96 +++ .../TransitionPaneElementPresentation.java | 69 +++ .../presentations/TransitionPresentation.java | 98 +++ .../ecdar/simulation/SimulationHandler.java | 410 +++++++++++++ .../ecdar/simulation/SimulationState.java | 52 ++ .../simulation/SimulationStateSuccessor.java | 17 + .../java/ecdar/simulation/Transition.java | 17 + .../ecdar/utility/helpers/BindingHelper.java | 10 + src/main/resources/ecdar/main.css | 20 +- .../presentations/EcdarPresentation.fxml | 132 ++-- .../presentations/EditorPresentation.fxml | 81 +++ .../LeftSimPanePresentation.fxml | 28 + .../presentations/ProcessPresentation.fxml | 56 ++ .../ProjectPanePresentation.fxml | 3 +- .../presentations/QueryPanePresentation.fxml | 3 +- .../RightSimPanePresentation.fxml | 24 + .../presentations/SimEdgePresentation.fxml | 12 + .../SimLocationPresentation.fxml | 39 ++ .../presentations/SimNailPresentation.fxml | 23 + .../presentations/SimTagPresentation.fxml | 11 + ...ationInitializationDialogPresentation.fxml | 43 ++ .../SimulatorOverviewPresentation.fxml | 13 + .../presentations/SimulatorPresentation.fxml | 19 + .../TracePaneElementPresentation.fxml | 49 ++ .../TransitionPaneElementPresentation.fxml | 51 ++ .../presentations/TransitionPresentation.fxml | 18 + src/test/java/ecdar/TestFXBase.java | 30 + .../java/ecdar/simulation/SimulationTest.java | 101 +++ 64 files changed, 5780 insertions(+), 740 deletions(-) create mode 100644 src/main/java/ecdar/abstractions/Transition.java create mode 100644 src/main/java/ecdar/controllers/EditorController.java create mode 100755 src/main/java/ecdar/controllers/LeftSimPaneController.java create mode 100755 src/main/java/ecdar/controllers/ProcessController.java create mode 100755 src/main/java/ecdar/controllers/RightSimPaneController.java create mode 100755 src/main/java/ecdar/controllers/SimEdgeController.java create mode 100755 src/main/java/ecdar/controllers/SimLocationController.java create mode 100755 src/main/java/ecdar/controllers/SimNailController.java create mode 100644 src/main/java/ecdar/controllers/SimulationInitializationDialogController.java create mode 100644 src/main/java/ecdar/controllers/SimulatorController.java create mode 100644 src/main/java/ecdar/controllers/SimulatorOverviewController.java create mode 100755 src/main/java/ecdar/controllers/TracePaneElementController.java create mode 100755 src/main/java/ecdar/controllers/TransitionController.java create mode 100755 src/main/java/ecdar/controllers/TransitionPaneElementController.java create mode 100644 src/main/java/ecdar/presentations/EditorPresentation.java create mode 100755 src/main/java/ecdar/presentations/LeftSimPanePresentation.java create mode 100755 src/main/java/ecdar/presentations/ProcessPresentation.java create mode 100755 src/main/java/ecdar/presentations/RightSimPanePresentation.java create mode 100755 src/main/java/ecdar/presentations/SimEdgePresentation.java create mode 100755 src/main/java/ecdar/presentations/SimLocationPresentation.java create mode 100755 src/main/java/ecdar/presentations/SimNailPresentation.java create mode 100755 src/main/java/ecdar/presentations/SimTagPresentation.java create mode 100644 src/main/java/ecdar/presentations/SimulationInitializationDialogPresentation.java create mode 100755 src/main/java/ecdar/presentations/SimulatorOverviewPresentation.java create mode 100755 src/main/java/ecdar/presentations/SimulatorPresentation.java create mode 100755 src/main/java/ecdar/presentations/TracePaneElementPresentation.java create mode 100755 src/main/java/ecdar/presentations/TransitionPaneElementPresentation.java create mode 100755 src/main/java/ecdar/presentations/TransitionPresentation.java create mode 100755 src/main/java/ecdar/simulation/SimulationHandler.java create mode 100644 src/main/java/ecdar/simulation/SimulationState.java create mode 100644 src/main/java/ecdar/simulation/SimulationStateSuccessor.java create mode 100644 src/main/java/ecdar/simulation/Transition.java create mode 100644 src/main/resources/ecdar/presentations/EditorPresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/LeftSimPanePresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/ProcessPresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/SimEdgePresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/SimLocationPresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/SimNailPresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/SimTagPresentation.fxml create mode 100644 src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/SimulatorOverviewPresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/SimulatorPresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/TracePaneElementPresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/TransitionPaneElementPresentation.fxml create mode 100755 src/main/resources/ecdar/presentations/TransitionPresentation.fxml create mode 100644 src/test/java/ecdar/TestFXBase.java create mode 100644 src/test/java/ecdar/simulation/SimulationTest.java diff --git a/build.gradle b/build.gradle index 1b049e61..f2c68089 100644 --- a/build.gradle +++ b/build.gradle @@ -46,7 +46,6 @@ def protocVersion = protobufVersion dependencies { implementation fileTree(dir: 'lib', include: ['*.jar']) - implementation 'com.jfoenix:jfoenix:9.0.10' implementation group: 'de.codecentric.centerdevice', name: 'javafxsvg', version: '1.3.0' implementation 'org.kordamp.ikonli:ikonli-core:12.3.1' diff --git a/src/main/java/ecdar/Ecdar.java b/src/main/java/ecdar/Ecdar.java index 05bd9efc..d2dc3dd9 100644 --- a/src/main/java/ecdar/Ecdar.java +++ b/src/main/java/ecdar/Ecdar.java @@ -4,6 +4,7 @@ import ecdar.abstractions.Project; import ecdar.backend.BackendDriver; import ecdar.backend.BackendHelper; +import ecdar.simulation.SimulationHandler; import ecdar.code_analysis.CodeAnalysis; import ecdar.controllers.EcdarController; import ecdar.presentations.BackgroundThreadPresentation; @@ -52,6 +53,7 @@ public class Ecdar extends Application { public static BooleanProperty shouldRunBackgroundQueries = new SimpleBooleanProperty(true); private static final BooleanProperty isSplit = new SimpleBooleanProperty(true); //Set to true to ensure correct behaviour at first toggle. private static BackendDriver backendDriver = new BackendDriver(); + private static SimulationHandler simulationHandler; private Stage debugStage; /** @@ -120,6 +122,16 @@ public static Project getProject() { return project; } + /** + * Returns the backend driver used to execute queries and handle simulation + * @return BackendDriver + */ + public static BackendDriver getBackendDriver() { + return backendDriver; + } + + public static SimulationHandler getSimulationHandler() { return simulationHandler; } + public static EcdarPresentation getPresentation() { return presentation; } @@ -134,8 +146,8 @@ public static void showHelp() { presentation.showHelp(); } - public static BooleanProperty toggleFilePane() { - return presentation.toggleFilePane(); + public static BooleanProperty toggleLeftPane() { + return presentation.toggleLeftPane(); } /** @@ -160,7 +172,7 @@ public static BooleanProperty toggleRunBackgroundQueries() { } public static BooleanProperty toggleQueryPane() { - return presentation.toggleQueryPane(); + return presentation.toggleRightPane(); } /** @@ -181,14 +193,6 @@ public static BooleanProperty toggleCanvasSplit() { return isSplit; } - /** - * Returns the backend driver used to execute queries and handle simulation - * @return BackendDriver - */ - public static BackendDriver getBackendDriver() { - return backendDriver; - } - public static double getDpiScale() { if (!autoScalingEnabled.getValue()) return 1; @@ -207,6 +211,7 @@ public void start(final Stage stage) { // Load or create new project project = new Project(); + simulationHandler = new SimulationHandler(); // Set the title for the application stage.setTitle("Ecdar " + VERSION); diff --git a/src/main/java/ecdar/abstractions/Edge.java b/src/main/java/ecdar/abstractions/Edge.java index 995e2ce8..2b1057c9 100644 --- a/src/main/java/ecdar/abstractions/Edge.java +++ b/src/main/java/ecdar/abstractions/Edge.java @@ -1,5 +1,6 @@ package ecdar.abstractions; +import EcdarProtoBuf.ComponentProtos; import com.google.gson.JsonPrimitive; import ecdar.Ecdar; import ecdar.controllers.EcdarController; diff --git a/src/main/java/ecdar/abstractions/Location.java b/src/main/java/ecdar/abstractions/Location.java index b490f5b9..dd3d0ebe 100644 --- a/src/main/java/ecdar/abstractions/Location.java +++ b/src/main/java/ecdar/abstractions/Location.java @@ -1,5 +1,6 @@ package ecdar.abstractions; +import EcdarProtoBuf.ComponentProtos; import ecdar.Ecdar; import ecdar.code_analysis.Nearable; import ecdar.controllers.EcdarController; @@ -87,6 +88,22 @@ public Location(final JsonObject jsonObject) { bindReachabilityAnalysis(); } + // ToDo NIELS: Comment in, when location should be received through ProtoBuf +// public Location(ComponentProtos.Location protoBufLocation) { +// setId(protoBufLocation.getId()); +// setNickname(protoBufLocation.getNickname()); +// setInvariant(protoBufLocation.getInvariant()); +// setType(Type.valueOf(protoBufLocation.getType())); +// setUrgency(Urgency.valueOf(protoBufLocation.getUrgency())); +// setX(protoBufLocation.getX()); +// setY(protoBufLocation.getY()); +// setColor(Color.valueOf(protoBufLocation.getColor())); +// setNicknameX(protoBufLocation.getNicknameX()); +// setNicknameY(protoBufLocation.getNicknameY()); +// setInvariantX(protoBufLocation.getInvariantX()); +// setInvariantY(protoBufLocation.getInvariantY()); +// } + /** * Generates an id for this, and binds reachability analysis. */ diff --git a/src/main/java/ecdar/abstractions/Nail.java b/src/main/java/ecdar/abstractions/Nail.java index 5ed84d45..f7555432 100644 --- a/src/main/java/ecdar/abstractions/Nail.java +++ b/src/main/java/ecdar/abstractions/Nail.java @@ -1,5 +1,6 @@ package ecdar.abstractions; +import EcdarProtoBuf.ComponentProtos; import ecdar.utility.helpers.Circular; import ecdar.utility.serialize.Serializable; import com.google.gson.Gson; @@ -39,6 +40,13 @@ public Nail(final JsonObject jsonObject) { deserialize(jsonObject); } + // ToDo NIELS: Comment in, when location should be received through ProtoBuf +// public Nail(ComponentProtos.Nail protoBufNail) { +// setPropertyType(DisplayableEdge.PropertyType.valueOf(protoBufNail.getPropertyType())); +// setPropertyX(protoBufNail.getPropertyX()); +// setPropertyY(protoBufNail.getPropertyY()); +// } + public double getX() { return x.get(); } diff --git a/src/main/java/ecdar/abstractions/Transition.java b/src/main/java/ecdar/abstractions/Transition.java new file mode 100644 index 00000000..9b9d25be --- /dev/null +++ b/src/main/java/ecdar/abstractions/Transition.java @@ -0,0 +1,30 @@ +package ecdar.abstractions; + +import EcdarProtoBuf.ObjectProtos; +import ecdar.Ecdar; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class Transition { + public final ArrayList edges = new ArrayList<>(); + + public Transition(ObjectProtos.Transition protoBufTransition) { + List protoBufEdges = (List) protoBufTransition.getField(ObjectProtos.Transition.getDescriptor().findFieldByName("edges")); + List componentNames = protoBufEdges.stream().map(ObjectProtos.Transition.EdgeTuple::getComponentName).collect(Collectors.toList()); + List edgeIds = protoBufEdges.stream().map(ObjectProtos.Transition.EdgeTuple::getId).collect(Collectors.toList()); + + // For each affected component, find each affected edge within that component, and add these edges to the edges list + List affectedComponents = Ecdar.getProject().getComponents().stream().filter(c -> componentNames.contains(c.getName())).collect(Collectors.toList()); + edges.addAll(affectedComponents.stream() + .map(c -> c.getEdges() + .stream() + .filter(e -> edgeIds.contains(e.getId())) + .collect(Collectors.toList())) + .flatMap(Collection::stream) + .collect(Collectors.toList()) + ); + } +} diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index a3ab0e1a..36418b39 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -2,6 +2,7 @@ import EcdarProtoBuf.ComponentProtos; import EcdarProtoBuf.EcdarBackendGrpc; +import EcdarProtoBuf.ObjectProtos; import EcdarProtoBuf.QueryProtos; import com.google.gson.JsonObject; import com.google.gson.JsonParser; @@ -10,10 +11,12 @@ import ecdar.abstractions.BackendInstance; import ecdar.abstractions.Component; import ecdar.abstractions.QueryState; -import ecdar.controllers.EcdarController; -import ecdar.utility.UndoRedoStack; +import ecdar.simulation.SimulationState; import io.grpc.*; import io.grpc.stub.StreamObserver; +import javafx.util.Pair; +import ecdar.controllers.EcdarController; +import ecdar.utility.UndoRedoStack; import javafx.application.Platform; import javafx.collections.ObservableList; import org.springframework.util.SocketUtils; @@ -384,6 +387,12 @@ private void handleQueryBackendError(Throwable t, ExecutableQuery executableQuer } } + public SimulationState getInitialSimulationState() { + SimulationState state = new SimulationState(ObjectProtos.StateTuple.newBuilder().getDefaultInstanceForType()); + state.getLocations().add(new Pair<>(Ecdar.getProject().getComponents().get(0).getName(), Ecdar.getProject().getComponents().get(0).getLocations().get(0).getId())); + return state; + } + private void addGeneratedComponent(Component newComponent) { Platform.runLater(() -> { newComponent.setTemporary(true); diff --git a/src/main/java/ecdar/controllers/DeclarationsController.java b/src/main/java/ecdar/controllers/DeclarationsController.java index 4b55672a..b4ab05e0 100644 --- a/src/main/java/ecdar/controllers/DeclarationsController.java +++ b/src/main/java/ecdar/controllers/DeclarationsController.java @@ -43,11 +43,10 @@ public void initialize(final URL location, final ResourceBundle resources) { */ private void initializeWidthAndHeight() { // Fetch width and height of canvas and update - root.minWidthProperty().bind(Ecdar.getPresentation().getController().canvasPane.minWidthProperty()); - root.maxWidthProperty().bind(Ecdar.getPresentation().getController().canvasPane.maxWidthProperty()); - root.minHeightProperty().bind(Ecdar.getPresentation().getController().canvasPane.minHeightProperty()); - root.maxHeightProperty().bind(Ecdar.getPresentation().getController().canvasPane.maxHeightProperty()); - textArea.setTranslateY(20); + root.minWidthProperty().bind(Ecdar.getPresentation().getController().getEditorPresentation().getController().canvasPane.minWidthProperty()); + root.maxWidthProperty().bind(Ecdar.getPresentation().getController().getEditorPresentation().getController().canvasPane.maxWidthProperty()); + root.minHeightProperty().bind(Ecdar.getPresentation().getController().getEditorPresentation().getController().canvasPane.minHeightProperty()); + root.maxHeightProperty().bind(Ecdar.getPresentation().getController().getEditorPresentation().getController().canvasPane.maxHeightProperty()); } /** diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index e350b296..8c432ed5 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -12,7 +12,6 @@ import ecdar.presentations.*; import ecdar.utility.UndoRedoStack; import ecdar.utility.colors.Color; -import ecdar.utility.colors.EnabledColor; import ecdar.utility.helpers.SelectHelper; import ecdar.utility.keyboard.Keybind; import ecdar.utility.keyboard.KeyboardTracker; @@ -23,7 +22,6 @@ import javafx.beans.binding.When; import javafx.beans.property.*; import javafx.collections.ListChangeListener; -import javafx.collections.ObservableList; import javafx.embed.swing.SwingFXUtils; import javafx.fxml.FXML; import javafx.fxml.Initializable; @@ -39,7 +37,6 @@ import javafx.scene.text.Text; import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; -import javafx.util.Pair; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; @@ -49,6 +46,7 @@ import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.stream.Collectors; public class EcdarController implements Initializable { // Reachability analysis @@ -56,29 +54,19 @@ public class EcdarController implements Initializable { private static long reachabilityTime = Long.MAX_VALUE; private static ExecutorService reachabilityService; - private static final ObjectProperty globalEdgeStatus = new SimpleObjectProperty<>(EdgeStatus.INPUT); - // View stuff public StackPane root; public BorderPane borderPane; - public StackPane canvasPane; public StackPane topPane; public StackPane leftPane; public StackPane rightPane; public Rectangle bottomFillerElement; - public QueryPanePresentation queryPane; - public ProjectPanePresentation filePane; - public HBox toolbar; public MessageTabPanePresentation messageTabPane; - public StackPane dialogContainer; - public JFXDialog dialog; + public StackPane modellingHelpDialogContainer; + public JFXDialog modellingHelpDialog; public StackPane modalBar; public JFXTextField queryTextField; public JFXTextField commentTextField; - public JFXRippler colorSelected; - public JFXRippler deleteSelected; - public JFXRippler undo; - public JFXRippler redo; public ImageView helpInitialImage; public StackPane helpInitialPane; @@ -89,10 +77,6 @@ public class EcdarController implements Initializable { public ImageView helpOutputImage; public StackPane helpOutputPane; - public JFXButton switchToInputButton; - public JFXButton switchToOutputButton; - public JFXToggleButton switchEdgeStatusButton; - public MenuItem menuEditMoveLeft; public MenuItem menuEditMoveUp; public MenuItem menuEditMoveRight; @@ -106,10 +90,10 @@ public class EcdarController implements Initializable { // The program top menu public MenuBar menuBar; - public MenuItem menuBarViewFilePanel; + public MenuItem menuBarViewProjectPanel; public MenuItem menuBarViewQueryPanel; public MenuItem menuBarViewGrid; - public MenuItem menuBarAutoscaling; + public MenuItem menuBarViewAutoscaling; public Menu menuViewMenuScaling; public ToggleGroup scaling; public RadioMenuItem scaleXS; @@ -120,6 +104,8 @@ public class EcdarController implements Initializable { public RadioMenuItem scaleXXL; public RadioMenuItem scaleXXXL; public MenuItem menuBarViewCanvasSplit; + public MenuItem menuBarViewEditor; + public MenuItem menuBarViewSimulator; public MenuItem menuBarFileCreateNewProject; public MenuItem menuBarFileOpenProject; public Menu menuBarFileRecentProjects; @@ -135,6 +121,7 @@ public class EcdarController implements Initializable { public MenuItem menuBarHelpAbout; public MenuItem menuBarHelpTest; + public JFXToggleButton switchGuiView; public Snackbar snackbar; public HBox statusBar; public Label statusLabel; @@ -148,6 +135,10 @@ public class EcdarController implements Initializable { public StackPane backendOptionsDialogContainer; public BackendOptionsDialogPresentation backendOptionsDialog; + + public StackPane simulationInitializationDialogContainer; + public SimulationInitializationDialogPresentation simulationInitializationDialog; + public final DoubleProperty scalingProperty = new SimpleDoubleProperty(); private static JFXDialog _queryDialog; @@ -161,10 +152,89 @@ public static void runReachabilityAnalysis() { reachabilityTime = System.currentTimeMillis() + 500; } - private static final ObjectProperty activeCanvasPresentation = new SimpleObjectProperty<>(new CanvasPresentation()); + /** + * Enumeration to keep track of which mode the application is in + */ + private enum Mode { + Editor, Simulator + } + + /** + * currentMode is a property that keeps track of which mode the application is in. + * The initial mode is Mode.Editor + */ + private static final ObjectProperty currentMode = new SimpleObjectProperty<>(Mode.Editor); + + private static final EditorPresentation editorPresentation = new EditorPresentation(); + public final ProjectPanePresentation projectPane = new ProjectPanePresentation(); + public final QueryPanePresentation queryPane = new QueryPanePresentation(); + + private static final SimulatorPresentation simulatorPresentation = new SimulatorPresentation(); + public final LeftSimPanePresentation leftSimPane = new LeftSimPanePresentation(); + public final RightSimPanePresentation rightSimPane = new RightSimPanePresentation(); + + @Override + public void initialize(final URL location, final ResourceBundle resources) { + initializeDialogs(); + initializeKeybindings(); + initializeStatusBar(); + initializeMenuBar(); + startBackgroundQueriesThread(); // Will terminate immediately if background queries are turned off + + bottomFillerElement.heightProperty().bind(messageTabPane.maxHeightProperty()); + messageTabPane.getController().setRunnableForOpeningAndClosingMessageTabPane(this::changeInsetsOfProjectAndQueryPanes); + + // Update file coloring when active model changes + editorPresentation.getController().getActiveCanvasPresentation().getController().activeComponentProperty().addListener(observable -> projectPane.getController().updateColorsOnFilePresentations()); + editorPresentation.getController().activeCanvasPresentationProperty().addListener(observable -> projectPane.getController().updateColorsOnFilePresentations()); + + leftSimPane.minWidthProperty().bind(projectPane.minWidthProperty()); + leftSimPane.maxWidthProperty().bind(projectPane.maxWidthProperty()); + rightSimPane.minWidthProperty().bind(queryPane.minWidthProperty()); + rightSimPane.maxWidthProperty().bind(queryPane.maxWidthProperty()); + + enterEditorMode(); + } + + public StackPane getCenter() { + if (currentMode.get().equals(Mode.Editor)) { + return editorPresentation.getController().canvasPane; + } else { + return simulatorPresentation; + } + } public static EdgeStatus getGlobalEdgeStatus() { - return globalEdgeStatus.get(); + return editorPresentation.getController().getGlobalEdgeStatus(); + } + + public EditorPresentation getEditorPresentation() { + return editorPresentation; + } + + public SimulatorPresentation getSimulatorPresentation() { + return simulatorPresentation; + } + + public static CanvasPresentation getActiveCanvasPresentation() { + return editorPresentation.getController().getActiveCanvasPresentation(); + } + + public static DoubleProperty getActiveCanvasZoomFactor() { + return getActiveCanvasPresentation().getController().zoomHelper.currentZoomFactor; + } + + public static void setActiveCanvasPresentation(CanvasPresentation newActiveCanvasPresentation) { + getActiveCanvasPresentation().setOpacity(0.75); + newActiveCanvasPresentation.setOpacity(1); + editorPresentation.getController().setActiveCanvasPresentation(newActiveCanvasPresentation); + } + + public static void setActiveModelForActiveCanvas(HighLevelModelObject newActiveModel) { + getActiveCanvasPresentation().getController().setActiveModel(newActiveModel); + + // Change zoom level to fit new active model + Platform.runLater(() -> getActiveCanvasPresentation().getController().zoomHelper.zoomToFit()); } public static void setTemporaryComponentWatermarkVisibility(boolean visibility) { @@ -195,57 +265,53 @@ private void scaleIcons(Node node, double size) { Set xSmallIcons = node.lookupAll(".icon-size-x-small"); for (Node icon : xSmallIcons) icon.setStyle("-fx-icon-size: " + Math.floor(size / 13.0 * 18) + "px;"); + + switchGuiView.setSize(Math.floor(size / 13.0 * 24)); } private double getNewCalculatedScale() { return (Double.parseDouble(scaling.getSelectedToggle().getProperties().get("scale").toString()) * Ecdar.getDpiScale()) * 13.0; } - private void scaleEdgeStatusToggle(double size) { - switchEdgeStatusButton.setScaleX(size / 13.0); - switchEdgeStatusButton.setScaleY(size / 13.0); - } - - @Override - public void initialize(final URL location, final ResourceBundle resources) { - initilizeDialogs(); - initializeCanvasPane(); - initializeEdgeStatusHandling(); - initializeKeybindings(); - initializeStatusBar(); - initializeMenuBar(); - intitializeTemporaryComponentWatermark(); - startBackgroundQueriesThread(); // Will terminate immediately if background queries are turned off - - bottomFillerElement.heightProperty().bind(messageTabPane.maxHeightProperty()); - messageTabPane.getController().setRunnableForOpeningAndClosingMessageTabPane(this::changeInsetsOfFileAndQueryPanes); - } - - private void initilizeDialogs() { - dialog.setDialogContainer(dialogContainer); - dialogContainer.opacityProperty().bind(dialog.getChildren().get(0).scaleXProperty()); - dialog.setOnDialogClosed(event -> dialogContainer.setVisible(false)); + private void initializeDialogs() { + modellingHelpDialog.setDialogContainer(modellingHelpDialogContainer); + modellingHelpDialogContainer.opacityProperty().bind(modellingHelpDialog.getChildren().get(0).scaleXProperty()); + modellingHelpDialog.setOnDialogClosed(event -> modellingHelpDialogContainer.setVisible(false)); _queryDialog = queryDialog; _queryTextResult = queryTextResult; _queryTextQuery = queryTextQuery; initializeDialog(queryDialog, queryDialogContainer); - initializeDialog(backendOptionsDialog, backendOptionsDialogContainer); + initializeBackendOptionsDialog(); + + initializeDialog(simulationInitializationDialog, simulationInitializationDialogContainer); + simulationInitializationDialog.getController().cancelButton.setOnMouseClicked(event -> { + switchGuiView.setSelected(false); + simulationInitializationDialog.close(); + }); + + simulationInitializationDialog.getController().startButton.setOnMouseClicked(event -> { + // ToDo NIELS: Start simulation of selected query + currentMode.setValue(Mode.Simulator); + simulationInitializationDialog.close(); + }); + } + + private void initializeBackendOptionsDialog() { + initializeDialog(backendOptionsDialog, backendOptionsDialogContainer); backendOptionsDialog.getController().resetBackendsButton.setOnMouseClicked(event -> { backendOptionsDialog.getController().resetBackendsToDefault(); }); backendOptionsDialog.getController().closeButton.setOnMouseClicked(event -> { backendOptionsDialog.getController().cancelBackendOptionsChanges(); - dialog.close(); backendOptionsDialog.close(); }); backendOptionsDialog.getController().saveButton.setOnMouseClicked(event -> { if (backendOptionsDialog.getController().saveChangesToBackendOptions()) { - dialog.close(); backendOptionsDialog.close(); } }); @@ -271,10 +337,9 @@ private void initializeDialog(JFXDialog dialog, StackPane dialogContainer) { dialogContainer.setMouseTransparent(false); }); - filePane.getStyleClass().add("responsive-pane-sizing"); + projectPane.getStyleClass().add("responsive-pane-sizing"); queryPane.getStyleClass().add("responsive-pane-sizing"); - initializeEdgeStatusHandling(); initializeKeybindings(); initializeStatusBar(); } @@ -319,17 +384,14 @@ private void initializeKeybindings() { event.consume(); nudgeSelected(NudgeDirection.UP); })); - KeyboardTracker.registerKeybind(KeyboardTracker.NUDGE_DOWN, new Keybind(new KeyCodeCombination(KeyCode.DOWN), (event) -> { event.consume(); nudgeSelected(NudgeDirection.DOWN); })); - KeyboardTracker.registerKeybind(KeyboardTracker.NUDGE_LEFT, new Keybind(new KeyCodeCombination(KeyCode.LEFT), (event) -> { event.consume(); nudgeSelected(NudgeDirection.LEFT); })); - KeyboardTracker.registerKeybind(KeyboardTracker.NUDGE_RIGHT, new Keybind(new KeyCodeCombination(KeyCode.RIGHT), (event) -> { event.consume(); nudgeSelected(NudgeDirection.RIGHT); @@ -343,67 +405,6 @@ private void initializeKeybindings() { KeyboardTracker.registerKeybind(KeyboardTracker.NUDGE_A, new Keybind(new KeyCodeCombination(KeyCode.A), () -> nudgeSelected(NudgeDirection.LEFT))); KeyboardTracker.registerKeybind(KeyboardTracker.NUDGE_S, new Keybind(new KeyCodeCombination(KeyCode.S), () -> nudgeSelected(NudgeDirection.DOWN))); KeyboardTracker.registerKeybind(KeyboardTracker.NUDGE_D, new Keybind(new KeyCodeCombination(KeyCode.D), () -> nudgeSelected(NudgeDirection.RIGHT))); - - // Keybind for deleting the selected elements - KeyboardTracker.registerKeybind(KeyboardTracker.DELETE_SELECTED, new Keybind(new KeyCodeCombination(KeyCode.DELETE), this::deleteSelectedClicked)); - - // Keybinds for coloring the selected elements - EnabledColor.enabledColors.forEach(enabledColor -> { - KeyboardTracker.registerKeybind(KeyboardTracker.COLOR_SELECTED + "_" + enabledColor.keyCode.getName(), new Keybind(new KeyCodeCombination(enabledColor.keyCode), () -> { - - final List> previousColor = new ArrayList<>(); - - SelectHelper.getSelectedElements().forEach(selectable -> { - previousColor.add(new Pair<>(selectable, new EnabledColor(selectable.getColor(), selectable.getColorIntensity()))); - }); - changeColorOnSelectedElements(enabledColor, previousColor); - SelectHelper.clearSelectedElements(); - })); - }); - } - - /** - * Handles the change of color on selected objects - * - * @param enabledColor The new color for the selected objects - * @param previousColor The color old color of the selected objects - */ - public void changeColorOnSelectedElements(final EnabledColor enabledColor, - final List> previousColor) { - UndoRedoStack.pushAndPerform(() -> { // Perform - SelectHelper.getSelectedElements() - .forEach(selectable -> selectable.color(enabledColor.color, enabledColor.intensity)); - }, () -> { // Undo - previousColor.forEach(selectableEnabledColorPair -> selectableEnabledColorPair.getKey().color(selectableEnabledColorPair.getValue().color, selectableEnabledColorPair.getValue().intensity)); - }, String.format("Changed the color of %d elements to %s", previousColor.size(), enabledColor.color.name()), "color-lens"); - } - - /** - * Initializes edge status. - * Input is the default status. - * This method sets buttons for edge status whenever the status changes. - */ - private void initializeEdgeStatusHandling() { - globalEdgeStatus.set(EdgeStatus.INPUT); - - Tooltip.install(switchToInputButton, new Tooltip("Switch to input mode")); - Tooltip.install(switchToOutputButton, new Tooltip("Switch to output mode")); - switchToInputButton.setDisableVisualFocus(true); // Hiding input button rippler on start-up - - globalEdgeStatus.addListener(((observable, oldValue, newValue) -> { - if (newValue.equals(EdgeStatus.INPUT)) { - switchToInputButton.setTextFill(javafx.scene.paint.Color.WHITE); - switchToOutputButton.setTextFill(javafx.scene.paint.Color.GREY); - switchEdgeStatusButton.setSelected(false); - } else { - switchToInputButton.setTextFill(javafx.scene.paint.Color.GREY); - switchToOutputButton.setTextFill(javafx.scene.paint.Color.WHITE); - switchEdgeStatusButton.setSelected(true); - } - })); - - // Ensure that the rippler is centered when scale is changed - Platform.runLater(() -> ((JFXRippler) switchEdgeStatusButton.lookup(".jfx-rippler")).setRipplerRecenter(true)); } private void startBackgroundQueriesThread() { @@ -554,27 +555,6 @@ private void initializeMenuBar() { initializeHelpMenu(); } - public static CanvasPresentation getActiveCanvasPresentation() { - return activeCanvasPresentation.get(); - } - - public static DoubleProperty getActiveCanvasZoomFactor() { - return getActiveCanvasPresentation().getController().zoomHelper.currentZoomFactor; - } - - public static void setActiveCanvasPresentation(CanvasPresentation newActiveCanvasPresentation) { - activeCanvasPresentation.get().setOpacity(0.75); - newActiveCanvasPresentation.setOpacity(1); - activeCanvasPresentation.set(newActiveCanvasPresentation); - } - - public static void setActiveModelForActiveCanvas(HighLevelModelObject newActiveModel) { - EcdarController.getActiveCanvasPresentation().getController().setActiveModel(newActiveModel); - - // Change zoom level to fit new active model - Platform.runLater(() -> EcdarController.getActiveCanvasPresentation().getController().zoomHelper.zoomToFit()); - } - private void initializeHelpMenu() { menuBarHelpHelp.setOnAction(event -> Ecdar.showHelp()); @@ -591,7 +571,6 @@ private void initializeHelpMenu() { }); aboutAcceptButton.setOnAction(event -> aboutDialog.close()); aboutDialog.setOnDialogClosed(event -> aboutContainer.setVisible(false)); // hide container when dialog is fully closed - } /** @@ -660,11 +639,11 @@ private void initializeEditMenu() { * Initialize the View menu. */ private void initializeViewMenu() { - menuBarViewFilePanel.getGraphic().setOpacity(1); - menuBarViewFilePanel.setAccelerator(new KeyCodeCombination(KeyCode.P, KeyCodeCombination.SHORTCUT_DOWN)); - menuBarViewFilePanel.setOnAction(event -> { - final BooleanProperty isOpen = Ecdar.toggleFilePane(); - menuBarViewFilePanel.getGraphic().opacityProperty().bind(new When(isOpen).then(1).otherwise(0)); + menuBarViewProjectPanel.getGraphic().setOpacity(1); + menuBarViewProjectPanel.setAccelerator(new KeyCodeCombination(KeyCode.P, KeyCodeCombination.SHORTCUT_DOWN)); + menuBarViewProjectPanel.setOnAction(event -> { + final BooleanProperty isOpen = Ecdar.toggleLeftPane(); + menuBarViewProjectPanel.getGraphic().opacityProperty().bind(new When(isOpen).then(1).otherwise(0)); }); menuBarViewQueryPanel.getGraphic().setOpacity(0); @@ -681,14 +660,14 @@ private void initializeViewMenu() { menuBarViewGrid.getGraphic().opacityProperty().bind(new When(isOn).then(1).otherwise(0)); }); - menuBarAutoscaling.getGraphic().setOpacity(Ecdar.autoScalingEnabled.getValue() ? 1 : 0); - menuBarAutoscaling.setOnAction(event -> { + menuBarViewAutoscaling.getGraphic().setOpacity(Ecdar.autoScalingEnabled.getValue() ? 1 : 0); + menuBarViewAutoscaling.setOnAction(event -> { Ecdar.autoScalingEnabled.setValue(!Ecdar.autoScalingEnabled.getValue()); updateScaling(getNewCalculatedScale() / 13); Ecdar.preferences.put("autoscaling", String.valueOf(Ecdar.autoScalingEnabled.getValue())); }); Ecdar.autoScalingEnabled.addListener((observable, oldValue, newValue) -> { - menuBarAutoscaling.getGraphic().opacityProperty().setValue(newValue ? 1 : 0); + menuBarViewAutoscaling.getGraphic().opacityProperty().setValue(newValue ? 1 : 0); }); scaling.selectedToggleProperty().addListener((observable, oldValue, newValue) -> updateScaling(Double.parseDouble(newValue.getProperties().get("scale").toString()))); @@ -697,14 +676,55 @@ private void initializeViewMenu() { menuBarViewCanvasSplit.setOnAction(event -> { final BooleanProperty isSplit = Ecdar.toggleCanvasSplit(); if (isSplit.get()) { - Platform.runLater(this::setCanvasModeToSingular); + Platform.runLater(() -> editorPresentation.getController().setCanvasModeToSingular()); menuBarViewCanvasSplit.setText("Split canvas"); } else { - Platform.runLater(this::setCanvasModeToSplit); + Platform.runLater(() -> editorPresentation.getController().setCanvasModeToSplit()); menuBarViewCanvasSplit.setText("Merge canvases"); } }); + switchGuiView.selectedProperty().addListener((observable, oldValue, newValue) -> { + if (newValue) { + if (Ecdar.getProject().getQueries().isEmpty()) { + Ecdar.showToast("Please add a query to simulate before entering the simulator"); + switchGuiView.setSelected(false); + return; + } + + if (!Ecdar.getSimulationHandler().isSimulationRunning()) { + ArrayList queryOptions = Ecdar.getProject().getQueries().stream().map(Query::getQuery).collect(Collectors.toCollection(ArrayList::new)); + + if (!simulationInitializationDialog.getController().simulationComboBox.getItems().equals(queryOptions)) { + simulationInitializationDialog.getController().simulationComboBox.getItems().setAll(queryOptions); + } + + simulationInitializationDialogContainer.setVisible(true); + simulationInitializationDialog.show(simulationInitializationDialogContainer); + simulationInitializationDialog.setMouseTransparent(false); + } else { + currentMode.setValue(Mode.Simulator); + } + } else { + currentMode.setValue(Mode.Editor); + } + }); + + currentMode.addListener((obs, oldMode, newMode) -> { + if (newMode == Mode.Editor && oldMode != newMode) { + enterEditorMode(); + } else if (newMode == Mode.Simulator && oldMode != newMode) { + enterSimulatorMode(); + } + }); + + menuBarViewEditor.setAccelerator(new KeyCodeCombination(KeyCode.DIGIT1, KeyCombination.SHORTCUT_DOWN)); + menuBarViewEditor.setOnAction(event -> switchGuiView.setSelected(false)); + + menuBarViewSimulator.setAccelerator(new KeyCodeCombination(KeyCode.DIGIT2, KeyCombination.SHORTCUT_DOWN)); + menuBarViewSimulator.setOnAction(event -> switchGuiView.setSelected(true)); + + // On startup, set the scaling to the values saved in preferences Platform.runLater(() -> { Ecdar.autoScalingEnabled.setValue(Ecdar.preferences.getBoolean("autoscaling", true)); @@ -733,7 +753,7 @@ private void updateScaling(double newScale) { Ecdar.preferences.put("scale", String.valueOf(newScale)); scaleIcons(root, newCalculatedScale); - scaleEdgeStatusToggle(newCalculatedScale); + editorPresentation.getController().scaleEdgeStatusToggle(newCalculatedScale); messageTabPane.getController().updateScale(newScale); // Update listeners of UI scale @@ -999,152 +1019,42 @@ private void initializeFileExportAsPng() { }); } - private void initializeCanvasPane() { - Platform.runLater(this::setCanvasModeToSingular); - } - - /** - * Removes the canvases and adds a new one, with the active component of the active canvasPresentation. - */ - private void setCanvasModeToSingular() { - canvasPane.getChildren().clear(); - CanvasPresentation canvasPresentation = new CanvasPresentation(); - HighLevelModelObject model = activeCanvasPresentation.get().getController().getActiveModel(); - if (model != null) { - canvasPresentation.getController().setActiveModel(activeCanvasPresentation.get().getController().getActiveModel()); - } else { - // If no components where found, the project has not been initialized. The active model will be updated when the project is initialized - canvasPresentation.getController().setActiveModel(Ecdar.getProject().getComponents().stream().findFirst().orElse(null)); - } - - canvasPane.getChildren().add(canvasPresentation); - activeCanvasPresentation.set(canvasPresentation); - filePane.getController().updateColorsOnFilePresentations(); - - Rectangle clip = new Rectangle(); - clip.setArcWidth(1); - clip.setArcHeight(1); - clip.widthProperty().bind(canvasPane.widthProperty()); - clip.heightProperty().bind(canvasPane.heightProperty()); - canvasPresentation.getController().zoomablePane.setClip(clip); - - canvasPresentation.getController().zoomablePane.minWidthProperty().bind(canvasPane.widthProperty()); - canvasPresentation.getController().zoomablePane.maxWidthProperty().bind(canvasPane.widthProperty()); - canvasPresentation.getController().zoomablePane.minHeightProperty().bind(canvasPane.heightProperty()); - canvasPresentation.getController().zoomablePane.maxHeightProperty().bind(canvasPane.heightProperty()); - } - - /** - * Removes the canvas and adds a GridPane with four new canvases, with different active components, - * the first being the one previously displayed on the single canvas. - */ - private void setCanvasModeToSplit() { - canvasPane.getChildren().clear(); - - GridPane canvasGrid = new GridPane(); - - canvasGrid.addColumn(0); - canvasGrid.addColumn(1); - canvasGrid.addRow(0); - canvasGrid.addRow(1); - - ColumnConstraints col1 = new ColumnConstraints(); - col1.setPercentWidth(50); - - RowConstraints row1 = new RowConstraints(); - row1.setPercentHeight(50); - - canvasGrid.getColumnConstraints().add(col1); - canvasGrid.getColumnConstraints().add(col1); - canvasGrid.getRowConstraints().add(row1); - canvasGrid.getRowConstraints().add(row1); - - ObservableList components = Ecdar.getProject().getComponents(); - int currentCompNum = 0, numComponents = components.size(); - - // Add the canvasPresentation at the top-left - CanvasPresentation canvasPresentation = initializeNewCanvasPresentation(); - canvasPresentation.getController().setActiveModel(getActiveCanvasPresentation().getController().getActiveModel()); - canvasGrid.add(canvasPresentation, 0, 0); - setActiveCanvasPresentation(canvasPresentation); - - // Add the canvasPresentation at the top-right - canvasPresentation = initializeNewCanvasPresentationWithActiveComponent(components, currentCompNum); - canvasPresentation.setOpacity(0.75); - canvasGrid.add(canvasPresentation, 1, 0); - // Update the startIndex for the next canvasPresentation - for (int i = 0; i < numComponents; i++) { - if (canvasPresentation.getController().getActiveModel() != null && canvasPresentation.getController().getActiveModel().equals(components.get(i))) { - currentCompNum = i + 1; - } - } - - // Add the canvasPresentation at the bottom-left - canvasPresentation = initializeNewCanvasPresentationWithActiveComponent(components, currentCompNum); - canvasPresentation.setOpacity(0.75); - canvasGrid.add(canvasPresentation, 0, 1); - - // Update the startIndex for the next canvasPresentation - for (int i = 0; i < numComponents; i++) - if (canvasPresentation.getController().getActiveModel() != null && canvasPresentation.getController().getActiveModel().equals(components.get(i))) { - currentCompNum = i + 1; - } - - // Add the canvasPresentation at the bottom-right - canvasPresentation = initializeNewCanvasPresentationWithActiveComponent(components, currentCompNum); - canvasPresentation.setOpacity(0.75); - canvasGrid.add(canvasPresentation, 1, 1); - - canvasPane.getChildren().add(canvasGrid); - filePane.getController().updateColorsOnFilePresentations(); - } - /** - * Initialize a new CanvasShellPresentation and set its active component to the next component encountered from the startIndex and return it - * - * @param components the list of components for assigning active component of the CanvasPresentation - * @param startIndex the index to start at when trying to find the component to set as active - * @return new CanvasShellPresentation + * Changes the view and mode to the editor + * Only enter if the mode is not already Editor */ - private CanvasPresentation initializeNewCanvasPresentationWithActiveComponent(ObservableList components, int startIndex) { - CanvasPresentation canvasPresentation = initializeNewCanvasPresentation(); - - int numComponents = components.size(); - canvasPresentation.getController().setActiveModel(null); - for (int currentCompNum = startIndex; currentCompNum < numComponents; currentCompNum++) { - if (getActiveCanvasPresentation().getController().getActiveModel() != components.get(currentCompNum)) { - canvasPresentation.getController().setActiveModel(components.get(currentCompNum)); - break; - } - } - - return canvasPresentation; + private void enterEditorMode() { +// ToDo NIELS: Consider implementing willShow and willHide to handle general elements that should only be available for one of the modes +// editorPresentation.getController().willShow(); +// simulatorPresentation.getController().willHide(); + + borderPane.setCenter(editorPresentation); + leftPane.getChildren().clear(); + leftPane.getChildren().add(projectPane); + rightPane.getChildren().clear(); + rightPane.getChildren().add(queryPane); + + // Enable or disable the menu items that can be used when in the simulator +// updateMenuItems(); } /** - * Initialize a new CanvasPresentation and return it - * - * @return new CanvasPresentation + * Changes the view and mode to the simulator + * Only enter if the mode is not already Simulator */ - private CanvasPresentation initializeNewCanvasPresentation() { - CanvasPresentation canvasPresentation = new CanvasPresentation(); - canvasPresentation.setBorder(new Border(new BorderStroke(Color.GREY.getColor(Color.Intensity.I500), BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderStroke.THIN))); - - // Set th clip of the zoomable pane to be half of the canvasPane, - // to ensure a 2 by 2 grid without overflowing borders - Rectangle clip = new Rectangle(); - clip.setArcWidth(1); - clip.setArcHeight(1); - clip.widthProperty().bind(canvasPane.widthProperty().divide(2)); - clip.heightProperty().bind(canvasPane.heightProperty().divide(2)); - canvasPresentation.getController().zoomablePane.setClip(clip); - - canvasPresentation.getController().zoomablePane.minWidthProperty().bind(canvasPane.widthProperty().divide(2)); - canvasPresentation.getController().zoomablePane.maxWidthProperty().bind(canvasPane.widthProperty().divide(2)); - canvasPresentation.getController().zoomablePane.minHeightProperty().bind(canvasPane.heightProperty().divide(2)); - canvasPresentation.getController().zoomablePane.maxHeightProperty().bind(canvasPane.heightProperty().divide(2)); - - return canvasPresentation; + private void enterSimulatorMode() { +// ToDo NIELS: Consider implementing willShow and willHide to handle general elements that should only be available for one of the modes +// ecdarPresentation.getController().willHide(); + simulatorPresentation.getController().willShow(); + + borderPane.setCenter(simulatorPresentation); + leftPane.getChildren().clear(); + leftPane.getChildren().add(leftSimPane); + rightPane.getChildren().clear(); + rightPane.getChildren().add(rightSimPane); + + // Enable or disable the menu items that can be used when in the simulator +// updateMenuItems(); } /** @@ -1309,15 +1219,15 @@ private static int getAutoCropRightX(final BufferedImage image) { } /** - * This method is used to push the contents of the file and query panes when the tab pane is opened + * This method is used to push the contents of the project and query panes when the tab pane is opened */ - private void changeInsetsOfFileAndQueryPanes() { + private void changeInsetsOfProjectAndQueryPanes() { if (messageTabPane.getController().isOpen()) { - filePane.showBottomInset(false); + projectPane.showBottomInset(false); queryPane.showBottomInset(false); CanvasPresentation.showBottomInset(false); } else { - filePane.showBottomInset(true); + projectPane.showBottomInset(true); queryPane.showBottomInset(true); CanvasPresentation.showBottomInset(true); } @@ -1355,88 +1265,9 @@ private void nudgeSelected(final NudgeDirection direction) { "open-with"); } - @FXML - private void deleteSelectedClicked() { - if (SelectHelper.getSelectedElements().size() == 0) return; - - // Run through the selected elements and look for something that we can delete - SelectHelper.getSelectedElements().forEach(selectable -> { - if (selectable instanceof LocationController) { - ((LocationController) selectable).tryDelete(); - } else if (selectable instanceof EdgeController) { - final Component component = ((EdgeController) selectable).getComponent(); - final DisplayableEdge edge = ((EdgeController) selectable).getEdge(); - - // Dont delete edge if it is locked - if (edge.getIsLockedProperty().getValue()) { - return; - } - - UndoRedoStack.pushAndPerform(() -> { // Perform - // Remove the edge - component.removeEdge(edge); - }, () -> { // Undo - // Re-all the edge - component.addEdge(edge); - }, String.format("Deleted %s", selectable.toString()), "delete"); - } else if (selectable instanceof NailController) { - ((NailController) selectable).tryDelete(); - } - }); - - SelectHelper.clearSelectedElements(); - } - - @FXML - private void undoClicked() { - UndoRedoStack.undo(); - } - - @FXML - private void redoClicked() { - UndoRedoStack.redo(); - } - - /** - * Switch to input edge mode - */ - @FXML - private void switchToInputClicked() { - setGlobalEdgeStatus(EdgeStatus.INPUT); - } - - /** - * Switch to output edge mode - */ - @FXML - private void switchToOutputClicked() { - setGlobalEdgeStatus(EdgeStatus.OUTPUT); - } - - /** - * Switch edge status. - */ - @FXML - private void switchEdgeStatusClicked() { - if (getGlobalEdgeStatus().equals(EdgeStatus.INPUT)) { - setGlobalEdgeStatus(EdgeStatus.OUTPUT); - } else { - setGlobalEdgeStatus(EdgeStatus.INPUT); - } - } - - /** - * Sets the global edge status. - * - * @param status the status - */ - private void setGlobalEdgeStatus(EdgeStatus status) { - globalEdgeStatus.set(status); - } - @FXML private void closeQueryDialog() { - dialog.close(); + modellingHelpDialog.close(); queryDialog.close(); } diff --git a/src/main/java/ecdar/controllers/EditorController.java b/src/main/java/ecdar/controllers/EditorController.java new file mode 100644 index 00000000..605cc6fd --- /dev/null +++ b/src/main/java/ecdar/controllers/EditorController.java @@ -0,0 +1,380 @@ +package ecdar.controllers; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXRippler; +import com.jfoenix.controls.JFXToggleButton; +import ecdar.Ecdar; +import ecdar.abstractions.Component; +import ecdar.abstractions.DisplayableEdge; +import ecdar.abstractions.EdgeStatus; +import ecdar.abstractions.HighLevelModelObject; +import ecdar.presentations.CanvasPresentation; +import ecdar.utility.UndoRedoStack; +import ecdar.utility.colors.Color; +import ecdar.utility.colors.EnabledColor; +import ecdar.utility.helpers.SelectHelper; +import ecdar.utility.keyboard.Keybind; +import ecdar.utility.keyboard.KeyboardTracker; +import javafx.application.Platform; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Tooltip; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.layout.*; +import javafx.scene.shape.Rectangle; +import javafx.util.Pair; + +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.ResourceBundle; + +public class EditorController implements Initializable { + public VBox root; + public HBox toolbar; + public JFXRippler colorSelected; + public JFXRippler deleteSelected; + public JFXRippler undo; + public JFXRippler redo; + public JFXButton switchToInputButton; + public JFXButton switchToOutputButton; + public JFXToggleButton switchEdgeStatusButton; + public StackPane canvasPane; + + private final ObjectProperty globalEdgeStatus = new SimpleObjectProperty<>(EdgeStatus.INPUT); + private final ObjectProperty activeCanvasPresentation = new SimpleObjectProperty<>(new CanvasPresentation()); + + @Override + public void initialize(final URL location, final ResourceBundle resources) { + initializeCanvasPane(); + initializeEdgeStatusHandling(); + initializeKeybindings(); + } + + public EdgeStatus getGlobalEdgeStatus() { + return globalEdgeStatus.get(); + } + + ObjectProperty activeCanvasPresentationProperty() { + return activeCanvasPresentation; + } + + public CanvasPresentation getActiveCanvasPresentation() { + return activeCanvasPresentation.get(); + } + + public void setActiveCanvasPresentation(CanvasPresentation newActiveCanvasPresentation) { + activeCanvasPresentation.get().setOpacity(0.75); + newActiveCanvasPresentation.setOpacity(1); + activeCanvasPresentation.set(newActiveCanvasPresentation); + } + + public void setActiveModelForActiveCanvas(HighLevelModelObject newActiveModel) { + EcdarController.getActiveCanvasPresentation().getController().setActiveModel(newActiveModel); + + // Change zoom level to fit new active model + Platform.runLater(() -> EcdarController.getActiveCanvasPresentation().getController().zoomHelper.zoomToFit()); + } + + private void initializeCanvasPane() { + Platform.runLater(this::setCanvasModeToSingular); + } + + private void initializeKeybindings() { + // Keybinds for coloring the selected elements + EnabledColor.enabledColors.forEach(enabledColor -> { + KeyboardTracker.registerKeybind(KeyboardTracker.COLOR_SELECTED + "_" + enabledColor.keyCode.getName(), new Keybind(new KeyCodeCombination(enabledColor.keyCode), () -> { + + final List> previousColor = new ArrayList<>(); + + SelectHelper.getSelectedElements().forEach(selectable -> { + previousColor.add(new Pair<>(selectable, new EnabledColor(selectable.getColor(), selectable.getColorIntensity()))); + }); + changeColorOnSelectedElements(enabledColor, previousColor); + SelectHelper.clearSelectedElements(); + })); + }); + + // Keybind for deleting the selected elements + KeyboardTracker.registerKeybind(KeyboardTracker.DELETE_SELECTED, new Keybind(new KeyCodeCombination(KeyCode.DELETE), this::deleteSelectedClicked)); + } + + /** + * Initializes edge status. + * Input is the default status. + * This method sets buttons for edge status whenever the status changes. + */ + private void initializeEdgeStatusHandling() { + globalEdgeStatus.set(EdgeStatus.INPUT); + + Tooltip.install(switchToInputButton, new Tooltip("Switch to input mode")); + Tooltip.install(switchToOutputButton, new Tooltip("Switch to output mode")); + switchToInputButton.setDisableVisualFocus(true); // Hiding input button rippler on start-up + + globalEdgeStatus.addListener(((observable, oldValue, newValue) -> { + if (newValue.equals(EdgeStatus.INPUT)) { + switchToInputButton.setTextFill(javafx.scene.paint.Color.WHITE); + switchToOutputButton.setTextFill(javafx.scene.paint.Color.GREY); + switchEdgeStatusButton.setSelected(false); + } else { + switchToInputButton.setTextFill(javafx.scene.paint.Color.GREY); + switchToOutputButton.setTextFill(javafx.scene.paint.Color.WHITE); + switchEdgeStatusButton.setSelected(true); + } + })); + + // Ensure that the rippler is centered when scale is changed + Platform.runLater(() -> { + var rippler = ((JFXRippler) switchEdgeStatusButton.lookup(".jfx-rippler")); + if (rippler == null) return; + rippler.setRipplerRecenter(true); + }); + } + + /** + * Sets the global edge status. + * + * @param status the status + */ + private void setGlobalEdgeStatus(EdgeStatus status) { + globalEdgeStatus.set(status); + } + + void scaleEdgeStatusToggle(double size) { + switchEdgeStatusButton.setScaleX(size / 13.0); + switchEdgeStatusButton.setScaleY(size / 13.0); + } + + /** + * Handles the change of color on selected objects + * + * @param enabledColor The new color for the selected objects + * @param previousColor The color old color of the selected objects + */ + public void changeColorOnSelectedElements(final EnabledColor enabledColor, + final List> previousColor) { + UndoRedoStack.pushAndPerform(() -> { // Perform + SelectHelper.getSelectedElements() + .forEach(selectable -> selectable.color(enabledColor.color, enabledColor.intensity)); + }, () -> { // Undo + previousColor.forEach(selectableEnabledColorPair -> selectableEnabledColorPair.getKey().color(selectableEnabledColorPair.getValue().color, selectableEnabledColorPair.getValue().intensity)); + }, String.format("Changed the color of %d elements to %s", previousColor.size(), enabledColor.color.name()), "color-lens"); + } + + /** + * Removes the canvases and adds a new one, with the active component of the active canvasPresentation. + */ + void setCanvasModeToSingular() { + canvasPane.getChildren().clear(); + CanvasPresentation canvasPresentation = new CanvasPresentation(); + HighLevelModelObject model = activeCanvasPresentation.get().getController().getActiveModel(); + if (model != null) { + canvasPresentation.getController().setActiveModel(activeCanvasPresentation.get().getController().getActiveModel()); + } else { + // If no components where found, the project has not been initialized. The active model will be updated when the project is initialized + canvasPresentation.getController().setActiveModel(Ecdar.getProject().getComponents().stream().findFirst().orElse(null)); + } + + canvasPane.getChildren().add(canvasPresentation); + activeCanvasPresentation.set(canvasPresentation); + + Rectangle clip = new Rectangle(); + clip.setArcWidth(1); + clip.setArcHeight(1); + clip.widthProperty().bind(canvasPane.widthProperty()); + clip.heightProperty().bind(canvasPane.heightProperty()); + + canvasPresentation.getController().zoomablePane.setClip(clip); + canvasPresentation.getController().zoomablePane.minWidthProperty().bind(canvasPane.widthProperty()); + canvasPresentation.getController().zoomablePane.maxWidthProperty().bind(canvasPane.widthProperty()); + canvasPresentation.getController().zoomablePane.minHeightProperty().bind(canvasPane.heightProperty()); + canvasPresentation.getController().zoomablePane.maxHeightProperty().bind(canvasPane.heightProperty()); + } + + /** + * Removes the canvas and adds a GridPane with four new canvases, with different active components, + * the first being the one previously displayed on the single canvas. + */ + void setCanvasModeToSplit() { + canvasPane.getChildren().clear(); + + GridPane canvasGrid = new GridPane(); + + canvasGrid.addColumn(0); + canvasGrid.addColumn(1); + canvasGrid.addRow(0); + canvasGrid.addRow(1); + + ColumnConstraints col1 = new ColumnConstraints(); + col1.setPercentWidth(50); + + RowConstraints row1 = new RowConstraints(); + row1.setPercentHeight(50); + + canvasGrid.getColumnConstraints().add(col1); + canvasGrid.getColumnConstraints().add(col1); + canvasGrid.getRowConstraints().add(row1); + canvasGrid.getRowConstraints().add(row1); + + ObservableList components = Ecdar.getProject().getComponents(); + int currentCompNum = 0, numComponents = components.size(); + + // Add the canvasPresentation at the top-left + CanvasPresentation canvasPresentation = initializeNewCanvasPresentation(); + canvasPresentation.getController().setActiveModel(getActiveCanvasPresentation().getController().getActiveModel()); + canvasGrid.add(canvasPresentation, 0, 0); + setActiveCanvasPresentation(canvasPresentation); + + // Add the canvasPresentation at the top-right + canvasPresentation = initializeNewCanvasPresentationWithActiveComponent(components, currentCompNum); + canvasPresentation.setOpacity(0.75); + canvasGrid.add(canvasPresentation, 1, 0); + + // Update the startIndex for the next canvasPresentation + for (int i = 0; i < numComponents; i++) { + if (canvasPresentation.getController().getActiveModel() != null && canvasPresentation.getController().getActiveModel().equals(components.get(i))) { + currentCompNum = i + 1; + } + } + + // Add the canvasPresentation at the bottom-left + canvasPresentation = initializeNewCanvasPresentationWithActiveComponent(components, currentCompNum); + canvasPresentation.setOpacity(0.75); + canvasGrid.add(canvasPresentation, 0, 1); + + // Update the startIndex for the next canvasPresentation + for (int i = 0; i < numComponents; i++) + if (canvasPresentation.getController().getActiveModel() != null && canvasPresentation.getController().getActiveModel().equals(components.get(i))) { + currentCompNum = i + 1; + } + + // Add the canvasPresentation at the bottom-right + canvasPresentation = initializeNewCanvasPresentationWithActiveComponent(components, currentCompNum); + canvasPresentation.setOpacity(0.75); + canvasGrid.add(canvasPresentation, 1, 1); + + canvasPane.getChildren().add(canvasGrid); + } + + /** + * Initialize a new CanvasShellPresentation and set its active component to the next component encountered from the startIndex and return it + * + * @param components the list of components for assigning active component of the CanvasPresentation + * @param startIndex the index to start at when trying to find the component to set as active + * @return new CanvasShellPresentation + */ + private CanvasPresentation initializeNewCanvasPresentationWithActiveComponent(ObservableList components, int startIndex) { + CanvasPresentation canvasPresentation = initializeNewCanvasPresentation(); + + int numComponents = components.size(); + canvasPresentation.getController().setActiveModel(null); + for (int currentCompNum = startIndex; currentCompNum < numComponents; currentCompNum++) { + if (getActiveCanvasPresentation().getController().getActiveModel() != components.get(currentCompNum)) { + canvasPresentation.getController().setActiveModel(components.get(currentCompNum)); + break; + } + } + + return canvasPresentation; + } + + /** + * Initialize a new CanvasPresentation and return it + * + * @return new CanvasPresentation + */ + private CanvasPresentation initializeNewCanvasPresentation() { + CanvasPresentation canvasPresentation = new CanvasPresentation(); + canvasPresentation.setBorder(new Border(new BorderStroke(Color.GREY.getColor(Color.Intensity.I500), BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderStroke.THIN))); + + // Set th clip of the zoomable pane to be half of the canvasPane, + // to ensure a 2 by 2 grid without overflowing borders + Rectangle clip = new Rectangle(); + clip.setArcWidth(1); + clip.setArcHeight(1); + clip.widthProperty().bind(canvasPane.widthProperty().divide(2)); + clip.heightProperty().bind(canvasPane.heightProperty().divide(2)); + canvasPresentation.getController().zoomablePane.setClip(clip); + + canvasPresentation.getController().zoomablePane.minWidthProperty().bind(canvasPane.widthProperty().divide(2)); + canvasPresentation.getController().zoomablePane.maxWidthProperty().bind(canvasPane.widthProperty().divide(2)); + canvasPresentation.getController().zoomablePane.minHeightProperty().bind(canvasPane.heightProperty().divide(2)); + canvasPresentation.getController().zoomablePane.maxHeightProperty().bind(canvasPane.heightProperty().divide(2)); + + return canvasPresentation; + } + + @FXML + private void undoClicked() { + UndoRedoStack.undo(); + } + + @FXML + private void redoClicked() { + UndoRedoStack.redo(); + } + + /** + * Switch to input edge mode + */ + @FXML + private void switchToInputClicked() { + setGlobalEdgeStatus(EdgeStatus.INPUT); + } + + /** + * Switch to output edge mode + */ + @FXML + private void switchToOutputClicked() { + setGlobalEdgeStatus(EdgeStatus.OUTPUT); + } + + /** + * Switch edge status. + */ + @FXML + private void switchEdgeStatusClicked() { + if (getGlobalEdgeStatus().equals(EdgeStatus.INPUT)) { + setGlobalEdgeStatus(EdgeStatus.OUTPUT); + } else { + setGlobalEdgeStatus(EdgeStatus.INPUT); + } + } + + @FXML + private void deleteSelectedClicked() { + if (SelectHelper.getSelectedElements().size() == 0) return; + + // Run through the selected elements and look for something that we can delete + SelectHelper.getSelectedElements().forEach(selectable -> { + if (selectable instanceof LocationController) { + ((LocationController) selectable).tryDelete(); + } else if (selectable instanceof EdgeController) { + final Component component = ((EdgeController) selectable).getComponent(); + final DisplayableEdge edge = ((EdgeController) selectable).getEdge(); + + // Dont delete edge if it is locked + if (edge.getIsLockedProperty().getValue()) { + return; + } + + UndoRedoStack.pushAndPerform(() -> { // Perform + // Remove the edge + component.removeEdge(edge); + }, () -> { // Undo + // Re-all the edge + component.addEdge(edge); + }, String.format("Deleted %s", selectable.toString()), "delete"); + } else if (selectable instanceof NailController) { + ((NailController) selectable).tryDelete(); + } + }); + + SelectHelper.clearSelectedElements(); + } +} diff --git a/src/main/java/ecdar/controllers/LeftSimPaneController.java b/src/main/java/ecdar/controllers/LeftSimPaneController.java new file mode 100755 index 00000000..d8dc30eb --- /dev/null +++ b/src/main/java/ecdar/controllers/LeftSimPaneController.java @@ -0,0 +1,25 @@ +package ecdar.controllers; + +import ecdar.presentations.TracePaneElementPresentation; +import ecdar.presentations.TransitionPaneElementPresentation; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.ScrollPane; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import java.net.URL; +import java.util.ResourceBundle; + +public class LeftSimPaneController implements Initializable { + public StackPane root; + public ScrollPane scrollPane; + public VBox scrollPaneVbox; + + public TransitionPaneElementPresentation transitionPanePresentation; + public TracePaneElementPresentation tracePanePresentation; + + @Override + public void initialize(URL location, ResourceBundle resources) { + + } +} diff --git a/src/main/java/ecdar/controllers/ProcessController.java b/src/main/java/ecdar/controllers/ProcessController.java new file mode 100755 index 00000000..6ce42fc2 --- /dev/null +++ b/src/main/java/ecdar/controllers/ProcessController.java @@ -0,0 +1,252 @@ +package ecdar.controllers; + +import com.jfoenix.controls.JFXRippler; +import ecdar.abstractions.*; +import ecdar.presentations.SimEdgePresentation; +import ecdar.presentations.SimLocationPresentation; +import javafx.animation.Interpolator; +import javafx.animation.Transition; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableMap; +import javafx.fxml.Initializable; +import javafx.scene.Node; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.*; +import javafx.scene.shape.Circle; +import javafx.util.Duration; +import org.kordamp.ikonli.javafx.FontIcon; + +import java.math.BigDecimal; +import java.net.URL; +import java.util.*; + +/** + * The controller for the process shown in the {@link SimulatorOverviewController} + */ +public class ProcessController extends ModelController implements Initializable { + public StackPane componentPane; + public Pane modelContainerEdge; + public Pane modelContainerLocation; + public JFXRippler toggleValuesButton; + public VBox valueArea; + public FontIcon toggleValueButtonIcon; + private ObjectProperty component; + + /** + * Keep track of locations/edges and their associated presentation class, by having the as key-value pairs in a Map + * E.g. a {@link Location} key is the model behind the value {@link SimLocationPresentation} view + */ + private final Map locationPresentationMap = new HashMap<>(); + private final Map edgePresentationMap = new HashMap<>(); + private final ObservableMap variables = FXCollections.observableHashMap(); + private final ObservableMap clocks = FXCollections.observableHashMap(); + + @Override + public void initialize(final URL location, final ResourceBundle resources) { + component = new SimpleObjectProperty<>(new Component(true)); + initializeValues(); + } + + private void initializeValues() { + final Circle circle = new Circle(0); + if (getComponent().isDeclarationOpen()) { + circle.setRadius(1000); + } + final ObjectProperty clip = new SimpleObjectProperty<>(circle); + valueArea.clipProperty().bind(clip); + clip.set(circle); + } + + /** + * Highlights the edges and accompanying source/target locations in the process + * @param edges The edges to highlight + */ + public void highlightEdges(final ArrayList edges) { + for (final Edge edge : edges) { + final Location source = edge.getSourceLocation(); + String sourceId = source.getId(); + final Location target = edge.getTargetLocation(); + String targetId = target.getId(); + + // If target name is empty the edge is a self loop + if (Objects.equals(targetId, "")) { + targetId = sourceId; + } + + boolean isSourceUniversal = false; + + // Iterate through all locations to check for Universal and Inconsistent locations + // The name of a Universal location may be "U2" in our system, but it is mapped to "Universal" in the engine + // This loop maps "Universal" to for example "U2" + for (Map.Entry locEntry : locationPresentationMap.entrySet()) { + if (locEntry.getKey().getType() == Location.Type.UNIVERSAL) { + if (sourceId.equals("Universal")) { + sourceId = locEntry.getKey().getId(); + isSourceUniversal = true; + } + + if (targetId.equals("Universal")) { + targetId = locEntry.getKey().getId(); + } + } + + if (locEntry.getKey().getType() == Location.Type.INCONSISTENT) { + if (sourceId.equals("Inconsistent")) { + sourceId = locEntry.getKey().getId(); + } + + if (targetId.equals("Inconsistent")) { + targetId = locEntry.getKey().getId(); + } + } + } + + // Self loop on a Universal locations means that the edge name should be mapped to * + String edgeId = edge.getId(); + if (isSourceUniversal && sourceId.equals(targetId)) { + edgeId = "*"; + } + + highlightEdge(edgeId, edge.getStatus(), sourceId, targetId); + } + } + + /** + * Unhighlights all edges and locations in the process + */ + public void unhighlightProcess() { + edgePresentationMap.forEach((key, value) -> value.getController().unhighlight()); + locationPresentationMap.forEach((key, value) -> value.unhighlight()); + } + + /** + * Helper method that finds the {@link SimLocationPresentation} and highlights it. + * Calls {@link ProcessController#highlightEdgeLocations(String, String)} to highlight the source/targets locations + * @param edgeName The name of the edge + * @param edgeStatus The status (input/output) of the edge to highlight + * @param sourceName The name of the source location + * @param targetName The name of the target location + */ + private void highlightEdge(final String edgeName, final EdgeStatus edgeStatus, final String sourceName, final String targetName) { + for (Map.Entry entry: edgePresentationMap.entrySet()) { + final String keyName = entry.getKey().getSync(); + final String keySourceId = entry.getKey().getSourceLocation().getId(); + final String keyTargetId = entry.getKey().getTargetLocation().getId(); + + // Multiple edges may have the same name, so we also check that the source and target match this edge + if(keyName.equals(edgeName) && + keySourceId.equals(sourceName) && + keyTargetId.equals(targetName) && + entry.getKey().ioStatus.get() == edgeStatus) { + + entry.getValue().getController().highlight(); + highlightEdgeLocations(keySourceId, keyTargetId); + } + } + } + + /** + * Helper method that finds the source/target {@link SimLocationPresentation} and highlights it + * @param sourceId The name of the source location + * @param targetId The name of the target location + */ + private void highlightEdgeLocations(final String sourceId, final String targetId) { + for (Map.Entry locEntry: locationPresentationMap.entrySet()) { + final String locationId = locEntry.getKey().getId(); + + // Check if location is either source or target and highlight it + if(locationId.equals(sourceId) || locationId.equals(targetId)) { + locEntry.getValue().highlight(); + } + } + } + + /** + * Method that highlights all locations with the input ID + * @param locationId The locations to highlight + */ + public void highlightLocation(final String locationId) { + for (Map.Entry locEntry: locationPresentationMap.entrySet()) { + if(locEntry.getKey().getId().equals(locationId)) { + locEntry.getValue().highlight(); + } + } + } + + /** + * Sets the component which is going to be shown as a process.
+ * This also initializes the rest of the views needed for the process to be shown properly + * @param component the component of the process + */ + public void setComponent(final Component component){ + this.component.set(component); + modelContainerEdge.getChildren().clear(); + modelContainerLocation.getChildren().clear(); + + component.getLocations().forEach(location -> { + final SimLocationPresentation lp = new SimLocationPresentation(location, component); + modelContainerLocation.getChildren().add(lp); + locationPresentationMap.put(location, lp); + }); + + component.getEdges().forEach(edge -> { + final SimEdgePresentation ep = new SimEdgePresentation(edge, component); + modelContainerEdge.getChildren().add(ep); + edgePresentationMap.put(edge, ep); + }); + } + + public void toggleValues(final MouseEvent mouseEvent) { + final Circle circle = new Circle(0); + circle.setCenterX(component.get().getBox().getWidth() - (toggleValuesButton.getWidth() - mouseEvent.getX())); + circle.setCenterY(-1 * mouseEvent.getY()); + + final ObjectProperty clip = new SimpleObjectProperty<>(circle); + valueArea.clipProperty().bind(clip); + + final Transition rippleEffect = new Transition() { + private final double maxRadius = Math.sqrt(Math.pow(getComponent().getBox().getWidth(), 2) + Math.pow(getComponent().getBox().getHeight(), 2)); + { + setCycleDuration(Duration.millis(500)); + } + + protected void interpolate(final double fraction) { + if (getComponent().isDeclarationOpen()) { + circle.setRadius(fraction * maxRadius); + } else { + circle.setRadius(maxRadius - fraction * maxRadius); + } + clip.set(circle); + } + }; + + final Interpolator interpolator = Interpolator.SPLINE(0.785, 0.135, 0.15, 0.86); + rippleEffect.setInterpolator(interpolator); + + rippleEffect.play(); + getComponent().declarationOpenProperty().set(!getComponent().isDeclarationOpen()); + } + + /** + * Gets the component linked to this process + * @return the component of the process + */ + public Component getComponent(){ + return component.get(); + } + + @Override + public HighLevelModelObject getModel() { + return component.get(); + } + + public ObservableMap getVariables() { + return variables; + } + + public ObservableMap getClocks() { + return clocks; + } +} diff --git a/src/main/java/ecdar/controllers/ProjectPaneController.java b/src/main/java/ecdar/controllers/ProjectPaneController.java index c80bcbc1..cc4c111b 100644 --- a/src/main/java/ecdar/controllers/ProjectPaneController.java +++ b/src/main/java/ecdar/controllers/ProjectPaneController.java @@ -12,6 +12,7 @@ import com.jfoenix.controls.JFXPopup; import com.jfoenix.controls.JFXRippler; import com.jfoenix.controls.JFXTextArea; +import javafx.application.Platform; import javafx.collections.ListChangeListener; import javafx.fxml.FXML; import javafx.fxml.Initializable; @@ -341,7 +342,7 @@ private void handleRemovedModel(final HighLevelModelObject model) { public void updateColorsOnFilePresentations() { for (Node child : filesList.getChildren()) { if (child instanceof FilePresentation) { - ((FilePresentation) child).updateColors(); + Platform.runLater(() -> ((FilePresentation) child).updateColors()); } } diff --git a/src/main/java/ecdar/controllers/RightSimPaneController.java b/src/main/java/ecdar/controllers/RightSimPaneController.java new file mode 100755 index 00000000..5b86fc1b --- /dev/null +++ b/src/main/java/ecdar/controllers/RightSimPaneController.java @@ -0,0 +1,20 @@ +package ecdar.controllers; + +import javafx.fxml.Initializable; +import javafx.scene.layout.*; + +import java.net.URL; +import java.util.ResourceBundle; + +/** + * Controller class for the right pane in the simulator + */ +public class RightSimPaneController implements Initializable { + public StackPane root; + public VBox scrollPaneVbox; +// public QueryPaneElementPresentation queryPaneElement; + + @Override + public void initialize(URL location, ResourceBundle resources) { + } +} diff --git a/src/main/java/ecdar/controllers/SimEdgeController.java b/src/main/java/ecdar/controllers/SimEdgeController.java new file mode 100755 index 00000000..2ca0048f --- /dev/null +++ b/src/main/java/ecdar/controllers/SimEdgeController.java @@ -0,0 +1,421 @@ +package ecdar.controllers; + +import ecdar.abstractions.Component; +import ecdar.abstractions.Edge; +import ecdar.abstractions.EdgeStatus; +import ecdar.abstractions.Nail; +import ecdar.model_canvas.arrow_heads.SimpleArrowHead; +import ecdar.presentations.Link; +import ecdar.presentations.NailPresentation; +import ecdar.presentations.SimNailPresentation; +import ecdar.utility.Highlightable; +import ecdar.utility.colors.Color; +import ecdar.utility.helpers.BindingHelper; +import ecdar.utility.helpers.Circular; +import ecdar.utility.helpers.ItemDragHelper; +import ecdar.utility.helpers.SelectHelper; +import ecdar.utility.keyboard.KeyboardTracker; +import javafx.animation.KeyFrame; +import javafx.animation.KeyValue; +import javafx.animation.Timeline; +import javafx.application.Platform; +import javafx.beans.property.*; +import javafx.beans.value.ChangeListener; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; +import javafx.fxml.Initializable; +import javafx.scene.Group; +import javafx.scene.Node; +import javafx.util.Duration; + +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.function.Consumer; + +/** + * The controller for the edge shown in the {@link ecdar.presentations.SimulatorOverviewPresentation} + */ +public class SimEdgeController implements Initializable, Highlightable { + private final ObservableList links = FXCollections.observableArrayList(); + private final ObjectProperty edge = new SimpleObjectProperty<>(); + private final ObjectProperty component = new SimpleObjectProperty<>(); + private final SimpleArrowHead simpleArrowHead = new SimpleArrowHead(); + private final SimpleBooleanProperty isHoveringEdge = new SimpleBooleanProperty(false); + private final SimpleIntegerProperty timeHoveringEdge = new SimpleIntegerProperty(0); + private final Map nailNailPresentationMap = new HashMap<>(); + public Group edgeRoot; + private Runnable collapseNail; + private Consumer enlargeNail; + private Consumer shrinkNail; + + @Override + public void initialize(final URL location, final ResourceBundle resources) { + initializeNailCollapse(); + + edge.addListener((obsEdge, oldEdge, newEdge) -> { + newEdge.targetCircularProperty().addListener(getNewTargetCircularListener(newEdge)); + component.addListener(getComponentChangeListener(newEdge)); + + // Invalidate the list of edges (to update UI and errors) + newEdge.targetCircularProperty().addListener(observable -> { + getComponent().removeEdge(getEdge()); + getComponent().addEdge(getEdge()); + }); + + // When an edge updates highlight property, + // we want to update the view to reflect current highlight property + edge.get().isHighlightedProperty().addListener(v -> { + if(edge.get().getIsHighlighted()) { + this.highlight(); + } else { + this.unhighlight(); + } + }); + }); + + ensureNailsInFront(); + } + + private void ensureNailsInFront() { + // When ever changes happens to the children of the edge root force nails in front and other elements to back + edgeRoot.getChildren().addListener((ListChangeListener) c -> { + while (c.next()) { + for (int i = 0; i < c.getAddedSize(); i++) { + final Node node = c.getAddedSubList().get(i); + if (node instanceof NailPresentation) { + node.toFront(); + } else { + node.toBack(); + } + } + } + }); + } + + private ChangeListener getComponentChangeListener(final Edge newEdge) { + return (obsComponent, oldComponent, newComponent) -> { + // Draw new edge from a location + if (newEdge.getNails().isEmpty() && newEdge.getTargetCircular() == null) { + final Link link = new Link(); + // Make dashed line, if output edge + if (newEdge.getStatus() == EdgeStatus.OUTPUT) link.makeDashed(); + links.add(link); + + // Add the link and its arrowhead to the view + edgeRoot.getChildren().addAll(link, simpleArrowHead); + + // Bind the first link and the arrowhead from the source location to the mouse + BindingHelper.bind(link, simpleArrowHead, newEdge.getSourceCircular(), + newComponent.getBox().getXProperty(), newComponent.getBox().getYProperty()); + } else if (newEdge.getTargetCircular() != null) { + + edgeRoot.getChildren().add(simpleArrowHead); + + final Circular[] previous = {newEdge.getSourceCircular()}; + + newEdge.getNails().forEach(nail -> { + final Link link = new Link(); + if (newEdge.getStatus() == EdgeStatus.OUTPUT) link.makeDashed(); + links.add(link); + + final SimNailPresentation nailPresentation = new SimNailPresentation(nail, newEdge, getComponent(), this); + nailNailPresentationMap.put(nail, nailPresentation); + + edgeRoot.getChildren().addAll(link, nailPresentation); + BindingHelper.bind(link, previous[0], nail); + + previous[0] = nail; + }); + + final Link link = new Link(); + if (newEdge.getStatus() == EdgeStatus.OUTPUT) link.makeDashed(); + links.add(link); + + edgeRoot.getChildren().add(link); + BindingHelper.bind(link, simpleArrowHead, previous[0], newEdge.getTargetCircular()); + } + + // Changes are made to the nails list + newEdge.getNails().addListener(getNailsChangeListener(newEdge, newComponent)); + + }; + } + + private ListChangeListener getNailsChangeListener(final Edge newEdge, final Component newComponent) { + return change -> { + while (change.next()) { + // There were added some nails + change.getAddedSubList().forEach(newNail -> { + // Create a new nail presentation based on the abstraction added to the list + final SimNailPresentation newNailPresentation = new SimNailPresentation(newNail, newEdge, newComponent, this); + nailNailPresentationMap.put(newNail, newNailPresentation); + + edgeRoot.getChildren().addAll(newNailPresentation); + + if (newEdge.getTargetCircular() != null) { + final int indexOfNewNail = edge.get().getNails().indexOf(newNail); + + final Link newLink = new Link(); + if (newEdge.getStatus() == EdgeStatus.OUTPUT) newLink.makeDashed(); + final Link pressedLink = links.get(indexOfNewNail); + links.add(indexOfNewNail, newLink); + + edgeRoot.getChildren().addAll(newLink); + + Circular oldStart = getEdge().getSourceCircular(); + Circular oldEnd = getEdge().getTargetCircular(); + + if (indexOfNewNail != 0) { + oldStart = getEdge().getNails().get(indexOfNewNail - 1); + } + + if (indexOfNewNail != getEdge().getNails().size() - 1) { + oldEnd = getEdge().getNails().get(indexOfNewNail + 1); + } + + BindingHelper.bind(newLink, oldStart, newNail); + + if (oldEnd.equals(getEdge().getTargetCircular())) { + BindingHelper.bind(pressedLink, simpleArrowHead, newNail, oldEnd); + } else { + BindingHelper.bind(pressedLink, newNail, oldEnd); + } + + if (isHoveringEdge.get()) { + enlargeNail.accept(newNail); + } + + } else { + // The previous last link must end in the new nail + final Link lastLink = links.get(links.size() - 1); + + // If the nail is the first in the list, bind it to the source location + // otherwise, bind it the the previous nail + final int nailIndex = edge.get().getNails().indexOf(newNail); + if (nailIndex == 0) { + BindingHelper.bind(lastLink, newEdge.getSourceCircular(), newNail); + } else { + final Nail previousNail = edge.get().getNails().get(nailIndex - 1); + BindingHelper.bind(lastLink, previousNail, newNail); + } + + // Create a new link that will bind from the new nail to the mouse + final Link newLink = new Link(); + if (newEdge.getStatus() == EdgeStatus.OUTPUT) newLink.makeDashed(); + links.add(newLink); + BindingHelper.bind(newLink, simpleArrowHead, newNail, newComponent.getBox().getXProperty(), newComponent.getBox().getYProperty()); + edgeRoot.getChildren().add(newLink); + } + }); + + change.getRemoved().forEach(removedNail -> { + final int removedIndex = change.getFrom(); + final SimNailPresentation removedNailPresentation = nailNailPresentationMap.remove(removedNail); + final Link danglingLink = links.get(removedIndex + 1); + edgeRoot.getChildren().remove(removedNailPresentation); + edgeRoot.getChildren().remove(links.get(removedIndex)); + + Circular newFrom = getEdge().getSourceCircular(); + Circular newTo = getEdge().getTargetCircular(); + + if (removedIndex > 0) { + newFrom = getEdge().getNails().get(removedIndex - 1); + } + + if (removedIndex - 1 != getEdge().getNails().size() - 1) { + newTo = getEdge().getNails().get(removedIndex); + } + + if (newTo.equals(getEdge().getTargetCircular())) { + BindingHelper.bind(danglingLink, simpleArrowHead, newFrom, newTo); + } else { + BindingHelper.bind(danglingLink, newFrom, newTo); + } + links.remove(removedIndex); + }); + } + }; + } + + private ChangeListener getNewTargetCircularListener(final Edge newEdge) { + // When the target location is set, finish drawing the edge + return (obsTargetLocation, oldTargetCircular, newTargetCircular) -> { + // If the nails list is empty, directly connect the source and target locations + // otherwise, bind the line from the last nail to the target location + final Link lastLink = links.get(links.size() - 1); + final ObservableList nails = getEdge().getNails(); + if (nails.size() == 0) { + BindingHelper.bind(lastLink, simpleArrowHead, newEdge.getSourceCircular(), newEdge.getTargetCircular()); + } else { + final Nail lastNail = nails.get(nails.size() - 1); + BindingHelper.bind(lastLink, simpleArrowHead, lastNail, newEdge.getTargetCircular()); + } + + KeyboardTracker.unregisterKeybind(KeyboardTracker.ABANDON_EDGE); + + // When the target location is set the + edgeRoot.setMouseTransparent(false); + }; + } + + /** + * Initializes functionality to enlarge, shirk, and collapse nails + */ + private void initializeNailCollapse() { + enlargeNail = nail -> { + if (!nail.getPropertyType().equals(Edge.PropertyType.NONE)) return; + final Timeline animation = new Timeline(); + + final KeyValue radius0 = new KeyValue(nail.radiusProperty(), NailPresentation.COLLAPSED_RADIUS); + final KeyValue radius2 = new KeyValue(nail.radiusProperty(), NailPresentation.HOVERED_RADIUS * 1.2); + final KeyValue radius1 = new KeyValue(nail.radiusProperty(), NailPresentation.HOVERED_RADIUS); + + final KeyFrame kf1 = new KeyFrame(Duration.millis(0), radius0); + final KeyFrame kf2 = new KeyFrame(Duration.millis(80), radius2); + final KeyFrame kf3 = new KeyFrame(Duration.millis(100), radius1); + + animation.getKeyFrames().addAll(kf1, kf2, kf3); + animation.play(); + }; + shrinkNail = nail -> { + if (!nail.getPropertyType().equals(Edge.PropertyType.NONE)) return; + final Timeline animation = new Timeline(); + + final KeyValue radius0 = new KeyValue(nail.radiusProperty(), NailPresentation.COLLAPSED_RADIUS); + final KeyValue radius1 = new KeyValue(nail.radiusProperty(), NailPresentation.HOVERED_RADIUS); + + final KeyFrame kf1 = new KeyFrame(Duration.millis(0), radius1); + final KeyFrame kf2 = new KeyFrame(Duration.millis(100), radius0); + + animation.getKeyFrames().addAll(kf1, kf2); + animation.play(); + }; + + collapseNail = () -> { + final int interval = 50; + int previousValue = 1; + + try { + while (true) { + Thread.sleep(interval); + + if (isHoveringEdge.get()) { + // Do not let the timer go above this threshold + if (timeHoveringEdge.get() <= 500) { + timeHoveringEdge.set(timeHoveringEdge.get() + interval); + } + } else { + timeHoveringEdge.set(timeHoveringEdge.get() - interval); + } + + if (previousValue >= 0 && timeHoveringEdge.get() < 0) { + // Run on UI thread + Platform.runLater(() -> { + // Collapse all nails + getEdge().getNails().forEach(shrinkNail); + }); + break; + } + previousValue = timeHoveringEdge.get(); + } + + } catch (final InterruptedException e) { + e.printStackTrace(); + } + }; + } + + public Edge getEdge() { + return edge.get(); + } + + public void setEdge(final Edge edge) { + this.edge.set(edge); + } + + public ObjectProperty edgeProperty() { + return edge; + } + + public Component getComponent() { + return component.get(); + } + + public void setComponent(final Component component) { + this.component.set(component); + } + + public ObjectProperty componentProperty() { + return component; + } + + /** + * Colors the edge model + * @param color the new color of the edge + * @param intensity the intensity of the edge + */ + public void color(final Color color, final Color.Intensity intensity) { + final Edge edge = getEdge(); + + // Set the color of the edge + edge.setColorIntensity(intensity); + edge.setColor(color); + } + + public Color getColor() { + return getEdge().getColor(); + } + + public Color.Intensity getColorIntensity() { + return getEdge().getColorIntensity(); + } + + public ItemDragHelper.DragBounds getDragBounds() { + return ItemDragHelper.DragBounds.generateLooseDragBounds(); + } + + /*** + * Highlights the child nodes of the edge + */ + @Override + public void highlight() { + // Clear the currently selected elements, so we don't have multiple things highlighted/selected + SelectHelper.clearSelectedElements(); + edgeRoot.getChildren().forEach(node -> { + if(node instanceof Highlightable) { + ((Highlightable) node).highlight(); + } + }); + } + + /*** + * Removes the highlight from child nodes + */ + @Override + public void unhighlight() { + edgeRoot.getChildren().forEach(node -> { + if(node instanceof Highlightable) { + ((Highlightable) node).unhighlight(); + } + }); + } + + public DoubleProperty xProperty() { + return edgeRoot.layoutXProperty(); + } + + public DoubleProperty yProperty() { + return edgeRoot.layoutYProperty(); + } + + public double getX() { + return xProperty().get(); + } + + public double getY() { + return yProperty().get(); + } +} diff --git a/src/main/java/ecdar/controllers/SimLocationController.java b/src/main/java/ecdar/controllers/SimLocationController.java new file mode 100755 index 00000000..c8c39522 --- /dev/null +++ b/src/main/java/ecdar/controllers/SimLocationController.java @@ -0,0 +1,124 @@ +package ecdar.controllers; + +import ecdar.abstractions.Component; +import ecdar.abstractions.Location; +import ecdar.presentations.SimLocationPresentation; +import ecdar.presentations.SimTagPresentation; +import ecdar.utility.colors.Color; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.fxml.Initializable; +import javafx.scene.Group; +import javafx.scene.control.Label; +import javafx.scene.shape.Circle; +import javafx.scene.shape.Line; +import javafx.scene.shape.Path; + +import java.net.URL; +import java.util.ResourceBundle; + +/** + * The controller of a location shown in the {@link ecdar.presentations.SimulatorOverviewPresentation} + */ +public class SimLocationController implements Initializable { + private final ObjectProperty location = new SimpleObjectProperty<>(); + private final ObjectProperty component = new SimpleObjectProperty<>(); + public SimLocationPresentation root; + public Path notCommittedShape; + public Path notCommittedInitialIndicator; + public Group shakeContent; + public Circle circle; + public Circle circleShakeIndicator; + public Group scaleContent; + public SimTagPresentation nicknameTag; + public SimTagPresentation invariantTag; + public Label idLabel; + public Line nameTagLine; + public Line invariantTagLine; + + @Override + public void initialize(final URL location, final ResourceBundle resources) { + this.location.addListener((obsLocation, oldLocation, newLocation) -> { + // The radius property on the abstraction must reflect the radius in the view + newLocation.radiusProperty().bind(circle.radiusProperty()); + + // The scale property on the abstraction must reflect the radius in the view + newLocation.scaleProperty().bind(scaleContent.scaleXProperty()); + }); + + // Scale x and y 1:1 (based on the x-scale) + scaleContent.scaleYProperty().bind(scaleContent.scaleXProperty()); + } + + public Location getLocation() { + return location.get(); + } + + /** + * Set/places the given location on the view. + * This have to be done before adding the {@link SimLocationPresentation} to the view as nothing + * would then be displayed. + * @param location the location + */ + public void setLocation(final Location location) { + this.location.set(location); + root.setLayoutX(location.getX()); + root.setLayoutY(location.getY()); + location.xProperty().bindBidirectional(root.layoutXProperty()); + location.yProperty().bindBidirectional(root.layoutYProperty()); + } + + public ObjectProperty locationProperty() { + return location; + } + + public Component getComponent() { + return component.get(); + } + + public void setComponent(final Component component) { + this.component.set(component); + } + + public ObjectProperty componentProperty() { + return component; + } + + /** + * Colors the location model + * @param color the new color of the location + * @param intensity the intensity of the color + */ + public void color(final Color color, final Color.Intensity intensity) { + final Location location = getLocation(); + + // Set the color of the location + location.setColorIntensity(intensity); + location.setColor(color); + } + + public Color getColor() { + return getLocation().getColor(); + } + + public Color.Intensity getColorIntensity() { + return getLocation().getColorIntensity(); + } + + public DoubleProperty xProperty() { + return root.layoutXProperty(); + } + + public DoubleProperty yProperty() { + return root.layoutYProperty(); + } + + public double getX() { + return xProperty().get(); + } + + public double getY() { + return yProperty().get(); + } +} diff --git a/src/main/java/ecdar/controllers/SimNailController.java b/src/main/java/ecdar/controllers/SimNailController.java new file mode 100755 index 00000000..053456d3 --- /dev/null +++ b/src/main/java/ecdar/controllers/SimNailController.java @@ -0,0 +1,126 @@ +package ecdar.controllers; + +import ecdar.Debug; +import ecdar.abstractions.Component; +import ecdar.abstractions.Edge; +import ecdar.abstractions.Nail; +import ecdar.presentations.SimNailPresentation; +import ecdar.presentations.SimTagPresentation; +import ecdar.utility.colors.Color; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.fxml.Initializable; +import javafx.scene.Group; +import javafx.scene.control.Label; +import javafx.scene.shape.Circle; +import javafx.scene.shape.Line; + +import java.net.URL; +import java.util.ResourceBundle; + +public class SimNailController implements Initializable { + private final ObjectProperty component = new SimpleObjectProperty<>(); + private final ObjectProperty edge = new SimpleObjectProperty<>(); + private final ObjectProperty nail = new SimpleObjectProperty<>(); + + private SimEdgeController edgeController; + public SimNailPresentation root; + public Circle nailCircle; + public Circle dragCircle; + public Line propertyTagLine; + public SimTagPresentation propertyTag; + public Group dragGroup; + public Label propertyLabel; + + @Override + public void initialize(final URL location, final ResourceBundle resources) { + nail.addListener((obsNail, oldNail, newNail) -> { + + // The radius from the abstraction is the master and the view simply reflects what is in the model + nailCircle.radiusProperty().bind(newNail.radiusProperty()); + + // Draw the presentation based on the initial value from the abstraction + root.setLayoutX(newNail.getX()); + root.setLayoutY(newNail.getY()); + + // Reflect future updates from the presentation into the abstraction + newNail.xProperty().bindBidirectional(root.layoutXProperty()); + newNail.yProperty().bindBidirectional(root.layoutYProperty()); + + }); + + // Debug visuals + dragCircle.opacityProperty().bind(Debug.draggableAreaOpacity); + dragCircle.setFill(Debug.draggableAreaColor.getColor(Debug.draggableAreaColorIntensity)); + } + + /** + * Sets an edge controller. + * This should be called when adding a nail. + * @param controller the edge controller + */ + public void setEdgeController(final SimEdgeController controller) { + this.edgeController = controller; + } + + public Nail getNail() { + return nail.get(); + } + + public void setNail(final Nail nail) { + this.nail.set(nail); + } + + public ObjectProperty nailProperty() { + return nail; + } + + public Component getComponent() { + return component.get(); + } + + public void setComponent(final Component component) { + this.component.set(component); + } + + public ObjectProperty componentProperty() { + return component; + } + + public Edge getEdge() { + return edge.get(); + } + + public void setEdge(final Edge edge) { + this.edge.set(edge); + } + + public ObjectProperty edgeProperty() { + return edge; + } + + public Color getColor() { + return getComponent().getColor(); + } + + public Color.Intensity getColorIntensity() { + return getComponent().getColorIntensity(); + } + + public DoubleProperty xProperty() { + return root.layoutXProperty(); + } + + public DoubleProperty yProperty() { + return root.layoutYProperty(); + } + + public double getX() { + return xProperty().get(); + } + + public double getY() { + return yProperty().get(); + } +} diff --git a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java new file mode 100644 index 00000000..1eb1529c --- /dev/null +++ b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java @@ -0,0 +1,18 @@ +package ecdar.controllers; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXComboBox; +import javafx.fxml.Initializable; + +import java.net.URL; +import java.util.ResourceBundle; + +public class SimulationInitializationDialogController implements Initializable { + public JFXComboBox simulationComboBox; + public JFXButton cancelButton; + public JFXButton startButton; + + public void initialize(URL location, ResourceBundle resources) { + + } +} diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java new file mode 100644 index 00000000..bdba66b4 --- /dev/null +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -0,0 +1,149 @@ +package ecdar.controllers; + +import ecdar.Ecdar; +import ecdar.abstractions.*; +import ecdar.simulation.SimulationHandler; +import ecdar.presentations.SimulatorOverviewPresentation; +import ecdar.simulation.SimulationState; +import ecdar.simulation.Transition; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.fxml.Initializable; +import javafx.scene.layout.StackPane; + +import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.ResourceBundle; + +public class SimulatorController implements Initializable { + public StackPane root; + public SimulatorOverviewPresentation overviewPresentation; + public StackPane toolbar; + + private boolean firstTimeInSimulator; + private final static DoubleProperty width = new SimpleDoubleProperty(), + height = new SimpleDoubleProperty(); + private static ObjectProperty selectedTransition = new SimpleObjectProperty<>(); + private static ObjectProperty selectedState = new SimpleObjectProperty<>(); + + @Override + public void initialize(URL location, ResourceBundle resources) { + root.widthProperty().addListener((observable, oldValue, newValue) -> width.setValue(newValue)); + root.heightProperty().addListener((observable, oldValue, newValue) -> height.setValue(newValue)); + firstTimeInSimulator = true; + } + + /** + * Prepares the simulator to be shown.
+ * It also prepares the processes to be shown in the {@link SimulatorOverviewPresentation} by:
+ * - Building the system if it has been updated or have never been created.
+ * - Adding the components which are going to be used in the simulation to + */ + public void willShow() { + final SimulationHandler sm = Ecdar.getSimulationHandler(); + boolean shouldSimulationBeReset = true; + + if (sm.getCurrentState() == null) sm.initialStep(); // ToDo NIELS: Find better solution + + //Have the user left a trace or is he simulating a query + if (sm.traceLog.size() >= 2 || sm.getCurrentSimulation().contains(SimulationHandler.QUERY_PREFIX)) { + shouldSimulationBeReset = false; + } + + if (!firstTimeInSimulator && !new HashSet<>(overviewPresentation.getController().getComponentObservableList()) + .containsAll(findComponentsInCurrentSimulation())) { + shouldSimulationBeReset = true; + } + + if (shouldSimulationBeReset || firstTimeInSimulator) { + + resetSimulation(); + sm.resetToInitialLocation(); + } + overviewPresentation.getController().addProcessesToGroup(); + overviewPresentation.getController().highlightProcessState(sm.getCurrentState()); + } + + /** + * Resets the current simulation, and prepares for a new simulation by clearing the + * {@link SimulatorOverviewController#processContainer} and adding the processes of the new simulation. + */ + private void resetSimulation() { + final SimulationHandler sm = Ecdar.getSimulationHandler(); + sm.initializeDefaultSystem(); + overviewPresentation.getController().clearOverview(); + overviewPresentation.getController().getComponentObservableList().clear(); + overviewPresentation.getController().getComponentObservableList().addAll(findComponentsInCurrentSimulation()); + firstTimeInSimulator = false; + } + + /** + * Finds the components that are used in the current simulation by looking at the component found in + * {@link Project#getComponents()} and compare them to the processes declared in the {@link SimulationHandler#getSystem()} + *

+ * TODO This does currently not work if the same component is used multiple times. + * + * @return all the components used in the current simulation + */ + private List findComponentsInCurrentSimulation() { + //Show components from the system + final SimulationHandler sm = Ecdar.getSimulationHandler(); + List components = new ArrayList<>(); + components = Ecdar.getProject().getComponents(); +// for (int i = 0; i < sm.getSystem().getNoOfProcesses(); i++) { +// final int finalI = i; // when using a var in lambda it has to be final +// final List filteredList = Ecdar.getProject().getComponents().filtered(component -> { +// return component.getName().contentEquals(sm.getSystem().getProcess(finalI).getName()); +// }); +// components.addAll(filteredList); +// } + return components; + } + + /** + * Resets the simulation and prepares the view for showing the new simulation to the user + */ + public void resetCurrentSimulation() { + overviewPresentation.getController().removeProcessesFromGroup(); + resetSimulation(); + Ecdar.getSimulationHandler().resetToInitialLocation(); + overviewPresentation.getController().addProcessesToGroup(); + } + + public void willHide() { + overviewPresentation.getController().removeProcessesFromGroup(); + overviewPresentation.getController().getComponentObservableList().forEach(component -> { + // Previously reset coordinates of component box + }); + overviewPresentation.getController().unhighlightProcesses(); + } + + public static DoubleProperty getWidthProperty() { + return width; + } + + public static DoubleProperty getHeightProperty() { + return height; + } + + + public static ObjectProperty getSelectedTransitionProperty() { + return selectedTransition; + } + + public static void setSelectedTransition(Transition selectedTransition) { + SimulatorController.selectedTransition.set(selectedTransition); + } + + public static ObjectProperty getSelectedStateProperty() { + return selectedState; + } + + public static void setSelectedState(SimulationState selectedState) { + SimulatorController.selectedState.set(selectedState); + } +} diff --git a/src/main/java/ecdar/controllers/SimulatorOverviewController.java b/src/main/java/ecdar/controllers/SimulatorOverviewController.java new file mode 100644 index 00000000..9af4af76 --- /dev/null +++ b/src/main/java/ecdar/controllers/SimulatorOverviewController.java @@ -0,0 +1,385 @@ +package ecdar.controllers; + +import ecdar.Ecdar; +import ecdar.abstractions.*; +import ecdar.presentations.ProcessPresentation; +import ecdar.simulation.SimulationState; +import ecdar.simulation.Transition; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; +import javafx.beans.InvalidationListener; +import javafx.collections.*; +import javafx.fxml.Initializable; +import javafx.geometry.Insets; +import javafx.scene.Group; +import javafx.scene.control.ScrollPane; +import javafx.scene.layout.*; +import javafx.util.Pair; + +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; + +/** + * The controller of the middle part of the simulator. + * It is here where processes of a simulation will be shown. + */ +public class SimulatorOverviewController implements Initializable { + public AnchorPane root; + public ScrollPane scrollPane; + public FlowPane processContainer; + public Group groupContainer; + + /** + * The amount that is going be zoomed in/out for each press on + or - + */ + private final double SCALE_DELTA = 1.1; + + /** + * The max that the user can zoom in + */ + private static final double MAX_ZOOM_IN = 1.6; + + /** + * The max that the user can zoom in + */ + private static final double MAX_ZOOM_OUT = 0.5; + + /** + * Offset such that the view does not overlap with the scroll bar on the right hand sig. + */ + private static final int SUPER_SPECIAL_SCROLLPANE_OFFSET = 20; + + private final ObservableList componentArrayList = FXCollections.observableArrayList(); + private final ObservableMap processPresentations = FXCollections.observableHashMap(); + + /** + * Is true if a reset of the zoom have been requested, false if not. + */ + private boolean resetZoom = false; + private boolean isMaxZoomInReached = false; + private boolean isMaxZoomOutReached = false; + + @Override + public void initialize(final URL location, final ResourceBundle resources) { + groupContainer = new Group(); + processContainer = new FlowPane(); + //In case that the processContainer gets moved around we have to keep in into place. + initializeProcessContainer(); + + initializeWindowResizing(); + initializeZoom(); + initializeHighlighting(); + initializeSimulationVariables(); + // Add the processes and group to the view + addProcessesToGroup(); + scrollPane.setContent(groupContainer); + } + + /** + * Initializes the {@link #processContainer} with its correct styling, and placement on the view. + * It also adds a {@link ListChangeListener} on {@link #componentArrayList} where it adds the + * {@link Component}s which are needed to the processContainer. + */ + private void initializeProcessContainer() { + processContainer.translateXProperty().addListener((observable, oldValue, newValue) -> { + processContainer.setTranslateX(0); + }); + //Sets the space between the processes + processContainer.setHgap(10); + processContainer.setVgap(10); + + // padding to the scrollpane + processContainer.setPadding(new Insets(5)); + + componentArrayList.addListener((ListChangeListener) c -> { + final Map processes = new HashMap<>(); + while (c.next()) { + if (c.wasRemoved()) { + clearOverview(); + } else { + c.getAddedSubList().forEach(o -> processes.put(o.getName(), new ProcessPresentation(o))); + } + } + // Highlight the current state when the processes change + highlightProcessState(Ecdar.getSimulationHandler().getCurrentState()); // ToDo NIELS: Throws NullPointerException inside method due to currentState + processContainer.getChildren().addAll(processes.values()); + processPresentations.putAll(processes); + }); + + final Map processes = new HashMap<>(); + componentArrayList.forEach(o -> processes.put(o.getName(), new ProcessPresentation(o))); + + processContainer.getChildren().addAll(processes.values()); + processPresentations.putAll(processes); + } + + /** + * Clears the {@link #processContainer} and the {@link #processPresentations}. + */ + void clearOverview() { + processContainer.getChildren().clear(); + processPresentations.clear(); + } + + /** + * Setup listeners for displaying clock and variable values on the {@link ProcessPresentation} + */ + private void initializeSimulationVariables() { + Ecdar.getSimulationHandler().getSimulationVariables().addListener((InvalidationListener) obs -> { + Ecdar.getSimulationHandler().getSimulationVariables().forEach((s, bigDecimal) -> { + if (!s.equals("t(0)")) {// As t(0) does not belong to any process + final String[] spittedString = s.split("\\."); + // If the process containing the var is not there we just skip it + if (spittedString.length > 0 && processPresentations.size() > 0) { + processPresentations.get(spittedString[0]).getController().getVariables().put(spittedString[1], bigDecimal); + } + } + }); + }); + Ecdar.getSimulationHandler().getSimulationClocks().addListener((InvalidationListener) obs -> { + if (processPresentations.size() == 0) return; + Ecdar.getSimulationHandler().getSimulationClocks().forEach((s, bigDecimal) -> { + if (!s.equals("t(0)")) {// As t(0) does not belong to any process + final String[] spittedString = s.split("\\."); + // If the process containing the clock is not there we just skip it + if (spittedString.length > 0 && processPresentations.size() > 0) { + processPresentations.get(spittedString[0]).getController().getClocks().put(spittedString[1], bigDecimal); + } + } + }); + }); + } + + /** + * Removes {@link #processContainer} from the {@link #groupContainer}.
+ * In this way the {@link Component}s in the processContainer will then again be resizable, + * as the class {@link Group} makes its children not resizeable. + * + * @see Group + */ + void removeProcessesFromGroup() { + groupContainer.getChildren().removeAll(processContainer); + } + + /** + * Adds the {@link #processContainer} to the {@link #groupContainer}.
+ * This method is usually needed to called if {@link #removeProcessesFromGroup()} have been called, or + * if the processContainer just need to be added to the groupContainer.
+ * This method makes sure that the processContainer will be added to the groupContainer + * which is needed to show the {@link ProcessPresentation}s in the {@link #scrollPane}. + * If the processContainer is already contained in the groupContainer + * the method does nothing but return. + * + * @see #removeProcessesFromGroup() + */ + void addProcessesToGroup() { + if (groupContainer.getChildren().contains(processContainer)) return; + groupContainer.getChildren().add(processContainer); + } + + /** + * Initializes the zoom functionality in {@link #processContainer} + */ + private void initializeZoom() { + processContainer.scaleXProperty().addListener((observable, oldValue, newValue) -> { + if (newValue.doubleValue() > MAX_ZOOM_IN) isMaxZoomInReached = true; + if (newValue.doubleValue() < MAX_ZOOM_OUT) isMaxZoomOutReached = true; + + handleWidthOnScale(oldValue, newValue); + }); + + // to support pinch zooming + //TODO this should be fixed at as it does not work as it should + /* + processContainer.setOnZoom(event -> { + //Tries to zoom in/out but max is reached + if(event.getZoomFactor() >= 1 && isMaxZoomInReached) return; + if(event.getZoomFactor() < 1 && isMaxZoomOutReached) return; + + isMaxZoomInReached = false; + isMaxZoomOutReached = false; + + processContainer.setScaleX(processContainer.getScaleX() * event.getZoomFactor()); + processContainer.setScaleY(processContainer.getScaleY() * event.getZoomFactor()); + });*/ + } + + /** + * Initializes listener for change of width in {@link #scrollPane} which also affects {@link #processContainer}
+ * This does also take the zooming into account when doing the resizing. + */ + private void initializeWindowResizing() { + scrollPane.widthProperty().addListener((observable, oldValue, newValue) -> { + final double width = (newValue.doubleValue()) * (1 + (1 - processContainer.getScaleX())); + if (processContainer.getScaleX() > 1) { //Zoomed in + processContainer.setMinWidth(width); + processContainer.setMaxWidth(width); + } else if (processContainer.getScaleX() < 1) { //Zoomed out + processContainer.setMinWidth(width); + processContainer.setMaxWidth(width); + final double deltaWidth = newValue.doubleValue() - groupContainer.layoutBoundsProperty().get().getWidth(); + processContainer.setMinWidth(processContainer.getWidth() + (deltaWidth - SUPER_SPECIAL_SCROLLPANE_OFFSET) * (1 + (1 - processContainer.getScaleX()))); + processContainer.setMaxWidth(processContainer.getWidth() + (deltaWidth - SUPER_SPECIAL_SCROLLPANE_OFFSET) * (1 + (1 - processContainer.getScaleX()))); + } else { // Reset + processContainer.setMinWidth(newValue.doubleValue() - SUPER_SPECIAL_SCROLLPANE_OFFSET); + processContainer.setMaxWidth(newValue.doubleValue() - SUPER_SPECIAL_SCROLLPANE_OFFSET); + } + }); + } + + /** + * Increments the {@link #processContainer} scaleX and scaleY properties + * which creates the zoom-in feeling. Resizing of the view is handled by {@link #handleWidthOnScale(Number, Number)} + * + * @see FlowPane#scaleXProperty() + * @see FlowPane#scaleYProperty() + */ + void zoomIn() { + if (isMaxZoomInReached) return; + isMaxZoomOutReached = false; + processContainer.setScaleX(processContainer.getScaleX() * SCALE_DELTA); + processContainer.setScaleY(processContainer.getScaleY() * SCALE_DELTA); + } + + + /** + * Decrements the {@link #processContainer} scaleX and scaleY properties + * which creates the zoom-in feeling. Resizing of the view is handled by {@link #handleWidthOnScale(Number, Number)} + * + * @see FlowPane#scaleXProperty() + * @see FlowPane#scaleYProperty() + */ + void zoomOut() { + if (isMaxZoomOutReached) return; + isMaxZoomInReached = false; + processContainer.setScaleX(processContainer.getScaleX() * (1 / SCALE_DELTA)); + processContainer.setScaleY(processContainer.getScaleY() * (1 / SCALE_DELTA)); + } + + /** + * Resets the scaling of the {@link #processContainer}, and hereby the zoom + * + * @see FlowPane#scaleXProperty() + * @see FlowPane#scaleYProperty() + */ + void resetZoom() { + if (processContainer.getScaleX() == 1) return; + resetZoom = true; + isMaxZoomInReached = false; + isMaxZoomOutReached = false; + processContainer.setScaleX(1); + processContainer.setScaleY(1); + } + + /** + * Handles the scaling of the width of the {@link #processContainer} + * + * @param oldValue the width of {@link #scrollPane} before the change + * @param newValue the width of {@link #scrollPane} after the change + */ + private void handleWidthOnScale(final Number oldValue, final Number newValue) { + if (resetZoom) { //Zoom reset + resetZoom = false; + processContainer.setMinWidth(scrollPane.getWidth() - SUPER_SPECIAL_SCROLLPANE_OFFSET); + processContainer.setMaxWidth(scrollPane.getWidth() - SUPER_SPECIAL_SCROLLPANE_OFFSET); + } else if (oldValue.doubleValue() > newValue.doubleValue()) { //Zoom in + resetZoom = false; + processContainer.setMinWidth(Math.round(processContainer.getWidth() * SCALE_DELTA)); + processContainer.setMaxWidth(Math.round(processContainer.getWidth() * SCALE_DELTA)); + } else { // Zoom out + resetZoom = false; + processContainer.setMinWidth(Math.round(processContainer.getWidth() * (1 / SCALE_DELTA))); + processContainer.setMaxWidth(Math.round(processContainer.getWidth() * (1 / SCALE_DELTA))); + } + } + + /** + * Initializer method to setup listeners that handle highlighting when selected/current state/transition changes + */ + private void initializeHighlighting() { + SimulatorController.getSelectedTransitionProperty().addListener((observable, oldTransition, newTransition) -> { + unhighlightProcesses(); + + // If the new transition is not null, we want to highlight the locations and edges in the new value + // otherwise we highlight the current state + if (newTransition != null) { + highlightProcessTransition(newTransition); + } else { + highlightProcessState(Ecdar.getSimulationHandler().getCurrentState()); + } + }); + + SimulatorController.getSelectedStateProperty().addListener((observable, oldState, newState) -> { + unhighlightProcesses(); + + // If the new state is not null, we want to highlight the locations in the new value + // otherwise we highlight the current state + if (newState != null) { + highlightProcessState(newState); + } else { + highlightProcessState(Ecdar.getSimulationHandler().getCurrentState()); + } + }); + } + + /** + * Highlights all the processes involved in the transition. + * Finds the processes involved in the transition (processes with edges in the transition) and highlights their edges + * Also fades processes that are not active in the selected transition + * + * @param transition The transition for which we highlight the involved processes + */ + public void highlightProcessTransition(final Transition transition) { + final var edges = transition.getEdges(); + + // List of all processes to show as inactive if they are not involved in a transition + // Processes are removed from this list, if they have an edge in the transition + final ArrayList processesToHide = new ArrayList<>(processPresentations.values()); + + for (final ProcessPresentation processPresentation : processPresentations.values()) { + + // Find the processes that have edges involved in this transition + processPresentation.getController().highlightEdges(edges); + processesToHide.remove(processPresentation); + } + + processesToHide.forEach(ProcessPresentation::showInactive); + } + + /** + * Unhighlights all processes + */ + public void unhighlightProcesses() { + for (final ProcessPresentation presentation : processPresentations.values()) { + presentation.getController().unhighlightProcess(); + presentation.showActive(); + } + } + + /** + * Finds the processes for the input locations in the input {@link SimulationState} and highlights the locations. + * + * @param state The state with the locations to highlight + */ + public void highlightProcessState(final SimulationState state) { + for (int i = 0; i < state.getLocations().size(); i++) { + final Pair loc = state.getLocations().get(i); + + for (final ProcessPresentation presentation : processPresentations.values()) { + final String processName = presentation.getController().getComponent().getName(); + + if (processName.equals(loc.getKey())) { + presentation.getController().highlightLocation(loc.getValue()); + } + } + } + } + + public ObservableList getComponentObservableList() { + return componentArrayList; + } +} diff --git a/src/main/java/ecdar/controllers/TracePaneElementController.java b/src/main/java/ecdar/controllers/TracePaneElementController.java new file mode 100755 index 00000000..6ace31df --- /dev/null +++ b/src/main/java/ecdar/controllers/TracePaneElementController.java @@ -0,0 +1,187 @@ +package ecdar.controllers; + +import com.jfoenix.controls.JFXRippler; +import ecdar.Ecdar; +import ecdar.abstractions.Location; +import ecdar.simulation.SimulationState; +import ecdar.simulation.SimulationHandler; +import ecdar.presentations.TransitionPresentation; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.collections.ListChangeListener; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Label; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import org.kordamp.ikonli.javafx.FontIcon; + +import java.net.URL; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.ResourceBundle; + +/** + * The controller class for the trace pane element that can be inserted into a simulator pane + */ +public class TracePaneElementController implements Initializable { + public VBox root; + public HBox toolbar; + public Label traceTitle; + public JFXRippler expandTrace; + public VBox traceList; + public FontIcon expandTraceIcon; + public AnchorPane traceSummary; + public Label summaryTitleLabel; + public Label summarySubtitleLabel; + + private SimpleBooleanProperty isTraceExpanded = new SimpleBooleanProperty(false); + private Map transitionPresentationMap = new LinkedHashMap<>(); + private SimpleIntegerProperty numberOfSteps = new SimpleIntegerProperty(0); + + @Override + public void initialize(URL location, ResourceBundle resources) { + Ecdar.getSimulationHandler().getTraceLog().addListener((ListChangeListener) c -> { + while (c.next()) { + for (final SimulationState state : c.getAddedSubList()) { + insertTraceState(state, true); + } + + for (final SimulationState state : c.getRemoved()) { + traceList.getChildren().remove(transitionPresentationMap.get(state)); + transitionPresentationMap.remove(state); + } + } + + numberOfSteps.set(transitionPresentationMap.size()); + }); + + initializeTraceExpand(); + } + + /** + * Initializes the expand functionality that allows the user to show or hide the trace. + * By default the trace is shown. + */ + private void initializeTraceExpand() { + isTraceExpanded.addListener((obs, oldVal, newVal) -> { + if (newVal) { + showTrace(); + expandTraceIcon.setIconLiteral("gmi-expand-less"); + expandTraceIcon.setIconSize(24); + } else { + hideTrace(); + expandTraceIcon.setIconLiteral("gmi-expand-more"); + expandTraceIcon.setIconSize(24); + } + }); + + isTraceExpanded.set(true); + } + + + /** + * Removes all the trace view elements as to hide the trace from the user + * Also shows the summary view when the trace is hidden + */ + private void hideTrace() { + traceList.getChildren().clear(); + root.getChildren().add(traceSummary); + } + + /** + * Shows the trace by inserting a {@link TransitionPresentation} for each trace state + * Also hides the summary view, since it should only be visible when the trace is hidden + */ + private void showTrace() { + transitionPresentationMap.forEach((state, presentation) -> { + insertTraceState(state, false); + }); + root.getChildren().remove(traceSummary); + } + + /** + * Instantiates a {@link TransitionPresentation} for a {@link SimulationState} and adds it to the view + * + * @param state The state the should be inserted into the trace log + * @param shouldAnimate A boolean that indicates whether the trace should fade in when added to the view + */ + private void insertTraceState(final SimulationState state, final boolean shouldAnimate) { + final TransitionPresentation transitionPresentation = new TransitionPresentation(); + transitionPresentationMap.put(state, transitionPresentation); + + transitionPresentation.setOnMouseReleased(event -> { + event.consume(); + final SimulationHandler simHandler = Ecdar.getSimulationHandler(); + if (simHandler == null) return; + Ecdar.getSimulationHandler().selectTransitionFromLog(state); + }); + + EventHandler mouseEntered = transitionPresentation.getOnMouseEntered(); + transitionPresentation.setOnMouseEntered(event -> { + SimulatorController.setSelectedState(state); + mouseEntered.handle(event); + }); + + EventHandler mouseExited = transitionPresentation.getOnMouseExited(); + transitionPresentation.setOnMouseExited(event -> { + SimulatorController.setSelectedState(null); + mouseExited.handle(event); + }); + + + String title = traceString(state); + transitionPresentation.getController().setTitle(title); + + // Only insert the presentation into the view if the trace is expanded + if (isTraceExpanded.get()) { + traceList.getChildren().add(transitionPresentation); + if (shouldAnimate) { + transitionPresentation.playFadeAnimation(); + } + } + } + + /** + * A helper method that returns a string representing a state in the trace log + * + * @param state The SimulationState to represent + * @return A string representing the state + */ + private String traceString(SimulationState state) { + StringBuilder title = new StringBuilder("("); + int length = state.getLocations().size(); + for (int i = 0; i < length; i++) { + Location loc = Ecdar.getProject() + .findComponent(state.getLocations().get(i).getKey()) + .findLocation(state.getLocations().get(i).getValue()); + String locationName = loc.getNickname(); + if (i == length - 1) { + title.append(locationName); + } else { + title.append(locationName).append(", "); + } + } + title.append(")"); + + return title.toString(); + } + + /** + * Method to be called when clicking on the expand rippler in the trace toolbar + */ + @FXML + private void expandTrace() { + if (isTraceExpanded.get()) { + isTraceExpanded.set(false); + } else { + isTraceExpanded.set(true); + } + } + + public SimpleIntegerProperty getNumberOfStepsProperty() { + return numberOfSteps; + } +} diff --git a/src/main/java/ecdar/controllers/TransitionController.java b/src/main/java/ecdar/controllers/TransitionController.java new file mode 100755 index 00000000..7bacda2e --- /dev/null +++ b/src/main/java/ecdar/controllers/TransitionController.java @@ -0,0 +1,45 @@ +package ecdar.controllers; + +import com.jfoenix.controls.JFXRippler; +import ecdar.simulation.Transition; +import javafx.beans.property.SimpleObjectProperty; +import javafx.fxml.Initializable; +import javafx.scene.control.Label; +import javafx.scene.layout.AnchorPane; + +import java.net.URL; +import java.util.ResourceBundle; + +/** + * The controller class for the transition view element. + * It represents a single transition and may be used by classes like {@see TransitionPaneElementController} + * to show a list of transitions + */ +public class TransitionController implements Initializable { + public AnchorPane root; + public Label titleLabel; + public JFXRippler rippler; + + // The transition that the view represents + private SimpleObjectProperty transition = new SimpleObjectProperty<>(); + private SimpleObjectProperty title = new SimpleObjectProperty<>(); + + @Override + public void initialize(URL location, ResourceBundle resources) { + title.addListener(((observable, oldValue, newValue) -> { + titleLabel.setText(newValue); + })); + } + + public void setTitle(String title) { + this.title.set(title); + } + + public void setTransition(Transition transition) { + this.transition.set(transition); + } + + public Transition getTransition() { + return transition.get(); + } +} diff --git a/src/main/java/ecdar/controllers/TransitionPaneElementController.java b/src/main/java/ecdar/controllers/TransitionPaneElementController.java new file mode 100755 index 00000000..8a985a92 --- /dev/null +++ b/src/main/java/ecdar/controllers/TransitionPaneElementController.java @@ -0,0 +1,241 @@ +package ecdar.controllers; + +import com.jfoenix.controls.JFXRippler; +import com.jfoenix.controls.JFXTextField; +import ecdar.Ecdar; +import ecdar.abstractions.Edge; +import ecdar.simulation.Transition; +import ecdar.simulation.SimulationHandler; +import ecdar.presentations.TransitionPresentation; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.ListChangeListener; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Label; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import org.kordamp.ikonli.javafx.FontIcon; + +import java.math.BigDecimal; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; + +/** + * The controller class for the transition pane element that can be inserted into the simulator panes + */ +public class TransitionPaneElementController implements Initializable { + public VBox root; + public VBox transitionList; + public HBox toolbar; + public Label toolbarTitle; + public JFXRippler refreshRippler; + public JFXRippler expandTransition; + public FontIcon expandTransitionIcon; + public VBox delayChooser; + public JFXTextField delayTextField; + + private SimpleBooleanProperty isTransitionExpanded = new SimpleBooleanProperty(false); + private Map transitionPresentationMap = new HashMap<>(); + private SimpleObjectProperty delay = new SimpleObjectProperty<>(BigDecimal.ZERO); + + @Override + public void initialize(URL location, ResourceBundle resources) { + Ecdar.getSimulationHandler().availableTransitions.addListener((ListChangeListener) c -> { + while (c.next()) { + for (Transition trans : c.getAddedSubList()) insertTransition(trans); + + for (final Transition trans: c.getRemoved()) { + transitionList.getChildren().remove(transitionPresentationMap.get(trans)); + transitionPresentationMap.remove(trans); + } + } + }); + + initializeTransitionExpand(); + initializeDelayChooser(); + } + + /** + * Sets up listeners for the delay chooser. + * Listens for changes in text property and updates the textfield with a sanitized value (e.g. no letters in delay). + * Also listens for changes in focus, so there's always a value in the textfield, even if the user deleted the text. + * Adds tooltip for the textfield. + */ + private void initializeDelayChooser() { + delayTextField.textProperty().addListener(((observable, oldValue, newValue) -> { + delayTextChanged(oldValue, newValue); + })); + + delayTextField.focusedProperty().addListener((observable, oldValue, newValue) -> { + // If the textfield loses focus and the user didn't enter anything + // show the value 0.0 + if(!newValue && delay.get().equals(BigDecimal.ZERO)) { + delayTextField.setText("0.0"); + } + }); + + Tooltip.install(delayTextField, new Tooltip("Enter delay to use for next transition")); + } + + /** + * Initializes the expand functionality that allows the user to show or hide the transitions. + * By default the transitions are shown. + */ + private void initializeTransitionExpand() { + isTransitionExpanded.addListener((obs, oldVal, newVal) -> { + if(newVal) { + if(!root.getChildren().contains(delayChooser)) { + // Add the delay chooser just below the toolbar + root.getChildren().add(1, delayChooser); + } + showTransitions(); + expandTransitionIcon.setIconLiteral("gmi-expand-less"); + expandTransitionIcon.setIconSize(24); + } else { + root.getChildren().remove(delayChooser); + hideTransitions(); + expandTransitionIcon.setIconLiteral("gmi-expand-more"); + expandTransitionIcon.setIconSize(24); + } + }); + + isTransitionExpanded.set(true); + } + + /** + * Removes all the transition view elements as to hide the transitions from the user + */ + private void hideTransitions() { + transitionList.getChildren().clear(); + } + + /** + * Shows the available transitions by inserting a {@link TransitionPresentation} for each transition + */ + private void showTransitions() { + transitionPresentationMap.forEach((transition, presentation) -> { + insertTransition(transition); + }); + } + + /** + * Instantiates a TransitionPresentation for a Transition and adds it to the view + * @param transition The transition that should be inserted into the view + */ + private void insertTransition(Transition transition) { + final TransitionPresentation transitionPresentation = new TransitionPresentation(); + String title = transitionString(transition); + transitionPresentation.getController().setTitle(title); + transitionPresentation.getController().setTransition(transition); + + // Update the selected transition when mouse entered. + // Add the event to existing mouseEntered events + // e.g. TransitionPresentation already has mouseEntered functionality and we want to keep it + EventHandler mouseEntered = transitionPresentation.getOnMouseEntered(); + transitionPresentation.setOnMouseEntered(event -> { + SimulatorController.setSelectedTransition(transitionPresentation.getController().getTransition()); + mouseEntered.handle(event); + }); + + EventHandler mouseExited = transitionPresentation.getOnMouseExited(); + transitionPresentation.setOnMouseExited(event -> { + SimulatorController.setSelectedTransition(null); + mouseExited.handle(event); + }); + + transitionPresentation.setOnMouseClicked(event -> { + event.consume(); + + // Performs the next step of the simulation when clicking on a transition + SimulationHandler simHandler = Ecdar.getSimulationHandler(); + if (simHandler != null) { + simHandler.nextStep(transitionPresentation.getController().getTransition(), this.delay.get()); + } + }); + + transitionPresentationMap.put(transition, transitionPresentation); + + // Only insert the presentation into the view if the transitions are expanded + // Avoids inserting duplicate elements in the view (it's still added to the map) + if(isTransitionExpanded.get()) { + transitionList.getChildren().add(transitionPresentation); + } + } + + /** + * A helper method that returns a string representing a transition in the transition chooser + * @param transition The {@link Transition} to represent + * @return A string representing the transition + */ + private String transitionString(Transition transition) { + String title = transition.getLabel(); + if(transition.getEdges() != null) { + for (Edge edge : transition.getEdges()) { + title += " " + edge.getId(); + } + } + return title; + } + + /** + * Method to be called when clicking on the expand rippler in the transition toolbar + */ + @FXML + private void expandTransitions() { + if(isTransitionExpanded.get()) { + isTransitionExpanded.set(false); + } else { + isTransitionExpanded.set(true); + } + } + + /** + * Gets the initial step from the SimulationHandler. + * Used by the refresh button. + */ + @FXML + private void refreshTransitions() { + SimulatorController.setSelectedTransition(null); +// MainController.openReloadSimulationDialog(); // ToDo: Implement + } + + /** + * Sanitizes the input that the user inserts into the delay textfield. + * Checks if the text can be converted into a BigDecimal otherwise show the previous value. + * For example avoids users entering letters. + * @param oldValue The old value to show if the newvalue cannot be converted to a BigDecimal + * @param newValue The new value to show in the textfield + */ + @FXML + private void delayTextChanged(String oldValue, String newValue) { + // If the value is empty (the user deleted the value), assume that the value is 0.0 but do not update the text + if(newValue.isEmpty()) { + this.delay.set(BigDecimal.ZERO); + } else { + // Try to convert the new value into a BigDecimal + // Note that we don't setText here, as the new value is already shown in the textfield + try { + BigDecimal bd = new BigDecimal(newValue); + + // Checking the string for "-" instead of whether bd is negative is due to the case of -0.0 + // So checking the string is just simpler + if(newValue.contains("-")) { + throw new NumberFormatException(); + } + + this.delay.set(bd); + + } catch (NumberFormatException ex) { + // If the conversion was not possible, show the old value + this.delayTextField.setText(oldValue); + } + } + + } + +} diff --git a/src/main/java/ecdar/presentations/EcdarPresentation.java b/src/main/java/ecdar/presentations/EcdarPresentation.java index f8fb5f25..0648b67d 100644 --- a/src/main/java/ecdar/presentations/EcdarPresentation.java +++ b/src/main/java/ecdar/presentations/EcdarPresentation.java @@ -7,10 +7,6 @@ import ecdar.controllers.EcdarController; import ecdar.utility.UndoRedoStack; import ecdar.utility.colors.Color; -import ecdar.utility.colors.EnabledColor; -import ecdar.utility.helpers.SelectHelper; -import com.jfoenix.controls.JFXPopup; -import com.jfoenix.controls.JFXRippler; import com.jfoenix.controls.JFXSnackbar; import ecdar.utility.keyboard.Keybind; import ecdar.utility.keyboard.KeyboardTracker; @@ -22,89 +18,65 @@ import javafx.beans.property.SimpleDoubleProperty; import javafx.collections.ListChangeListener; import javafx.geometry.Insets; -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; import javafx.scene.layout.*; -import javafx.scene.shape.Circle; import javafx.util.Duration; -import javafx.util.Pair; - -import java.util.ArrayList; -import java.util.List; - -import static ecdar.utility.colors.EnabledColor.enabledColors; public class EcdarPresentation extends StackPane { private final EcdarController controller; - private final BooleanProperty filePaneOpen = new SimpleBooleanProperty(false); - private final SimpleDoubleProperty filePaneAnimationProperty = new SimpleDoubleProperty(0); - private final BooleanProperty queryPaneOpen = new SimpleBooleanProperty(false); - private final SimpleDoubleProperty queryPaneAnimationProperty = new SimpleDoubleProperty(0); - private Timeline openQueryPaneAnimation; - private Timeline closeQueryPaneAnimation; - private Timeline openFilePaneAnimation; - private Timeline closeFilePaneAnimation; + private final BooleanProperty leftPaneOpen = new SimpleBooleanProperty(false); + private final SimpleDoubleProperty leftPaneAnimationProperty = new SimpleDoubleProperty(0); + private final BooleanProperty rightPaneOpen = new SimpleBooleanProperty(false); + private final SimpleDoubleProperty rightPaneAnimationProperty = new SimpleDoubleProperty(0); + private Timeline openLeftPaneAnimation; + private Timeline closeLeftPaneAnimation; + private Timeline openRightPaneAnimation; + private Timeline closeRightPaneAnimation; public EcdarPresentation() { controller = new EcdarFXMLLoader().loadAndGetController("EcdarPresentation.fxml", this); initializeTopBar(); - initializeToolbar(); initializeQueryDetailsDialog(); - initializeColorSelector(); - - initializeToggleQueryPaneFunctionality(); - initializeToggleFilePaneFunctionality(); - - initializeSelectDependentToolbarButton(controller.colorSelected); - Tooltip.install(controller.colorSelected, new Tooltip("Colour")); - - initializeSelectDependentToolbarButton(controller.deleteSelected); - Tooltip.install(controller.deleteSelected, new Tooltip("Delete")); - - initializeToolbarButton(controller.undo); - initializeToolbarButton(controller.redo); - initializeUndoRedoButtons(); + initializeToggleLeftPaneFunctionality(); + initializeToggleRightPaneFunctionality(); initializeSnackbar(); - // Open the file and query panel initially + // Open the left and right panes initially Platform.runLater(() -> { // Bind sizing of sides and center panes to ensure correct sizing - controller.canvasPane.minWidthProperty().bind(controller.root.widthProperty().subtract(filePaneAnimationProperty.add(queryPaneAnimationProperty))); - controller.canvasPane.maxWidthProperty().bind(controller.root.widthProperty().subtract(filePaneAnimationProperty.add(queryPaneAnimationProperty))); + controller.getEditorPresentation().getController().canvasPane.minWidthProperty().bind(controller.root.widthProperty().subtract(leftPaneAnimationProperty.add(rightPaneAnimationProperty))); + controller.getEditorPresentation().getController().canvasPane.maxWidthProperty().bind(controller.root.widthProperty().subtract(leftPaneAnimationProperty.add(rightPaneAnimationProperty))); // Bind the height to ensure that both the top and bottom panes are shown // The height of the top pane is multiplied by 4 as the UI does not account for the height otherwise - controller.canvasPane.minHeightProperty().bind(controller.root.heightProperty().subtract(controller.topPane.heightProperty().multiply(4).add(controller.bottomFillerElement.heightProperty()))); - controller.canvasPane.maxHeightProperty().bind(controller.root.heightProperty().subtract(controller.topPane.heightProperty().multiply(4).add(controller.bottomFillerElement.heightProperty()))); + controller.getEditorPresentation().getController().canvasPane.minHeightProperty().bind(controller.root.heightProperty().subtract(controller.topPane.heightProperty().multiply(4).add(controller.bottomFillerElement.heightProperty()))); + controller.getEditorPresentation().getController().canvasPane.maxHeightProperty().bind(controller.root.heightProperty().subtract(controller.topPane.heightProperty().multiply(4).add(controller.bottomFillerElement.heightProperty()))); - controller.leftPane.minWidthProperty().bind(filePaneAnimationProperty); - controller.leftPane.maxWidthProperty().bind(filePaneAnimationProperty); + controller.leftPane.minWidthProperty().bind(leftPaneAnimationProperty); + controller.leftPane.maxWidthProperty().bind(leftPaneAnimationProperty); - controller.rightPane.minWidthProperty().bind(queryPaneAnimationProperty); - controller.rightPane.maxWidthProperty().bind(queryPaneAnimationProperty); + controller.rightPane.minWidthProperty().bind(rightPaneAnimationProperty); + controller.rightPane.maxWidthProperty().bind(rightPaneAnimationProperty); controller.topPane.minHeightProperty().bind(controller.menuBar.heightProperty()); controller.topPane.maxHeightProperty().bind(controller.menuBar.heightProperty()); - Platform.runLater(() -> { - toggleFilePane(); - toggleQueryPane(); - }); + toggleLeftPane(); + toggleRightPane(); Ecdar.getPresentation().controller.scalingProperty.addListener((observable, oldValue, newValue) -> { // If the scaling has changed trigger animations for open panes to update width Platform.runLater(() -> { - if (filePaneOpen.get()) { - openFilePaneAnimation.play(); + if (leftPaneOpen.get()) { + openLeftPaneAnimation.play(); } - if (queryPaneOpen.get()) { - openQueryPaneAnimation.play(); + if (rightPaneOpen.get()) { + openRightPaneAnimation.play(); } }); @@ -130,147 +102,6 @@ private void initializeSnackbar() { controller.snackbar.autosize(); } - private void initializeUndoRedoButtons() { - UndoRedoStack.canUndoProperty().addListener((obs, oldState, newState) -> { - if (newState) { - // Enable the undo button - controller.undo.setEnabled(true); - controller.undo.setOpacity(1); - } else { - // Disable the undo button - controller.undo.setEnabled(false); - controller.undo.setOpacity(0.3); - } - }); - - UndoRedoStack.canRedoProperty().addListener((obs, oldState, newState) -> { - if (newState) { - // Enable the redo button - controller.redo.setEnabled(true); - controller.redo.setOpacity(1); - } else { - // Disable the redo button - controller.redo.setEnabled(false); - controller.redo.setOpacity(0.3); - } - }); - - // Disable the undo button - controller.undo.setEnabled(false); - controller.undo.setOpacity(0.3); - - // Disable the redo button - controller.redo.setEnabled(false); - controller.redo.setOpacity(0.3); - - // Set tooltips - Tooltip.install(controller.undo, new Tooltip("Undo")); - Tooltip.install(controller.redo, new Tooltip("Redo")); - } - - private void initializeColorSelector() { - final JFXPopup popup = new JFXPopup(); - - final double listWidth = 136; - final FlowPane list = new FlowPane(); - for (final EnabledColor color : enabledColors) { - final Circle circle = new Circle(16, color.color.getColor(color.intensity)); - circle.setStroke(color.color.getColor(color.intensity.next(2))); - circle.setStrokeWidth(1); - - final Label label = new Label(color.keyCode.getName()); - label.getStyleClass().add("subhead"); - label.setTextFill(color.color.getTextColor(color.intensity)); - - final StackPane child = new StackPane(circle, label); - child.setMinSize(40, 40); - child.setMaxSize(40, 40); - - child.setOnMouseEntered(event -> { - final ScaleTransition scaleTransition = new ScaleTransition(Duration.millis(100), circle); - scaleTransition.setFromX(circle.getScaleX()); - scaleTransition.setFromY(circle.getScaleY()); - scaleTransition.setToX(1.1); - scaleTransition.setToY(1.1); - scaleTransition.play(); - }); - - child.setOnMouseExited(event -> { - final ScaleTransition scaleTransition = new ScaleTransition(Duration.millis(100), circle); - scaleTransition.setFromX(circle.getScaleX()); - scaleTransition.setFromY(circle.getScaleY()); - scaleTransition.setToX(1.0); - scaleTransition.setToY(1.0); - scaleTransition.play(); - }); - - child.setOnMouseClicked(event -> { - final List> previousColor = new ArrayList<>(); - - SelectHelper.getSelectedElements().forEach(selectable -> { - previousColor.add(new Pair<>(selectable, new EnabledColor(selectable.getColor(), selectable.getColorIntensity()))); - }); - - controller.changeColorOnSelectedElements(color, previousColor); - - popup.hide(); - SelectHelper.clearSelectedElements(); - }); - - list.getChildren().add(child); - } - list.setMinWidth(listWidth); - list.setMaxWidth(listWidth); - list.setStyle("-fx-background-color: white; -fx-padding: 8;"); - - popup.setPopupContent(list); - - controller.colorSelected.setOnMouseClicked((e) -> { - // If nothing is selected - if (SelectHelper.getSelectedElements().size() == 0) return; - popup.show(controller.colorSelected, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, -10, 15); - }); - } - - private void initializeSelectDependentToolbarButton(final JFXRippler button) { - initializeToolbarButton(button); - - // The color button should only be enabled when an element is selected - SelectHelper.getSelectedElements().addListener(new ListChangeListener() { - @Override - public void onChanged(final Change c) { - if (SelectHelper.getSelectedElements().size() > 0) { - button.setEnabled(true); - - final FadeTransition fadeAnimation = new FadeTransition(Duration.millis(100), button); - fadeAnimation.setFromValue(button.getOpacity()); - fadeAnimation.setToValue(1); - fadeAnimation.play(); - } else { - button.setEnabled(false); - - final FadeTransition fadeAnimation = new FadeTransition(Duration.millis(100), button); - fadeAnimation.setFromValue(1); - fadeAnimation.setToValue(0.3); - fadeAnimation.play(); - } - } - }); - - // Disable the button - button.setEnabled(false); - button.setOpacity(0.3); - } - - private void initializeToolbarButton(final JFXRippler button) { - final Color color = Color.GREY_BLUE; - final Color.Intensity colorIntensity = Color.Intensity.I800; - - button.setMaskType(JFXRippler.RipplerMask.CIRCLE); - button.setRipplerFill(color.getTextColor(colorIntensity)); - button.setPosition(JFXRippler.RipplerPos.BACK); - } - private void initializeQueryDetailsDialog() { final Color modalBarColor = Color.GREY_BLUE; final Color.Intensity modalBarColorIntensity = Color.Intensity.I500; @@ -283,106 +114,114 @@ private void initializeQueryDetailsDialog() { ))); } - private void initializeToggleFilePaneFunctionality() { - initializeOpenFilePaneAnimation(); - initializeCloseFilePaneAnimation(); + private void initializeToggleLeftPaneFunctionality() { + initializeOpenLeftPaneAnimation(); + initializeCloseLeftPaneAnimation(); // Translate the x coordinate to create the open/close animations - controller.filePane.translateXProperty().bind(filePaneAnimationProperty.subtract(controller.filePane.widthProperty())); + controller.projectPane.translateXProperty().bind(leftPaneAnimationProperty.subtract(controller.projectPane.widthProperty())); + controller.leftSimPane.translateXProperty().bind(leftPaneAnimationProperty.subtract(controller.leftSimPane.widthProperty())); // Whenever the width of the file pane is updated, update the animations - controller.filePane.widthProperty().addListener((observable) -> { - initializeOpenFilePaneAnimation(); - initializeCloseFilePaneAnimation(); + controller.projectPane.widthProperty().addListener((observable) -> { + initializeOpenLeftPaneAnimation(); + initializeCloseLeftPaneAnimation(); + }); + + // Whenever the width of the file pane is updated, update the animations + controller.leftPane.widthProperty().addListener((observable) -> { + initializeOpenLeftPaneAnimation(); + initializeCloseLeftPaneAnimation(); }); } - private void initializeCloseFilePaneAnimation() { + private void initializeCloseLeftPaneAnimation() { final Interpolator interpolator = Interpolator.SPLINE(0.645, 0.045, 0.355, 1); - closeFilePaneAnimation = new Timeline(); + closeLeftPaneAnimation = new Timeline(); - final KeyValue open = new KeyValue(filePaneAnimationProperty, controller.filePane.getWidth(), interpolator); - final KeyValue closed = new KeyValue(filePaneAnimationProperty, 0, interpolator); + final KeyValue open = new KeyValue(leftPaneAnimationProperty, controller.projectPane.getWidth(), interpolator); + final KeyValue closed = new KeyValue(leftPaneAnimationProperty, 0, interpolator); final KeyFrame kf1 = new KeyFrame(Duration.millis(0), open); final KeyFrame kf2 = new KeyFrame(Duration.millis(200), closed); - closeFilePaneAnimation.getKeyFrames().addAll(kf1, kf2); + closeLeftPaneAnimation.getKeyFrames().addAll(kf1, kf2); } - private void initializeOpenFilePaneAnimation() { + private void initializeOpenLeftPaneAnimation() { final Interpolator interpolator = Interpolator.SPLINE(0.645, 0.045, 0.355, 1); - openFilePaneAnimation = new Timeline(); + openLeftPaneAnimation = new Timeline(); - final KeyValue closed = new KeyValue(filePaneAnimationProperty, 0, interpolator); - final KeyValue open = new KeyValue(filePaneAnimationProperty, controller.filePane.getWidth(), interpolator); + final KeyValue closed = new KeyValue(leftPaneAnimationProperty, 0, interpolator); + final KeyValue open = new KeyValue(leftPaneAnimationProperty, controller.projectPane.getWidth(), interpolator); final KeyFrame kf1 = new KeyFrame(Duration.millis(0), closed); final KeyFrame kf2 = new KeyFrame(Duration.millis(200), open); - openFilePaneAnimation.getKeyFrames().addAll(kf1, kf2); + openLeftPaneAnimation.getKeyFrames().addAll(kf1, kf2); } - private void initializeToggleQueryPaneFunctionality() { - initializeOpenQueryPaneAnimation(); - initializeCloseQueryPaneAnimation(); + private void initializeToggleRightPaneFunctionality() { + initializeOpenRightPaneAnimation(); + initializeCloseRightPaneAnimation(); // Translate the x coordinate to create the open/close animations - controller.queryPane.translateXProperty().bind(queryPaneAnimationProperty.multiply(-1).add(controller.queryPane.widthProperty())); + controller.queryPane.translateXProperty().bind(rightPaneAnimationProperty.multiply(-1).add(controller.queryPane.widthProperty())); + controller.rightSimPane.translateXProperty().bind(rightPaneAnimationProperty.multiply(-1).add(controller.rightSimPane.widthProperty())); // Whenever the width of the query pane is updated, update the animations controller.queryPane.widthProperty().addListener((observable) -> { - initializeOpenQueryPaneAnimation(); - initializeCloseQueryPaneAnimation(); + initializeOpenRightPaneAnimation(); + initializeCloseRightPaneAnimation(); }); // When new queries are added, make sure that the query pane is open Ecdar.getProject().getQueries().addListener((ListChangeListener) c -> { - if (closeQueryPaneAnimation == null) + if (closeRightPaneAnimation == null) return; // The query pane is not yet initialized while (c.next()) { c.getAddedSubList().forEach(o -> { - if (!queryPaneOpen.get()) { + if (!rightPaneOpen.get()) { // Open the pane - openQueryPaneAnimation.play(); + openRightPaneAnimation.play(); // Toggle the open state - queryPaneOpen.set(queryPaneOpen.not().get()); + rightPaneOpen.set(rightPaneOpen.not().get()); } }); } }); } - private void initializeCloseQueryPaneAnimation() { + private void initializeCloseRightPaneAnimation() { final Interpolator interpolator = Interpolator.SPLINE(0.645, 0.045, 0.355, 1); - closeQueryPaneAnimation = new Timeline(); + closeRightPaneAnimation = new Timeline(); - final KeyValue open = new KeyValue(queryPaneAnimationProperty, controller.queryPane.getWidth(), interpolator); - final KeyValue closed = new KeyValue(queryPaneAnimationProperty, 0, interpolator); + final KeyValue open = new KeyValue(rightPaneAnimationProperty, controller.queryPane.getWidth(), interpolator); + final KeyValue closed = new KeyValue(rightPaneAnimationProperty, 0, interpolator); final KeyFrame kf1 = new KeyFrame(Duration.millis(0), open); final KeyFrame kf2 = new KeyFrame(Duration.millis(200), closed); - closeQueryPaneAnimation.getKeyFrames().addAll(kf1, kf2); + closeRightPaneAnimation.getKeyFrames().addAll(kf1, kf2); } - private void initializeOpenQueryPaneAnimation() { + private void initializeOpenRightPaneAnimation() { final Interpolator interpolator = Interpolator.SPLINE(0.645, 0.045, 0.355, 1); - openQueryPaneAnimation = new Timeline(); + openRightPaneAnimation = new Timeline(); - final KeyValue closed = new KeyValue(queryPaneAnimationProperty, 0, interpolator); - final KeyValue open = new KeyValue(queryPaneAnimationProperty, controller.queryPane.getWidth(), interpolator); + final KeyValue closed = new KeyValue(rightPaneAnimationProperty, 0, interpolator); + final KeyValue open = new KeyValue(rightPaneAnimationProperty, controller.queryPane.getWidth(), interpolator); final KeyFrame kf1 = new KeyFrame(Duration.millis(0), closed); final KeyFrame kf2 = new KeyFrame(Duration.millis(200), open); - openQueryPaneAnimation.getKeyFrames().addAll(kf1, kf2); + openRightPaneAnimation.getKeyFrames().addAll(kf1, kf2); } private void initializeTopBar() { @@ -405,18 +244,6 @@ private void initializeTopBar() { ))); } - private void initializeToolbar() { - final Color color = Color.GREY_BLUE; - final Color.Intensity intensity = Color.Intensity.I700; - - // Set the background for the top toolbar - controller.toolbar.setBackground( - new Background(new BackgroundFill(color.getColor(intensity), - CornerRadii.EMPTY, - Insets.EMPTY) - )); - } - /** * Initialize help image views. */ @@ -451,37 +278,36 @@ private void initializeResizeQueryPane() { // Set bounds for resizing to be between 280px and half the screen width final double newWidth = Math.min(Math.max(prevWidth.get() + diff, 280), controller.root.getWidth() / 2); - queryPaneAnimationProperty.set(newWidth); + rightPaneAnimationProperty.set(newWidth); controller.queryPane.setMaxWidth(newWidth); controller.queryPane.setMinWidth(newWidth); }); } - - public BooleanProperty toggleFilePane() { - if (filePaneOpen.get()) { - closeFilePaneAnimation.play(); + public BooleanProperty toggleLeftPane() { + if (leftPaneOpen.get()) { + closeLeftPaneAnimation.play(); } else { - openFilePaneAnimation.play(); + openLeftPaneAnimation.play(); } // Toggle the open state - filePaneOpen.set(filePaneOpen.not().get()); + leftPaneOpen.set(leftPaneOpen.not().get()); - return filePaneOpen; + return leftPaneOpen; } - public BooleanProperty toggleQueryPane() { - if (queryPaneOpen.get()) { - closeQueryPaneAnimation.play(); + public BooleanProperty toggleRightPane() { + if (rightPaneOpen.get()) { + closeRightPaneAnimation.play(); } else { - openQueryPaneAnimation.play(); + openRightPaneAnimation.play(); } // Toggle the open state - queryPaneOpen.set(queryPaneOpen.not().get()); + rightPaneOpen.set(rightPaneOpen.not().get()); - return queryPaneOpen; + return rightPaneOpen; } public static void fitSizeWhenAvailable(final ImageView imageView, final StackPane pane) { @@ -506,8 +332,8 @@ public void showSnackbarMessage(final String message) { } public void showHelp() { - controller.dialogContainer.setVisible(true); - controller.dialog.show(controller.dialogContainer); + controller.modellingHelpDialogContainer.setVisible(true); + controller.modellingHelpDialog.show(controller.modellingHelpDialogContainer); } public EcdarController getController() { diff --git a/src/main/java/ecdar/presentations/EditorPresentation.java b/src/main/java/ecdar/presentations/EditorPresentation.java new file mode 100644 index 00000000..d17a4ab0 --- /dev/null +++ b/src/main/java/ecdar/presentations/EditorPresentation.java @@ -0,0 +1,201 @@ +package ecdar.presentations; + +import com.jfoenix.controls.JFXPopup; +import com.jfoenix.controls.JFXRippler; +import ecdar.controllers.EditorController; +import ecdar.utility.UndoRedoStack; +import ecdar.utility.colors.Color; +import ecdar.utility.colors.EnabledColor; +import ecdar.utility.helpers.SelectHelper; +import javafx.animation.FadeTransition; +import javafx.animation.ScaleTransition; +import javafx.collections.ListChangeListener; +import javafx.geometry.Insets; +import javafx.scene.control.Label; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.*; +import javafx.scene.shape.Circle; +import javafx.util.Duration; +import javafx.util.Pair; + +import java.util.ArrayList; +import java.util.List; + +import static ecdar.utility.colors.EnabledColor.enabledColors; + +public class EditorPresentation extends VBox { + private final EditorController controller; + + public EditorPresentation() { + this.controller = new EcdarFXMLLoader().loadAndGetController("EditorPresentation.fxml", this); + initializeToolbar(); + initializeColorSelector(); + + initializeSelectDependentToolbarButton(controller.colorSelected); + Tooltip.install(controller.colorSelected, new Tooltip("Colour")); + + initializeSelectDependentToolbarButton(controller.deleteSelected); + Tooltip.install(controller.deleteSelected, new Tooltip("Delete")); + + initializeToolbarButton(controller.undo); + initializeToolbarButton(controller.redo); + initializeUndoRedoButtons(); + } + + public EditorController getController() { + return controller; + } + + private void initializeSelectDependentToolbarButton(final JFXRippler button) { + initializeToolbarButton(button); + + // The color button should only be enabled when an element is selected + SelectHelper.getSelectedElements().addListener(new ListChangeListener() { + @Override + public void onChanged(final Change c) { + if (SelectHelper.getSelectedElements().size() > 0) { + button.setEnabled(true); + + final FadeTransition fadeAnimation = new FadeTransition(Duration.millis(100), button); + fadeAnimation.setFromValue(button.getOpacity()); + fadeAnimation.setToValue(1); + fadeAnimation.play(); + } else { + button.setEnabled(false); + + final FadeTransition fadeAnimation = new FadeTransition(Duration.millis(100), button); + fadeAnimation.setFromValue(1); + fadeAnimation.setToValue(0.3); + fadeAnimation.play(); + } + } + }); + + // Disable the button + button.setEnabled(false); + button.setOpacity(0.3); + } + + private void initializeToolbarButton(final JFXRippler button) { + final Color color = Color.GREY_BLUE; + final Color.Intensity colorIntensity = Color.Intensity.I800; + + button.setMaskType(JFXRippler.RipplerMask.CIRCLE); + button.setRipplerFill(color.getTextColor(colorIntensity)); + button.setPosition(JFXRippler.RipplerPos.BACK); + } + + private void initializeUndoRedoButtons() { + UndoRedoStack.canUndoProperty().addListener((obs, oldState, newState) -> { + if (newState) { + // Enable the undo button + controller.undo.setEnabled(true); + controller.undo.setOpacity(1); + } else { + // Disable the undo button + controller.undo.setEnabled(false); + controller.undo.setOpacity(0.3); + } + }); + + UndoRedoStack.canRedoProperty().addListener((obs, oldState, newState) -> { + if (newState) { + // Enable the redo button + controller.redo.setEnabled(true); + controller.redo.setOpacity(1); + } else { + // Disable the redo button + controller.redo.setEnabled(false); + controller.redo.setOpacity(0.3); + } + }); + + // Disable the undo button + controller.undo.setEnabled(false); + controller.undo.setOpacity(0.3); + + // Disable the redo button + controller.redo.setEnabled(false); + controller.redo.setOpacity(0.3); + + // Set tooltips + Tooltip.install(controller.undo, new Tooltip("Undo")); + Tooltip.install(controller.redo, new Tooltip("Redo")); + } + + private void initializeColorSelector() { + final JFXPopup popup = new JFXPopup(); + + final double listWidth = 136; + final FlowPane list = new FlowPane(); + for (final EnabledColor color : enabledColors) { + final Circle circle = new Circle(16, color.color.getColor(color.intensity)); + circle.setStroke(color.color.getColor(color.intensity.next(2))); + circle.setStrokeWidth(1); + + final Label label = new Label(color.keyCode.getName()); + label.getStyleClass().add("subhead"); + label.setTextFill(color.color.getTextColor(color.intensity)); + + final StackPane child = new StackPane(circle, label); + child.setMinSize(40, 40); + child.setMaxSize(40, 40); + + child.setOnMouseEntered(event -> { + final ScaleTransition scaleTransition = new ScaleTransition(Duration.millis(100), circle); + scaleTransition.setFromX(circle.getScaleX()); + scaleTransition.setFromY(circle.getScaleY()); + scaleTransition.setToX(1.1); + scaleTransition.setToY(1.1); + scaleTransition.play(); + }); + + child.setOnMouseExited(event -> { + final ScaleTransition scaleTransition = new ScaleTransition(Duration.millis(100), circle); + scaleTransition.setFromX(circle.getScaleX()); + scaleTransition.setFromY(circle.getScaleY()); + scaleTransition.setToX(1.0); + scaleTransition.setToY(1.0); + scaleTransition.play(); + }); + + child.setOnMouseClicked(event -> { + final List> previousColor = new ArrayList<>(); + + SelectHelper.getSelectedElements().forEach(selectable -> { + previousColor.add(new Pair<>(selectable, new EnabledColor(selectable.getColor(), selectable.getColorIntensity()))); + }); + + controller.changeColorOnSelectedElements(color, previousColor); + + popup.hide(); + SelectHelper.clearSelectedElements(); + }); + + list.getChildren().add(child); + } + list.setMinWidth(listWidth); + list.setMaxWidth(listWidth); + list.setStyle("-fx-background-color: white; -fx-padding: 8;"); + + popup.setPopupContent(list); + + controller.colorSelected.setOnMouseClicked((e) -> { + // If nothing is selected + if (SelectHelper.getSelectedElements().size() == 0) return; + popup.show(controller.colorSelected, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, -10, 15); + }); + } + + private void initializeToolbar() { + final Color color = Color.GREY_BLUE; + final Color.Intensity intensity = Color.Intensity.I700; + + // Set the background for the top toolbar + controller.toolbar.setBackground( + new Background(new BackgroundFill(color.getColor(intensity), + CornerRadii.EMPTY, + Insets.EMPTY) + )); + } +} diff --git a/src/main/java/ecdar/presentations/FilePresentation.java b/src/main/java/ecdar/presentations/FilePresentation.java index 5f1d1251..700b61b8 100644 --- a/src/main/java/ecdar/presentations/FilePresentation.java +++ b/src/main/java/ecdar/presentations/FilePresentation.java @@ -132,7 +132,7 @@ private void initializeColors() { private ArrayList getActiveComponents() { ArrayList activeComponents = new ArrayList<>(); - Node canvasPaneFirstChild = Ecdar.getPresentation().getController().canvasPane.getChildren().get(0); + Node canvasPaneFirstChild = Ecdar.getPresentation().getController().getEditorPresentation().getController().canvasPane.getChildren().get(0); if(canvasPaneFirstChild instanceof GridPane) { for (Node child : ((GridPane) canvasPaneFirstChild).getChildren()) { activeComponents.add(((CanvasPresentation) child).getController().getActiveModel()); diff --git a/src/main/java/ecdar/presentations/LeftSimPanePresentation.java b/src/main/java/ecdar/presentations/LeftSimPanePresentation.java new file mode 100755 index 00000000..adc693ed --- /dev/null +++ b/src/main/java/ecdar/presentations/LeftSimPanePresentation.java @@ -0,0 +1,37 @@ +package ecdar.presentations; + +import ecdar.controllers.LeftSimPaneController; +import ecdar.utility.colors.Color; +import javafx.geometry.Insets; +import javafx.scene.layout.*; + +public class LeftSimPanePresentation extends StackPane { + private LeftSimPaneController controller; + + public LeftSimPanePresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("LeftSimPanePresentation.fxml", this); + + initializeBackground(); + initializeRightBorder(); + } + + private void initializeBackground() { + controller.scrollPaneVbox.setBackground(new Background(new BackgroundFill( + Color.GREY.getColor(Color.Intensity.I200), + CornerRadii.EMPTY, + Insets.EMPTY + ))); + } + + /** + * Initializes the thin border on the right side of the transition toolbar + */ + private void initializeRightBorder() { + controller.transitionPanePresentation.getController().toolbar.setBorder(new Border(new BorderStroke( + Color.GREY_BLUE.getColor(Color.Intensity.I900), + BorderStrokeStyle.SOLID, + CornerRadii.EMPTY, + new BorderWidths(0, 1, 0, 0) + ))); + } +} diff --git a/src/main/java/ecdar/presentations/ProcessPresentation.java b/src/main/java/ecdar/presentations/ProcessPresentation.java new file mode 100755 index 00000000..51cebab0 --- /dev/null +++ b/src/main/java/ecdar/presentations/ProcessPresentation.java @@ -0,0 +1,282 @@ +package ecdar.presentations; + +import ecdar.abstractions.Component; +import ecdar.abstractions.Edge; +import ecdar.abstractions.Location; +import ecdar.abstractions.Nail; +import ecdar.controllers.ModelController; +import ecdar.controllers.ProcessController; +import ecdar.utility.colors.Color; +import javafx.beans.InvalidationListener; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.layout.*; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Path; +import javafx.scene.shape.Rectangle; +import javafx.scene.shape.Shape; +import javafx.scene.text.Font; + +import java.math.BigDecimal; +import java.util.List; +import java.util.function.BiConsumer; + +import static ecdar.presentations.Grid.GRID_SIZE; + +/** + * The presenter of a Process which is shown in {@link SimulatorOverviewPresentation}.
+ * This class have some of the same functionality as {@link ComponentPresentation} and could be refactored + * into a base class. + */ +public class ProcessPresentation extends ModelPresentation { + private final ProcessController controller; + + /** + * Constructs a Process ready to go to the view. + * @param component the component which the process should look like + */ + public ProcessPresentation(final Component component){ + controller = new EcdarFXMLLoader().loadAndGetController("ProcessPresentation.fxml", this); + controller.setComponent(component); + super.initialize(component.getBox()); + // Initialize methods that is sensitive to width and height + final Runnable onUpdateSize = () -> { + initializeToolbar(); + initializeFrame(); + initializeBackground(); + }; + + onUpdateSize.run(); + + // Re run initialisation on update of width and height property + component.getBox().getWidthProperty().addListener(observable -> onUpdateSize.run()); + component.getBox().getHeightProperty().addListener(observable -> onUpdateSize.run()); + setValueAreaStyle(); + setToggleValueButtonStyle(); + + controller.getClocks().forEach(this::addValueToValueArea); + controller.getVariables().forEach(this::addValueToValueArea); + + controller.getClocks().addListener((InvalidationListener) obs -> { + controller.getClocks().forEach((s, bigDecimal) -> { + final List filteredList = controller.valueArea.getChildren().filtered(node -> { + if (!(node instanceof Label))// we currently only want to look at labels + return false; + final String[] splitString = ((Label) node).getText().split("="); + return splitString[0].trim().equals(s); + }); + controller.valueArea.getChildren().removeAll(filteredList); + addValueToValueArea(s, bigDecimal); + }); + }); + controller.getVariables().addListener((InvalidationListener) obs -> { + controller.getVariables().forEach((s, bigDecimal) -> { + final List filteredList = controller.valueArea.getChildren().filtered(node -> { + if (!(node instanceof Label))// we currently only want to look at labels + return false; + final String[] splitString = ((Label) node).getText().split("="); + return splitString[0].trim().equals(s); + }); + controller.valueArea.getChildren().removeAll(filteredList); + addValueToValueArea(s, bigDecimal); + }); + }); + } + + /** + * Sets the Icon and Icon size of the {@link ProcessController#toggleValueButtonIcon} + */ + private void setToggleValueButtonStyle() { + controller.toggleValueButtonIcon.setIconLiteral("gmi-code"); + controller.toggleValueButtonIcon.setIconSize(17); + } + + /** + * Set the style needed for the {@link ProcessController#valueArea}. + * This include padding and background. + */ + private void setValueAreaStyle() { + // As for some reason the styling fail to be applied we need to set the styling again here + controller.valueArea.setPadding(new Insets(16, 4, 16, 4)); + controller.valueArea.setBackground(new Background( + new BackgroundFill(Paint.valueOf("rgba(242, 243, 244, 0.85)"),CornerRadii.EMPTY, Insets.EMPTY))); + } + + private void addValueToValueArea(final String s, final BigDecimal bigDecimal) { + final Label valueLabel = new Label(); + valueLabel.setText(s + " = " + bigDecimal); + // As the styling sometimes are gone missing + valueLabel.setFont(Font.font("Roboto Mono Medium",13)); + controller.valueArea.getChildren().add(valueLabel); + } + + /** + * Initializes the frame around the body of the process. + * It also updates the color if the color is changed + */ + private void initializeFrame() { + final Component component = controller.getComponent(); + + final Shape[] mask = new Shape[1]; + final Rectangle rectangle = new Rectangle(component.getBox().getWidth(), component.getBox().getHeight()); + + final BiConsumer updateColor = (newColor, newIntensity) -> { + // Mask the parent of the frame (will also mask the background) + mask[0] = Path.subtract(rectangle, TOP_LEFT_CORNER); + controller.frame.setClip(mask[0]); + controller.background.setClip(Path.union(mask[0], mask[0])); + controller.background.setOpacity(0.5); + + // Bind the missing lines that we cropped away + controller.topLeftLine.setStartX(Grid.CORNER_SIZE); + controller.topLeftLine.setStartY(0); + controller.topLeftLine.setEndX(0); + controller.topLeftLine.setEndY(Grid.CORNER_SIZE); + controller.topLeftLine.setStroke(newColor.getColor(newIntensity.next(2))); + controller.topLeftLine.setStrokeWidth(1.25); + StackPane.setAlignment(controller.topLeftLine, Pos.TOP_LEFT); + + // Set the stroke color to two shades darker + controller.frame.setBorder(new Border(new BorderStroke( + newColor.getColor(newIntensity.next(2)), + BorderStrokeStyle.SOLID, + CornerRadii.EMPTY, + new BorderWidths(1), + Insets.EMPTY + ))); + }; + component.colorProperty().addListener(observable -> { + updateColor.accept(component.getColor(), component.getColorIntensity()); + }); + updateColor.accept(component.getColor(), component.getColorIntensity()); + } + + /** + * Initializes the background of the process with the right color + * If the color is changed it will also update it self + */ + private void initializeBackground() { + final Component component = controller.getComponent(); + + // Bind the background width and height to the values in the model + controller.background.widthProperty().bind(component.getBox().getWidthProperty()); + controller.background.heightProperty().bind(component.getBox().getHeightProperty()); + controller.background.setFill(component.getColor().getColor(component.getColorIntensity().next(-10).next(2))); + final BiConsumer updateColor = (newColor, newIntensity) -> { + // Set the background color to the lightest possible version of the color + controller.background.setFill(newColor.getColor(newIntensity.next(-10).next(2))); + }; + component.colorProperty().addListener(observable -> { + updateColor.accept(component.getColor(), component.getColorIntensity()); + }); + + updateColor.accept(component.getColor(), component.getColorIntensity()); + } + + /** + * Initialize the Toolbar of the process.
+ * The toolbar is where the {@link ProcessController#name} is placed. + */ + private void initializeToolbar() { + final Component component = controller.getComponent(); + + final BiConsumer updateColor = (newColor, newIntensity) -> { + // Set the background of the toolbar + controller.toolbar.setBackground(new Background(new BackgroundFill( + newColor.getColor(newIntensity), + CornerRadii.EMPTY, + Insets.EMPTY + ))); + + // Set the icon color and rippler color of the toggleDeclarationButton + controller.toggleValuesButton.setRipplerFill(newColor.getTextColor(newIntensity)); + + controller.toolbar.setPrefHeight(Grid.TOOL_BAR_HEIGHT); + controller.toggleValuesButton.setBackground(Background.EMPTY); + }; + controller.getComponent().colorProperty().addListener(observable -> updateColor.accept(component.getColor(), component.getColorIntensity())); + + updateColor.accept(component.getColor(), component.getColorIntensity()); + + // Set a hover effect for the controller.toggleDeclarationButton + controller.toggleValuesButton.setOnMouseEntered(event -> controller.toggleValuesButton.setCursor(Cursor.HAND)); + controller.toggleValuesButton.setOnMouseExited(event -> controller.toggleValuesButton.setCursor(Cursor.DEFAULT)); + } + + /** + * Fades the process. + * Used if it is not involved in a transition + */ + public void showInactive() { + setOpacity(0.5); + } + + /** + * Show the process as active. + * Used to reset the effect of {@link ProcessPresentation#showInactive()} + */ + public void showActive() { + setOpacity(1.0); + } + + @Override + ModelController getModelController() { + return controller; + } + + /** + * Gets the minimum possible width when dragging the anchor. + * The width is based on the x coordinate of locations, nails and the signature arrows.
+ * This should be removed from {@link ModelPresentation} and made into an interface of its own + * @return the minimum possible width. + */ + @Override + @Deprecated + double getDragAnchorMinWidth() { + final Component component = controller.getComponent(); + double minWidth = 10 * GRID_SIZE; + + for (final Location location : component.getLocations()) { + minWidth = Math.max(minWidth, location.getX() + GRID_SIZE * 2); + } + + for (final Edge edge : component.getEdges()) { + for (final Nail nail : edge.getNails()) { + minWidth = Math.max(minWidth, nail.getX() + GRID_SIZE); + } + } + return minWidth; + } + + /** + * Gets the minimum possible height when dragging the anchor. + * The height is based on the y coordinate of locations, nails and the signature arrows
+ * This should be removed from {@link ModelPresentation} and made into an interface of its own + * @return the minimum possible height. + */ + @Override + @Deprecated + double getDragAnchorMinHeight() { + final Component component = controller.getComponent(); + double minHeight = 10 * GRID_SIZE; + + for (final Location location : component.getLocations()) { + minHeight = Math.max(minHeight, location.getY() + GRID_SIZE * 2); + } + + for (final Edge edge : component.getEdges()) { + for (final Nail nail : edge.getNails()) { + minHeight = Math.max(minHeight, nail.getY() + GRID_SIZE); + } + } + + return minHeight; + } + + public ProcessController getController() { + return controller; + } +} diff --git a/src/main/java/ecdar/presentations/RightSimPanePresentation.java b/src/main/java/ecdar/presentations/RightSimPanePresentation.java new file mode 100755 index 00000000..48c79f1c --- /dev/null +++ b/src/main/java/ecdar/presentations/RightSimPanePresentation.java @@ -0,0 +1,45 @@ +package ecdar.presentations; + +import ecdar.controllers.RightSimPaneController; +import ecdar.utility.colors.Color; +import ecdar.utility.helpers.DropShadowHelper; +import javafx.geometry.Insets; +import javafx.scene.layout.*; + +/** + * Presentation class for the right pane in the simulator + */ +public class RightSimPanePresentation extends StackPane { + private RightSimPaneController controller; + + public RightSimPanePresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("RightSimPanePresentation.fxml", this); + + initializeBackground(); + initializeLeftBorder(); + } + + /** + * Sets the background color of the ScrollPane Vbox + */ + private void initializeBackground() { + controller.scrollPaneVbox.setBackground(new Background(new BackgroundFill( + Color.GREY.getColor(Color.Intensity.I200), + CornerRadii.EMPTY, + Insets.EMPTY + ))); + } + + /** + * Initializes the thin border on the left side of the querypane toolbar + */ + private void initializeLeftBorder() { + setBorder(new Border(new BorderStroke( + Color.GREY_BLUE.getColor(Color.Intensity.I900), + BorderStrokeStyle.SOLID, + CornerRadii.EMPTY, + new BorderWidths(0, 0, 0, 1) + ))); + } + +} diff --git a/src/main/java/ecdar/presentations/SimEdgePresentation.java b/src/main/java/ecdar/presentations/SimEdgePresentation.java new file mode 100755 index 00000000..8d74623b --- /dev/null +++ b/src/main/java/ecdar/presentations/SimEdgePresentation.java @@ -0,0 +1,32 @@ +package ecdar.presentations; + +import ecdar.abstractions.Component; +import ecdar.abstractions.Edge; +import ecdar.controllers.SimEdgeController; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.scene.Group; + +/** + * The presentation class for the edges shown in the {@link SimulatorOverviewPresentation} + */ +public class SimEdgePresentation extends Group { + private final SimEdgeController controller; + + private final ObjectProperty edge = new SimpleObjectProperty<>(); + private final ObjectProperty component = new SimpleObjectProperty<>(); + + public SimEdgePresentation(final Edge edge, final Component component) { + controller = new EcdarFXMLLoader().loadAndGetController("SimEdgePresentation.fxml", this); + + controller.setEdge(edge); + this.edge.bind(controller.edgeProperty()); + + controller.setComponent(component); + this.component.bind(controller.componentProperty()); + } + + public SimEdgeController getController() { + return controller; + } +} diff --git a/src/main/java/ecdar/presentations/SimLocationPresentation.java b/src/main/java/ecdar/presentations/SimLocationPresentation.java new file mode 100755 index 00000000..e250433a --- /dev/null +++ b/src/main/java/ecdar/presentations/SimLocationPresentation.java @@ -0,0 +1,492 @@ +package ecdar.presentations; + +import ecdar.abstractions.Component; +import ecdar.abstractions.Location; +import ecdar.controllers.SimLocationController; +import ecdar.utility.Highlightable; +import ecdar.utility.colors.Color; +import ecdar.utility.helpers.BindingHelper; +import ecdar.utility.helpers.SelectHelper; +import javafx.animation.*; +import javafx.beans.binding.DoubleBinding; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleDoubleProperty; +import javafx.scene.Group; +import javafx.scene.control.Label; +import javafx.scene.effect.DropShadow; +import javafx.scene.paint.Paint; +import javafx.scene.shape.*; +import javafx.util.Duration; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import static ecdar.presentations.LocationPresentation.INITIAL_RADIUS; +import static ecdar.presentations.LocationPresentation.RADIUS; + +/** + * Presentation for a location in the {@link SimulatorOverviewPresentation}. + * This class should be refactored such that the shared code between this class + * and {@link LocationPresentation} is placed in a base class. + */ +public class SimLocationPresentation extends Group implements Highlightable { + + private static int id = 0; + private final SimLocationController controller; + private final Timeline initialAnimation = new Timeline(); + private final Timeline hoverAnimationEntered = new Timeline(); + private final Timeline hoverAnimationExited = new Timeline(); + private final Timeline hiddenAreaAnimationEntered = new Timeline(); + private final Timeline hiddenAreaAnimationExited = new Timeline(); + private final Timeline scaleShakeIndicatorBackgroundAnimation = new Timeline(); + private final Timeline shakeContentAnimation = new Timeline(); + private final List> updateColorDelegates = new ArrayList<>(); + private final DoubleProperty animation = new SimpleDoubleProperty(0); + private final DoubleBinding reverseAnimation = new SimpleDoubleProperty(1).subtract(animation); + + /** + * Constructs a Simulator Location ready to be placed on the view + * @param location the location model, which the presenter should show + * @param component the component where the location is + */ + public SimLocationPresentation(final Location location, final Component component) { + controller = new EcdarFXMLLoader().loadAndGetController("SimLocationPresentation.fxml", this); + + // Bind the component with the one of the controller + controller.setComponent(component); + + // Bind the location with the one of the controller + controller.setLocation(location); + + initializeIdLabel(); + initializeTypeGraphics(); + initializeLocationShapes(); + initializeTags(); + initializeShakeAnimation(); + initializeCircle(); + } + + /** + * Initializes the label in the middle of the location + */ + private void initializeIdLabel() { + final Location location = controller.getLocation(); + final Label idLabel = controller.idLabel; + + final DropShadow ds = new DropShadow(); + ds.setRadius(2); + ds.setSpread(1); + + idLabel.setEffect(ds); + + idLabel.textProperty().bind((location.idProperty())); + + // Center align the label + idLabel.widthProperty().addListener((obsWidth, oldWidth, newWidth) -> idLabel.translateXProperty().set(newWidth.doubleValue() / -2)); + idLabel.heightProperty().addListener((obsHeight, oldHeight, newHeight) -> idLabel.translateYProperty().set(newHeight.doubleValue() / -2)); + + final ObjectProperty color = location.colorProperty(); + final ObjectProperty colorIntensity = location.colorIntensityProperty(); + + // Delegate to style the label based on the color of the location + final BiConsumer updateColor = (newColor, newIntensity) -> { + idLabel.setTextFill(newColor.getTextColor(newIntensity)); + ds.setColor(newColor.getColor(newIntensity)); + }; + + updateColorDelegates.add(updateColor); + + // Set the initial color + updateColor.accept(color.get(), colorIntensity.get()); + + // Update the color of the circle when the color of the location is updated + color.addListener((obs, old, newColor) -> updateColor.accept(newColor, colorIntensity.get())); + } + + /** + * Initialize the tags placed on this location + */ + private void initializeTags() { + + // Set the layout from the model (if they are not both 0) + final Location loc = controller.getLocation(); + if((loc.getNicknameX() != 0) && (loc.getNicknameY() != 0)) { + controller.nicknameTag.setTranslateX(loc.getNicknameX()); + controller.nicknameTag.setTranslateY(loc.getNicknameY()); + } + + if((loc.getInvariantX() != 0) && (loc.getInvariantY() != 0)) { + controller.invariantTag.setTranslateX(loc.getInvariantX()); + controller.invariantTag.setTranslateY(loc.getInvariantY()); + } + + // Bind the model to the layout + loc.nicknameXProperty().bind(controller.nicknameTag.translateXProperty()); + loc.nicknameYProperty().bind(controller.nicknameTag.translateYProperty()); + loc.invariantXProperty().bind(controller.invariantTag.translateXProperty()); + loc.invariantYProperty().bind(controller.invariantTag.translateYProperty()); + + final Consumer updateTags = location -> { + // Update the color + controller.nicknameTag.bindToColor(location.colorProperty(), location.colorIntensityProperty(), true); + controller.invariantTag.bindToColor(location.colorProperty(), location.colorIntensityProperty(), false); + + // Update the invariant + controller.nicknameTag.setAndBindString(location.nicknameProperty()); + controller.invariantTag.setAndBindString(location.invariantProperty()); + + // Set the visibility of the name tag depending on the nickname + final Consumer updateVisibilityFromNickName = (nickname) -> { + if (nickname.equals("")) { + controller.nicknameTag.setOpacity(0); + } else { + controller.nicknameTag.setOpacity(1); + } + }; + + location.nicknameProperty().addListener((obs, oldNickname, newNickname) -> updateVisibilityFromNickName.accept(newNickname)); + updateVisibilityFromNickName.accept(location.getNickname()); + + // Set the visibility of the invariant tag depending on the invariant + final Consumer updateVisibilityFromInvariant = (invariant) -> { + if (invariant.equals("") ) { + controller.invariantTag.setOpacity(0); + } else { + controller.invariantTag.setOpacity(1); + } + }; + + location.invariantProperty().addListener((obs, oldInvariant, newInvariant) -> updateVisibilityFromInvariant.accept(newInvariant)); + updateVisibilityFromInvariant.accept(location.getInvariant()); + + controller.nicknameTag.setComponent(controller.getComponent()); + controller.nicknameTag.setLocationAware(location); + BindingHelper.bind(controller.nameTagLine, controller.nicknameTag); + + controller.invariantTag.setComponent(controller.getComponent()); + controller.invariantTag.setLocationAware(location); + BindingHelper.bind(controller.invariantTagLine, controller.invariantTag); + }; + + // Update the tags when the loc updates + controller.locationProperty().addListener(observable -> updateTags.accept(loc)); + + // Initialize the tags from the current loc + updateTags.accept(loc); + } + + /** + * Initialize the circle which makes up most of the location + */ + private void initializeCircle() { + final Location location = controller.getLocation(); + + final Circle circle = controller.circle; + circle.setRadius(RADIUS); + final ObjectProperty color = location.colorProperty(); + final ObjectProperty colorIntensity = location.colorIntensityProperty(); + + // Delegate to style the label based on the color of the location + final BiConsumer updateColor = (newColor, newIntensity) -> { + circle.setFill(newColor.getColor(newIntensity)); + circle.setStroke(newColor.getColor(newIntensity.next(2))); + }; + + updateColorDelegates.add(updateColor); + + // Set the initial color + updateColor.accept(color.get(), colorIntensity.get()); + + // Update the color of the circle when the color of the location is updated + color.addListener((obs, old, newColor) -> updateColor.accept(newColor, colorIntensity.get())); + colorIntensity.addListener((obs, old, newIntensity) -> updateColor.accept(color.get(), newIntensity)); + } + + /** + * Initializes the shapes of a urgent location + */ + private void initializeLocationShapes() { + final Path notCommittedShape = controller.notCommittedShape; + + // Bind sizes for shape that transforms between urgent and normal + initializeLocationShapes(notCommittedShape, RADIUS); + + final Location location = controller.getLocation(); + + final BiConsumer updateUrgencies = (oldUrgency, newUrgency) -> { + final Transition toUrgent = new Transition() { + { + setCycleDuration(Duration.millis(200)); + } + + @Override + protected void interpolate(final double frac) { + animation.set(frac); + } + }; + + final Transition toNormal = new Transition() { + { + setCycleDuration(Duration.millis(200)); + } + + @Override + protected void interpolate(final double frac) { + animation.set(1-frac); + } + }; + + if(oldUrgency.equals(Location.Urgency.NORMAL) && !newUrgency.equals(Location.Urgency.NORMAL)) { + toUrgent.play(); + } else if(newUrgency.equals(Location.Urgency.NORMAL)) { + toNormal.play(); + } + notCommittedShape.setVisible(true); + }; + + location.urgencyProperty().addListener((obsUrgency, oldUrgency, newUrgency) -> { + updateUrgencies.accept(oldUrgency, newUrgency); + }); + + updateUrgencies.accept(Location.Urgency.NORMAL, location.getUrgency()); + + // Update the colors + final ObjectProperty color = location.colorProperty(); + final ObjectProperty colorIntensity = location.colorIntensityProperty(); + + // Delegate to style the label based on the color of the location + final BiConsumer updateColor = (newColor, newIntensity) -> { + notCommittedShape.setFill(newColor.getColor(newIntensity)); + notCommittedShape.setStroke(newColor.getColor(newIntensity.next(2))); + }; + + updateColorDelegates.add(updateColor); + + // Set the initial color + updateColor.accept(color.get(), colorIntensity.get()); + + // Update the color of the circle when the color of the location is updated + color.addListener((obs, old, newColor) -> updateColor.accept(newColor, colorIntensity.get())); + } + + private void initializeTypeGraphics() { + final Location location = controller.getLocation(); + + final Path notCommittedInitialIndicator = controller.notCommittedInitialIndicator; + + // Bind visibility and size of normal shape + initializeLocationShapes(notCommittedInitialIndicator, INITIAL_RADIUS); + notCommittedInitialIndicator.visibleProperty().bind(location.typeProperty().isEqualTo(Location.Type.INITIAL)); + // As the style sometimes "forget" its values + notCommittedInitialIndicator.setStrokeType(StrokeType.INSIDE); + notCommittedInitialIndicator.setStroke(Paint.valueOf("white")); + notCommittedInitialIndicator.setFill(Paint.valueOf("transparent")); + } + + public void animateIn() { + initialAnimation.play(); + } + + public void animateHoverEntered() { + + if (shakeContentAnimation.getStatus().equals(Animation.Status.RUNNING)) return; + + hoverAnimationEntered.play(); + } + + public void animateHoverExited() { + if (shakeContentAnimation.getStatus().equals(Animation.Status.RUNNING)) return; + + hoverAnimationExited.play(); + } + + public void animateLocationEntered() { + hiddenAreaAnimationExited.stop(); + hiddenAreaAnimationEntered.play(); + } + + public void animateLocationExited() { + hiddenAreaAnimationEntered.stop(); + hiddenAreaAnimationExited.play(); + } + + + /** + * Initializes the animation of shaking the location. Can for instance be used when the user tries an + * action which is not allowed, i.e. deleting + */ + private void initializeShakeAnimation() { + final Interpolator interpolator = Interpolator.SPLINE(0.645, 0.045, 0.355, 1); + + final KeyValue scale0x = new KeyValue(controller.scaleContent.scaleXProperty(), 1, interpolator); + final KeyValue radius0 = new KeyValue(controller.circleShakeIndicator.radiusProperty(), 0, interpolator); + final KeyValue opacity0 = new KeyValue(controller.circleShakeIndicator.opacityProperty(), 0, interpolator); + + final KeyValue scale1x = new KeyValue(controller.scaleContent.scaleXProperty(), 1.3, interpolator); + final KeyValue radius1 = new KeyValue(controller.circleShakeIndicator.radiusProperty(), controller.circle.getRadius() * 0.85, interpolator); + final KeyValue opacity1 = new KeyValue(controller.circleShakeIndicator.opacityProperty(), 0.2, interpolator); + + final KeyFrame kf1 = new KeyFrame(Duration.millis(0), scale0x, radius0, opacity0); + final KeyFrame kf2 = new KeyFrame(Duration.millis(2500), scale1x, radius1, opacity1); + final KeyFrame kf3 = new KeyFrame(Duration.millis(3300), radius0, opacity0); + final KeyFrame kf4 = new KeyFrame(Duration.millis(3500), scale0x); + final KeyFrame kfEnd = new KeyFrame(Duration.millis(8000)); + + scaleShakeIndicatorBackgroundAnimation.getKeyFrames().addAll(kf1, kf2, kf3, kf4, kfEnd); + + final KeyValue noShakeX = new KeyValue(controller.shakeContent.translateXProperty(), 0, interpolator); + final KeyValue shakeLeftX = new KeyValue(controller.shakeContent.translateXProperty(), -1, interpolator); + final KeyValue shakeRightX = new KeyValue(controller.shakeContent.translateXProperty(), 1, interpolator); + + final KeyFrame[] shakeFrames = { + new KeyFrame(Duration.millis(0), noShakeX), + new KeyFrame(Duration.millis(1450), noShakeX), + + new KeyFrame(Duration.millis(1500), shakeLeftX), + new KeyFrame(Duration.millis(1550), shakeRightX), + new KeyFrame(Duration.millis(1600), shakeLeftX), + new KeyFrame(Duration.millis(1650), shakeRightX), + new KeyFrame(Duration.millis(1700), shakeLeftX), + new KeyFrame(Duration.millis(1750), shakeRightX), + new KeyFrame(Duration.millis(1800), shakeLeftX), + new KeyFrame(Duration.millis(1850), shakeRightX), + new KeyFrame(Duration.millis(1900), shakeLeftX), + new KeyFrame(Duration.millis(1950), shakeRightX), + new KeyFrame(Duration.millis(2000), shakeLeftX), + new KeyFrame(Duration.millis(2050), shakeRightX), + new KeyFrame(Duration.millis(2100), shakeLeftX), + new KeyFrame(Duration.millis(2150), shakeRightX), + new KeyFrame(Duration.millis(2200), shakeLeftX), + new KeyFrame(Duration.millis(2250), shakeRightX), + + new KeyFrame(Duration.millis(2300), noShakeX), + new KeyFrame(Duration.millis(8000)) + }; + + shakeContentAnimation.getKeyFrames().addAll(shakeFrames); + + shakeContentAnimation.setCycleCount(1000); + scaleShakeIndicatorBackgroundAnimation.setCycleCount(1000); + } + + /** + * Plays the shake animation + * @param start false - the animation resets to the beginning
+ * true - the animation starts + */ + public void animateShakeWarning(final boolean start) { + if (start) { + scaleShakeIndicatorBackgroundAnimation.play(); + shakeContentAnimation.play(); + } else { + controller.scaleContent.scaleXProperty().set(1); + scaleShakeIndicatorBackgroundAnimation.playFromStart(); + scaleShakeIndicatorBackgroundAnimation.stop(); + + controller.circleShakeIndicator.setOpacity(0); + shakeContentAnimation.playFromStart(); + shakeContentAnimation.stop(); + } + } + + /** + * Get the controller associated with this presenter + * @return the controller + */ + public SimLocationController getController() { + return controller; + } + + private void initializeLocationShapes(final Path locationShape, final double radius) { + final double c = 0.551915024494; + final double circleToOctagonLineRatio = 0.35; + + final MoveTo moveTo = new MoveTo(); + moveTo.xProperty().bind(animation.multiply(circleToOctagonLineRatio * radius)); + moveTo.yProperty().set(radius); + + final CubicCurveTo cc1 = new CubicCurveTo(); + cc1.controlX1Property().bind(reverseAnimation.multiply(c * radius).add(animation.multiply(circleToOctagonLineRatio * radius))); + cc1.controlY1Property().bind(reverseAnimation.multiply(radius).add(animation.multiply(radius))); + cc1.controlX2Property().bind(reverseAnimation.multiply(radius).add(animation.multiply(radius))); + cc1.controlY2Property().bind(reverseAnimation.multiply(c * radius).add(animation.multiply(circleToOctagonLineRatio * radius))); + cc1.setX(radius); + cc1.yProperty().bind(animation.multiply(circleToOctagonLineRatio * radius)); + + + final LineTo lineTo1 = new LineTo(); + lineTo1.xProperty().bind(cc1.xProperty()); + lineTo1.yProperty().bind(cc1.yProperty().multiply(-1)); + + final CubicCurveTo cc2 = new CubicCurveTo(); + cc2.controlX1Property().bind(cc1.controlX2Property()); + cc2.controlY1Property().bind(cc1.controlY2Property().multiply(-1)); + cc2.controlX2Property().bind(cc1.controlX1Property()); + cc2.controlY2Property().bind(cc1.controlY1Property().multiply(-1)); + cc2.xProperty().bind(moveTo.xProperty()); + cc2.yProperty().bind(moveTo.yProperty().multiply(-1)); + + + final LineTo lineTo2 = new LineTo(); + lineTo2.xProperty().bind(cc2.xProperty().multiply(-1)); + lineTo2.yProperty().bind(cc2.yProperty()); + + final CubicCurveTo cc3 = new CubicCurveTo(); + cc3.controlX1Property().bind(cc2.controlX2Property().multiply(-1)); + cc3.controlY1Property().bind(cc2.controlY2Property()); + cc3.controlX2Property().bind(cc2.controlX1Property().multiply(-1)); + cc3.controlY2Property().bind(cc2.controlY1Property()); + cc3.xProperty().bind(lineTo1.xProperty().multiply(-1)); + cc3.yProperty().bind(lineTo1.yProperty()); + + + final LineTo lineTo3 = new LineTo(); + lineTo3.xProperty().bind(cc3.xProperty()); + lineTo3.yProperty().bind(cc3.yProperty().multiply(-1)); + + final CubicCurveTo cc4 = new CubicCurveTo(); + cc4.controlX1Property().bind(cc3.controlX2Property()); + cc4.controlY1Property().bind(cc3.controlY2Property().multiply(-1)); + cc4.controlX2Property().bind(cc3.controlX1Property()); + cc4.controlY2Property().bind(cc3.controlY1Property().multiply(-1)); + cc4.xProperty().bind(lineTo2.xProperty()); + cc4.yProperty().bind(lineTo2.yProperty().multiply(-1)); + + + final LineTo lineTo4 = new LineTo(); + lineTo4.xProperty().bind(moveTo.xProperty()); + lineTo4.yProperty().bind(moveTo.yProperty()); + + + locationShape.getElements().add(moveTo); + locationShape.getElements().add(cc1); + + locationShape.getElements().add(lineTo1); + locationShape.getElements().add(cc2); + + locationShape.getElements().add(lineTo2); + locationShape.getElements().add(cc3); + + locationShape.getElements().add(lineTo3); + locationShape.getElements().add(cc4); + + locationShape.getElements().add(lineTo4); + } + + @Override + public void highlight() { + updateColorDelegates.forEach(colorConsumer -> colorConsumer.accept(SelectHelper.SELECT_COLOR, SelectHelper.SELECT_COLOR_INTENSITY_NORMAL)); + } + + @Override + public void unhighlight() { + updateColorDelegates.forEach(colorConsumer -> { + final Location location = controller.getLocation(); + + colorConsumer.accept(location.getColor(), location.getColorIntensity()); + }); + } +} \ No newline at end of file diff --git a/src/main/java/ecdar/presentations/SimNailPresentation.java b/src/main/java/ecdar/presentations/SimNailPresentation.java new file mode 100755 index 00000000..250f2f9d --- /dev/null +++ b/src/main/java/ecdar/presentations/SimNailPresentation.java @@ -0,0 +1,258 @@ +package ecdar.presentations; + +import ecdar.abstractions.Component; +import ecdar.abstractions.Edge; +import ecdar.abstractions.EdgeStatus; +import ecdar.abstractions.Nail; +import ecdar.controllers.SimEdgeController; +import ecdar.controllers.SimNailController; +import ecdar.utility.Highlightable; +import ecdar.utility.colors.Color; +import ecdar.utility.helpers.BindingHelper; +import javafx.animation.Interpolator; +import javafx.animation.KeyFrame; +import javafx.animation.KeyValue; +import javafx.animation.Timeline; +import javafx.scene.Group; +import javafx.scene.control.Label; +import javafx.scene.shape.Line; + +import java.util.function.Consumer; + +import static javafx.util.Duration.millis; + +/** + * The presentation for the nail shown on a {@link SimEdgePresentation} in the {@link SimulatorOverviewPresentation}
+ * This class should be refactored such that code which are duplicated from {@link NailPresentation} + * have its own base class. + */ +public class SimNailPresentation extends Group implements Highlightable { + + public static final double COLLAPSED_RADIUS = 2d; + public static final double HOVERED_RADIUS = 7d; + + private final SimNailController controller; + private final Timeline shakeAnimation = new Timeline(); + + /** + * Constructs a nail which is ready to be displayed in the simulator + * @param nail the nail which should be presented + * @param edge the edge where the nail belongs to + * @param component the component where the nail belongs + * @param simEdgeController the controller of the edge where the nail belongs to + */ + public SimNailPresentation(final Nail nail, final Edge edge, final Component component, final SimEdgeController simEdgeController) { + controller = new EcdarFXMLLoader().loadAndGetController("SimNailPresentation.fxml", this); + + // Bind the component with the one of the controller + controller.setComponent(component); + + // Bind the edge with the one of the controller + controller.setEdge(edge); + // Bind the nail with the one of the controller + controller.setNail(nail); + + controller.setEdgeController(simEdgeController); + + initializeNailCircleColor(); + initializePropertyTag(); + initializeRadius(); + initializeShakeAnimation(); + } + + /** + * Sets the radius of the nail + */ + private void initializeRadius() { + final Consumer radiusUpdater = (propertyType) -> { + if(!propertyType.equals(Edge.PropertyType.NONE)) { + controller.getNail().setRadius(SimNailPresentation.HOVERED_RADIUS); + } + }; + controller.getNail().propertyTypeProperty().addListener((observable, oldValue, newValue) -> { + radiusUpdater.accept(newValue); + }); + radiusUpdater.accept(controller.getNail().getPropertyType()); + } + + /** + * Initializes the text / PropertyTag shown on a {@link SimTagPresentation} on the nail. + */ + private void initializePropertyTag() { + final SimTagPresentation propertyTag = controller.propertyTag; + final Line propertyTagLine = controller.propertyTagLine; + propertyTag.setComponent(controller.getComponent()); + propertyTag.setLocationAware(controller.getNail()); + + // Bind the line to the tag + BindingHelper.bind(propertyTagLine, propertyTag); + + // Bind the color of the tag to the color of the component + propertyTag.bindToColor(controller.getComponent().colorProperty(), controller.getComponent().colorIntensityProperty()); + + // Updates visibility and placeholder of the tag depending on the type of nail + final Consumer updatePropertyType = (propertyType) -> { + + // If it is not a property nail hide the tag otherwise show it and write proper placeholder + if(propertyType.equals(Edge.PropertyType.NONE)) { + propertyTag.setVisible(false); + } else { + + // Show the property tag since the nail is a property nail + propertyTag.setVisible(true); + + // Set and bind the location of the property tag + if((controller.getNail().getPropertyX() != 0) && (controller.getNail().getPropertyY() != 0)) { + propertyTag.setTranslateX(controller.getNail().getPropertyX()); + propertyTag.setTranslateY(controller.getNail().getPropertyY()); + } + controller.getNail().propertyXProperty().bind(propertyTag.translateXProperty()); + controller.getNail().propertyYProperty().bind(propertyTag.translateYProperty()); + + final Label propertyLabel = controller.propertyLabel; + + if(propertyType.equals(Edge.PropertyType.SELECTION)) { + propertyLabel.setText(":"); + propertyLabel.setTranslateX(-3); + propertyLabel.setTranslateY(-8); + propertyTag.setAndBindString(controller.getEdge().selectProperty()); + } else if(propertyType.equals(Edge.PropertyType.GUARD)) { + propertyLabel.setText("<"); + propertyLabel.setTranslateX(-3); + propertyLabel.setTranslateY(-7); + propertyTag.setAndBindString(controller.getEdge().guardProperty()); + } else if(propertyType.equals(Edge.PropertyType.SYNCHRONIZATION)) { + updateSyncLabel(); + propertyLabel.setTranslateX(-3); + propertyLabel.setTranslateY(-7); + propertyTag.setAndBindString(controller.getEdge().syncProperty()); + } else if(propertyType.equals(Edge.PropertyType.UPDATE)) { + propertyLabel.setText("="); + propertyLabel.setTranslateX(-3); + propertyLabel.setTranslateY(-7); + propertyTag.setAndBindString(controller.getEdge().updateProperty()); + } + + //Disable the ability to edit the tag if the nails edge is locked + if(controller.getEdge().getIsLockedProperty().getValue()){ + propertyTag.setDisabledText(true); + } + } + }; + + // Whenever the property type updates, update the tag + controller.getNail().propertyTypeProperty().addListener((obs, oldPropertyType, newPropertyType) -> { + updatePropertyType.accept(newPropertyType); + }); + + // Whenever the edge changes I/O status, if sync nail then update its label + controller.getEdge().ioStatus.addListener((observable, oldValue, newValue) -> { + if (controller.getNail().getPropertyType().equals(Edge.PropertyType.SYNCHRONIZATION)) + updateSyncLabel(); + }); + + // Update the tag initially + updatePropertyType.accept(controller.getNail().getPropertyType()); + } + + /** + * Updates the synchronization label and tag. + * The update depends on the edge I/O status. + */ + private void updateSyncLabel() { + final Label propertyLabel = controller.propertyLabel; + + // show ? or ! dependent on edge I/O status + if (controller.getEdge().ioStatus.get().equals(EdgeStatus.INPUT)) { + propertyLabel.setText("?"); + } else { + propertyLabel.setText("!"); + } + } + + /** + * Set up Listeners for updating the color of the nail, set the color initially + */ + private void initializeNailCircleColor() { + final Runnable updateNailColor = () -> { + final Color color = controller.getComponent().getColor(); + final Color.Intensity colorIntensity = controller.getComponent().getColorIntensity(); + + if(!controller.getNail().getPropertyType().equals(Edge.PropertyType.NONE)) { + controller.nailCircle.setFill(color.getColor(colorIntensity)); + controller.nailCircle.setStroke(color.getColor(colorIntensity.next(2))); + } else { + controller.nailCircle.setFill(Color.GREY_BLUE.getColor(Color.Intensity.I800)); + controller.nailCircle.setStroke(Color.GREY_BLUE.getColor(Color.Intensity.I900)); + } + }; + + // When the color of the component updates, update the nail indicator as well + controller.getComponent().colorProperty().addListener((observable) -> updateNailColor.run()); + + // When the color intensity of the component updates, update the nail indicator + controller.getComponent().colorIntensityProperty().addListener((observable) -> updateNailColor.run()); + + // Initialize the color of the nail + updateNailColor.run(); + } + + /** + * Initializes a shake animation found in {@link SimNailPresentation#shakeAnimation} which can + * be played using {@link SimNailPresentation#shake()} + */ + private void initializeShakeAnimation() { + final Interpolator interpolator = Interpolator.SPLINE(0.645, 0.045, 0.355, 1); + + final double startX = controller.root.getTranslateX(); + final KeyValue kv1 = new KeyValue(controller.root.translateXProperty(), startX - 3, interpolator); + final KeyValue kv2 = new KeyValue(controller.root.translateXProperty(), startX + 3, interpolator); + final KeyValue kv3 = new KeyValue(controller.root.translateXProperty(), startX, interpolator); + + final KeyFrame kf1 = new KeyFrame(millis(50), kv1); + final KeyFrame kf2 = new KeyFrame(millis(100), kv2); + final KeyFrame kf3 = new KeyFrame(millis(150), kv1); + final KeyFrame kf4 = new KeyFrame(millis(200), kv2); + final KeyFrame kf5 = new KeyFrame(millis(250), kv3); + + shakeAnimation.getKeyFrames().addAll(kf1, kf2, kf3, kf4, kf5); + } + + /** + * Plays the {@link SimNailPresentation#shakeAnimation} + */ + public void shake() { + shakeAnimation.play(); + } + + /** + * Highlights the nail + */ + @Override + public void highlight() { + final Color color = Color.DEEP_ORANGE; + final Color.Intensity intensity = Color.Intensity.I500; + + // Set the color + controller.nailCircle.setFill(color.getColor(intensity)); + controller.nailCircle.setStroke(color.getColor(intensity.next(2))); + } + + /** + * Removes the highlight from the nail + */ + @Override + public void unhighlight() { + Color color = Color.GREY_BLUE; + Color.Intensity intensity = Color.Intensity.I800; + + // Set the color + if(!controller.getNail().getPropertyType().equals(Edge.PropertyType.NONE)) { + color = controller.getComponent().getColor(); + intensity = controller.getComponent().getColorIntensity(); + } + + controller.nailCircle.setFill(color.getColor(intensity)); + controller.nailCircle.setStroke(color.getColor(intensity.next(2))); + } +} diff --git a/src/main/java/ecdar/presentations/SimTagPresentation.java b/src/main/java/ecdar/presentations/SimTagPresentation.java new file mode 100755 index 00000000..d2916ee1 --- /dev/null +++ b/src/main/java/ecdar/presentations/SimTagPresentation.java @@ -0,0 +1,186 @@ +package ecdar.presentations; + +import ecdar.abstractions.Component; +import ecdar.utility.colors.Color; +import ecdar.utility.helpers.LocationAware; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.StringProperty; +import javafx.geometry.Insets; +import javafx.scene.control.Label; +import javafx.scene.layout.StackPane; +import javafx.scene.shape.LineTo; +import javafx.scene.shape.MoveTo; +import javafx.scene.shape.Path; + +import java.util.function.BiConsumer; + +import static ecdar.presentations.Grid.GRID_SIZE; + +/** + * The presentation for the tag shown on a {@link SimEdgePresentation} in the {@link SimulatorOverviewPresentation}
+ * This class should be refactored such that code which are duplicated from {@link TagPresentation} + * have its own base class. + */ +public class SimTagPresentation extends StackPane { + + private final static Color backgroundColor = Color.GREY; + private final static Color.Intensity backgroundColorIntensity = Color.Intensity.I50; + + private final ObjectProperty component = new SimpleObjectProperty<>(null); + private final ObjectProperty locationAware = new SimpleObjectProperty<>(null); + + private LineTo l2; + private LineTo l3; + + private static double TAG_HEIGHT = 1.6 * GRID_SIZE; + + /** + * Constructs the {@link SimTagPresentation} + */ + public SimTagPresentation() { + new EcdarFXMLLoader().loadAndGetController("SimTagPresentation.fxml", this); + initializeShape(); + initializeLabel(); + initializeMouseTransparency(); + } + + private void initializeMouseTransparency() { + mouseTransparentProperty().bind(opacityProperty().isEqualTo(0, 0.00f)); + } + + /** + * Initializes the label which shows the property + */ + private void initializeLabel() { + final Label label = (Label) lookup("#label"); + final Path shape = (Path) lookup("#shape"); + + final Insets insets = new Insets(0,2,0,2); + label.setPadding(insets); + + final int padding = 0; + + label.layoutBoundsProperty().addListener((obs, oldBounds, newBounds) -> { + double newWidth = Math.max(newBounds.getWidth(), 10); + final double res = GRID_SIZE * 2 - (newWidth % (GRID_SIZE * 2)); + newWidth += res; + + l2.setX(newWidth + padding); + l3.setX(newWidth + padding); + + setMinWidth(newWidth + padding); + setMaxWidth(newWidth + padding); + + label.focusedProperty().addListener((observable, oldFocused, newFocused) -> { + if (newFocused) { + shape.setTranslateY(2); + label.setTranslateY(2); + } + }); + + if (getWidth() >= 1000) { + setWidth(newWidth); + setHeight(TAG_HEIGHT); + shape.setTranslateY(-1); + } + + // Fixes the jumping of the shape when the text field is empty + if (label.getText().isEmpty()) { + shape.setLayoutX(0); + } + }); + } + + /** + * Initialize the shape which is around the property label + */ + private void initializeShape() { + final int WIDTH = 5000; + final double HEIGHT = TAG_HEIGHT; + + final Path shape = (Path) lookup("#shape"); + + final MoveTo start = new MoveTo(0, 0); + + l2 = new LineTo(WIDTH, 0); + l3 = new LineTo(WIDTH, HEIGHT); + final LineTo l4 = new LineTo(0, HEIGHT); + final LineTo l6 = new LineTo(0, 0); + + shape.getElements().addAll(start, l2, l3, l4, l6); + shape.setFill(backgroundColor.getColor(backgroundColorIntensity)); + shape.setStroke(backgroundColor.getColor(backgroundColorIntensity.next(4))); + } + + public void bindToColor(final ObjectProperty color, final ObjectProperty intensity) { + bindToColor(color, intensity, false); + } + + public void bindToColor(final ObjectProperty color, final ObjectProperty intensity, final boolean doColorBackground) { + final BiConsumer recolor = (newColor, newIntensity) -> { + if (doColorBackground) { + final Path shape = (Path) lookup("#shape"); + shape.setFill(newColor.getColor(newIntensity.next(-1))); + shape.setStroke(newColor.getColor(newIntensity.next(-1).next(2))); + } + }; + + color.addListener(observable -> recolor.accept(color.get(), intensity.get())); + intensity.addListener(observable -> recolor.accept(color.get(), intensity.get())); + recolor.accept(color.get(), intensity.get()); + } + + /** + * Updates the label with the given string and binds updates from the label on the given {@link StringProperty} + * @param string the string to set and bind to + */ + public void setAndBindString(final StringProperty string) { + final Label label = (Label) lookup("#label"); + + label.textProperty().unbind(); + label.setText(string.get()); + string.bind(label.textProperty()); + } + + /** + * Replaces spaces with underscores in the label + */ +/* public void replaceSpace() { + initializeTextAid(); + }*/ + + public Component getComponent() { + return component.get(); + } + + public void setComponent(final Component component) { + this.component.set(component); + } + + public ObjectProperty componentProperty() { + return component; + } + + public LocationAware getLocationAware() { + return locationAware.get(); + } + + public ObjectProperty locationAwareProperty() { + return locationAware; + } + + public void setLocationAware(LocationAware locationAware) { + this.locationAware.set(locationAware); + } + + /** + * Sets the disabled property in the label + * @param bool true --- the label will be disabled + * false --- the label will be enabled + */ + public void setDisabledText(boolean bool){ + final Label label = (Label) lookup("#label"); + label.setDisable(true); + } +} diff --git a/src/main/java/ecdar/presentations/SimulationInitializationDialogPresentation.java b/src/main/java/ecdar/presentations/SimulationInitializationDialogPresentation.java new file mode 100644 index 00000000..677217bd --- /dev/null +++ b/src/main/java/ecdar/presentations/SimulationInitializationDialogPresentation.java @@ -0,0 +1,17 @@ +package ecdar.presentations; + +import com.jfoenix.controls.JFXDialog; +import ecdar.controllers.BackendOptionsDialogController; +import ecdar.controllers.SimulationInitializationDialogController; + +public class SimulationInitializationDialogPresentation extends JFXDialog { + private final SimulationInitializationDialogController controller; + + public SimulationInitializationDialogPresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("SimulationInitializationDialogPresentation.fxml", this); + } + + public SimulationInitializationDialogController getController() { + return controller; + } +} diff --git a/src/main/java/ecdar/presentations/SimulatorOverviewPresentation.java b/src/main/java/ecdar/presentations/SimulatorOverviewPresentation.java new file mode 100755 index 00000000..15a34e54 --- /dev/null +++ b/src/main/java/ecdar/presentations/SimulatorOverviewPresentation.java @@ -0,0 +1,20 @@ +package ecdar.presentations; + +import ecdar.controllers.SimulatorOverviewController; +import javafx.scene.layout.AnchorPane; + +/** + * The presenter of the middle part of the simulator. + * It is here where processes of a simulation will be shown. + */ +public class SimulatorOverviewPresentation extends AnchorPane { + private final SimulatorOverviewController controller; + + public SimulatorOverviewPresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("SimulatorOverviewPresentation.fxml", this); + } + + public SimulatorOverviewController getController() { + return controller; + } +} diff --git a/src/main/java/ecdar/presentations/SimulatorPresentation.java b/src/main/java/ecdar/presentations/SimulatorPresentation.java new file mode 100755 index 00000000..255b7dd1 --- /dev/null +++ b/src/main/java/ecdar/presentations/SimulatorPresentation.java @@ -0,0 +1,20 @@ +package ecdar.presentations; + +import ecdar.controllers.SimulatorController; +import javafx.scene.layout.StackPane; + +public class SimulatorPresentation extends StackPane { + private final SimulatorController controller; + + public SimulatorPresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("SimulatorPresentation.fxml", this); + } + + /** + * The way to get the associated/linked controller of this presenter + * @return the controller linked to this presenter + */ + public SimulatorController getController() { + return controller; + } +} diff --git a/src/main/java/ecdar/presentations/TracePaneElementPresentation.java b/src/main/java/ecdar/presentations/TracePaneElementPresentation.java new file mode 100755 index 00000000..0e8be627 --- /dev/null +++ b/src/main/java/ecdar/presentations/TracePaneElementPresentation.java @@ -0,0 +1,96 @@ +package ecdar.presentations; + +import com.jfoenix.controls.JFXRippler; +import ecdar.controllers.TracePaneElementController; +import ecdar.utility.colors.Color; +import javafx.geometry.Insets; +import javafx.scene.Cursor; +import javafx.scene.layout.*; + +import java.util.function.BiConsumer; + +/** + * The presentation class for the trace element that can be inserted into the simulator panes + */ +public class TracePaneElementPresentation extends VBox { + final private TracePaneElementController controller; + + public TracePaneElementPresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("TracePaneElementPresentation.fxml", this); + + initializeToolbar(); + initializeSummaryView(); + } + + /** + * Initializes the tool bar that contains the trace pane element's title and buttons + * Sets the color of the bar and title label. Also sets the look of the rippler effect + */ + private void initializeToolbar() { + final Color color = Color.GREY_BLUE; + final Color.Intensity colorIntensity = Color.Intensity.I800; + + controller.toolbar.setBackground(new Background(new BackgroundFill( + color.getColor(colorIntensity), + CornerRadii.EMPTY, + Insets.EMPTY))); + controller.traceTitle.setTextFill(color.getTextColor(colorIntensity)); + + controller.expandTrace.setMaskType(JFXRippler.RipplerMask.CIRCLE); + controller.expandTrace.setRipplerFill(color.getTextColor(colorIntensity)); + } + + /** + * Initializes the summary view so it is update when steps are taken in the trace. + * Also changes the color and cursor when mouse enters and exits the summary view. + */ + private void initializeSummaryView() { + controller.getNumberOfStepsProperty().addListener( + (observable, oldValue, newValue) -> updateSummaryTitle(newValue.intValue())); + + final Color color = Color.GREY_BLUE; + final Color.Intensity colorIntensity = Color.Intensity.I50; + + final BiConsumer setBackground = (newColor, newIntensity) -> { + controller.traceSummary.setBackground(new Background(new BackgroundFill( + newColor.getColor(newIntensity), + CornerRadii.EMPTY, + Insets.EMPTY + ))); + + controller.traceSummary.setBorder(new Border(new BorderStroke( + newColor.getColor(newIntensity.next(2)), + BorderStrokeStyle.SOLID, + CornerRadii.EMPTY, + new BorderWidths(0, 0, 1, 0) + ))); + }; + + // Update the background when hovered + controller.traceSummary.setOnMouseEntered(event -> { + setBackground.accept(color, colorIntensity.next()); + setCursor(Cursor.HAND); + }); + + // Update the background when the mouse exits + controller.traceSummary.setOnMouseExited(event -> { + setBackground.accept(color, colorIntensity); + setCursor(Cursor.DEFAULT); + }); + + // Update the background initially + setBackground.accept(color, colorIntensity); + } + + /** + * Updates the text of the summary title label with the current number of steps in the trace + * @param steps The number of steps in the trace + */ + private void updateSummaryTitle(int steps) { + controller.summaryTitleLabel.setText(steps + " number of steps in trace"); + } + + public TracePaneElementController getController() { + return controller; + } +} diff --git a/src/main/java/ecdar/presentations/TransitionPaneElementPresentation.java b/src/main/java/ecdar/presentations/TransitionPaneElementPresentation.java new file mode 100755 index 00000000..8453af64 --- /dev/null +++ b/src/main/java/ecdar/presentations/TransitionPaneElementPresentation.java @@ -0,0 +1,69 @@ +package ecdar.presentations; + +import com.jfoenix.controls.JFXRippler; +import ecdar.controllers.TransitionPaneElementController; +import ecdar.utility.colors.Color; +import javafx.geometry.Insets; +import javafx.scene.layout.*; + +/** + * The presentation class for the transition pane element that can be inserted into the simulator panes + */ +public class TransitionPaneElementPresentation extends VBox { + final private TransitionPaneElementController controller; + + public TransitionPaneElementPresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("TransitionPaneElementPresentation.fxml", this); + + initializeToolbar(); + initializeDelayChooser(); + } + + /** + * Initializes the toolbar for the transition pane element. + * Sets the background of the toolbar and changes the title color. + * Also changes the look of the rippler effect. + */ + private void initializeToolbar() { + final Color color = Color.GREY_BLUE; + final Color.Intensity colorIntensity = Color.Intensity.I800; + + // Set the background of the toolbar + controller.toolbar.setBackground(new Background(new BackgroundFill( + color.getColor(colorIntensity), + CornerRadii.EMPTY, + Insets.EMPTY))); + // Set the font color of elements in the toolbar + controller.toolbarTitle.setTextFill(color.getTextColor(colorIntensity)); + + controller.refreshRippler.setMaskType(JFXRippler.RipplerMask.CIRCLE); + controller.refreshRippler.setRipplerFill(color.getTextColor(colorIntensity)); + + controller.expandTransition.setMaskType(JFXRippler.RipplerMask.CIRCLE); + controller.expandTransition.setRipplerFill(color.getTextColor(colorIntensity)); + } + + /** + * Sets the background color of the delay chooser + */ + private void initializeDelayChooser() { + final Color color = Color.GREY_BLUE; + final Color.Intensity colorIntensity = Color.Intensity.I50; + controller.delayChooser.setBackground(new Background(new BackgroundFill( + color.getColor(colorIntensity), + CornerRadii.EMPTY, + Insets.EMPTY + ))); + + controller.delayChooser.setBorder(new Border(new BorderStroke( + color.getColor(colorIntensity.next(2)), + BorderStrokeStyle.SOLID, + CornerRadii.EMPTY, + new BorderWidths(0, 0, 1, 0) + ))); + } + + public TransitionPaneElementController getController() { + return controller; + } +} diff --git a/src/main/java/ecdar/presentations/TransitionPresentation.java b/src/main/java/ecdar/presentations/TransitionPresentation.java new file mode 100755 index 00000000..15fb47e6 --- /dev/null +++ b/src/main/java/ecdar/presentations/TransitionPresentation.java @@ -0,0 +1,98 @@ +package ecdar.presentations; + +import com.jfoenix.controls.JFXRippler; +import ecdar.controllers.TransitionController; +import ecdar.utility.colors.Color; +import javafx.animation.FadeTransition; +import javafx.animation.Interpolator; +import javafx.geometry.Insets; +import javafx.scene.Cursor; +import javafx.scene.layout.*; +import javafx.util.Duration; + +import java.util.function.BiConsumer; + +/** + * The presentation class for a transition view element. + * It represents a single transition and may be used by classes like {@see TransitionPaneElementController} + * to show a list of transitions + */ +public class TransitionPresentation extends AnchorPane { + private TransitionController controller; + private FadeTransition transition; + + public TransitionPresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("TransitionPresentation.fxml", this); + + initializeRippler(); + initializeColors(); + initializeFadeAnimation(); + } + + /** + * Initializes the rippler. + * Sets the color, mask and position of the rippler effect. + */ + private void initializeRippler() { + final Color color = Color.GREY_BLUE; + final Color.Intensity colorIntensity = Color.Intensity.I400; + + controller.rippler.setMaskType(JFXRippler.RipplerMask.RECT); + controller.rippler.setRipplerFill(color.getColor(colorIntensity)); + controller.rippler.setPosition(JFXRippler.RipplerPos.BACK); + } + + /** + * Initializes the colors of the view. + * The background of the view changes colors depending on whether a mouse enters or exits the view. + */ + private void initializeColors() { + final Color color = Color.GREY_BLUE; + final Color.Intensity colorIntensity = Color.Intensity.I50; + + final BiConsumer setBackground = (newColor, newIntensity) -> { + setBackground(new Background(new BackgroundFill( + newColor.getColor(newIntensity), + CornerRadii.EMPTY, + Insets.EMPTY + ))); + + setBorder(new Border(new BorderStroke( + newColor.getColor(newIntensity.next(2)), + BorderStrokeStyle.SOLID, + CornerRadii.EMPTY, + new BorderWidths(0, 0, 1, 0) + ))); + }; + + // Update the background when hovered + setOnMouseEntered(event -> { + setBackground.accept(color, colorIntensity.next()); + setCursor(Cursor.HAND); + }); + + // Update the background when the mouse exits + setOnMouseExited(event -> { + setBackground.accept(color, colorIntensity); + setCursor(Cursor.DEFAULT); + }); + + // Update the background initially + setBackground.accept(color, colorIntensity); + } + + private void initializeFadeAnimation() { + this.transition = new FadeTransition(Duration.millis(500), this); + transition.setFromValue(0); + transition.setToValue(1); + transition.setInterpolator(Interpolator.EASE_IN); + } + + public void playFadeAnimation() { + this.transition.play(); + } + + public TransitionController getController() { + return controller; + } +} diff --git a/src/main/java/ecdar/simulation/SimulationHandler.java b/src/main/java/ecdar/simulation/SimulationHandler.java new file mode 100755 index 00000000..bb894b62 --- /dev/null +++ b/src/main/java/ecdar/simulation/SimulationHandler.java @@ -0,0 +1,410 @@ +package ecdar.simulation; + +import ecdar.Ecdar; +import ecdar.abstractions.*; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.ObservableMap; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Map; + +/** + * Handles state changes, updates of values / clocks, and keeps track of all the transitions that + * have been taken throughout a simulation. + */ +public class SimulationHandler { + public static final String QUERY_PREFIX = "Query: "; + private ObjectProperty currentConcreteState = new SimpleObjectProperty<>(); + private ObjectProperty initialConcreteState = new SimpleObjectProperty<>(); + private ObjectProperty currentTime = new SimpleObjectProperty<>(); + private BigDecimal delay; + private ArrayList edgesSelected; + private EcdarSystem system; + private SimulationStateSuccessor successor; + private int numberOfSteps; + + /** + * A string to keep track what is currently being simulated + * For now the string is prefixed with {@link #QUERY_PREFIX} when doing a query simulation + * and kept empty when doing system simulations + */ + private String currentSimulation = ""; + + private final ObservableMap simulationVariables = FXCollections.observableHashMap(); + private final ObservableMap simulationClocks = FXCollections.observableHashMap(); + /** + * For some reason the successor.getTransitions() only sometimes returns some of the transitions + * that are available, when running the initial step. + * That is why we need to keep track of the initial transitions. + */ + private final ObservableList initialTransitions = FXCollections.observableArrayList(); + public ObservableList traceLog = FXCollections.observableArrayList(); + public ObservableList availableTransitions = FXCollections.observableArrayList(); + + /** + * Empty constructor that should be used if the system or project has not be initialized yet + */ + public SimulationHandler() { + + } + + /** + * Initializes the default system (non-query system) + */ + public void initializeDefaultSystem() { + currentSimulation = ""; + } + + /** + * Initializes the values and properties in the {@link SimulationHandler}. + * Can also be used as a reset of the simulation. + * THIS METHOD DOES NOT RESET THE ENGINE, + */ + private void initializeSimulation() { + // Initialization + this.delay = new BigDecimal(0); + this.edgesSelected = new ArrayList<>(); + this.numberOfSteps = 0; + this.availableTransitions.clear(); + this.simulationVariables.clear(); + this.simulationClocks.clear(); + this.traceLog.clear(); + this.currentConcreteState.set(getInitialConcreteState()); + this.initialConcreteState.set(getInitialConcreteState()); + this.currentTime = new SimpleObjectProperty<>(BigDecimal.ZERO); + + //Preparation for the simulation + this.system = getSystem(); + this.currentConcreteState.get().setTime(currentTime.getValue()); + this.initialTransitions.clear(); + this.successor = null; + } + + /** + * Reloads the whole simulation sets the initial transitions, states, etc + */ + public void initialStep() { + initializeSimulation(); + final SimulationState currentState = currentConcreteState.get(); + successor = getStateSuccessor(); + + //Save the previous states, and get the new + currentConcreteState.set(successor.getState()); + this.traceLog.add(currentState); + numberOfSteps++; + + //Updates the transitions available + availableTransitions.addAll(FXCollections.observableArrayList(successor.getTransitions())); + initialTransitions.addAll(availableTransitions); + updateAllValues(); + } + + /** + * Resets the simulation to the initial location + * where the SimulationState is the {@link SimulationHandler#initialConcreteState}, when there are + * elements in the {@link SimulationHandler#traceLog}. Otherwise it calls {@link SimulationHandler#initialStep} + */ + public void resetToInitialLocation() { + //If the simulation has not begone + if (traceLog.size() == 0) + initialStep(); + else + selectTransitionFromLog(initialConcreteState.get()); + } + + /** + * Resets the simulation to the state after executing the given transition.
+ * This method also resets the state, variables, and clocks to the values they had after the given transition. + * This also updates {@link SimulationHandler#availableTransitions} such that + * it displays the available transitions after taking the given transition. + * + * @param transition the transition which the simulation should go back to + */ + public void selectTransitionFromLog(final SimulationState transition) { + final int indexInTrace = traceLog.indexOf(transition); + final SimulationState selectedState; + if (indexInTrace == -1) { + System.out.println("Cannot find transition: " + transition); + Ecdar.showToast("Cannot find transition: " + transition); + return; + } else if (indexInTrace == numberOfSteps - 1) { + return; //you have selected the current system + } else { + selectedState = traceLog.get(indexInTrace); + } + final int sizeOfTraceLog = traceLog.size(); + final int maxRetries = 3; + int numberOfRetries = 0; + edgesSelected = new ArrayList<>(); + //In case that we fail we have to save the time we had before + final BigDecimal tempTime = currentTime.get(); + + currentTime.setValue(new BigDecimal(selectedState.getTime().doubleValue())); + successor.getState().setTime(currentTime.getValue()); + + while (numberOfRetries < maxRetries) { + successor = getStateSuccessor(); + break; + } + currentConcreteState.set(selectedState); + setSimVarAndClocks(); + traceLog.remove(indexInTrace + 1, sizeOfTraceLog); + availableTransitions.clear(); + + // If the user selected the initial/first state in the trace log, we do not trust the engine, + // as it only gives us a subset of the available transitions, in some cases. + if (indexInTrace == 0) availableTransitions.addAll(initialTransitions); + else availableTransitions.addAll(successor.getTransitions()); + + numberOfSteps = indexInTrace + 1; + } + + /** + * Take a step in the simulation. + * + * @param selectedTransitionIndex the index of the availableTransition that you want to take. + * @param delay the time which should pass after the transition. + */ + public void nextStep(final int selectedTransitionIndex, final BigDecimal delay) { + if (selectedTransitionIndex > availableTransitions.size()) { + Ecdar.showToast("The selected transition index: " + selectedTransitionIndex + " is bigger than it should: " + availableTransitions); + return; + } + + final Transition selectedTransition = availableTransitions.get(selectedTransitionIndex); + edgesSelected = new ArrayList<>(); + + //Preparing for the step + for (int i = 0; i < selectedTransition.getEdges().size(); i++) { + edgesSelected.set(i, selectedTransition.getEdges().get(i)); + } + + final int maxRetries = 3; + int numberOfRetries = 0; + + // getConcreteSuccessor may throw a "ProtocolException: Word expected" but in some cases calling the same + // method again does not throw this exception, and actually gives us the expected result. + // This loop calls the method a number of times (maxRetries) + while (numberOfRetries < maxRetries) { + successor = getStateSuccessor(); + // Break from the loop if the method call was a success + break; + } + + //Save the previous states, and get the new + currentConcreteState.set(successor.getState()); + this.traceLog.add(currentConcreteState.get()); + + // increments the number of steps taken during this simulation + numberOfSteps++; + + //Updates the transitions available + availableTransitions.clear(); + availableTransitions.setAll(successor.getTransitions()); + this.delay = delay; + updateAllValues(); + } + + private SimulationStateSuccessor getStateSuccessor() { + // ToDo: Implement + return new SimulationStateSuccessor(); + } + + /** + * An overload of {@link SimulationHandler#nextStep(int, BigDecimal)} where the delay is 0. + * + * @param selectedTransition the index of the availableTransition that you want to take. + */ + public void nextStep(final int selectedTransition) { + nextStep(selectedTransition, BigDecimal.ZERO); + } + + public void nextStep(final Transition transition, final BigDecimal delay) { + int index = availableTransitions.indexOf(transition); + if (index != -1) { + nextStep(index, delay); + } + } + + /** + * Updates all values and clocks that are used doing the current simulation. + * It also stores the variables in the {@link SimulationHandler#simulationVariables} + * and the clocks in {@link SimulationHandler#simulationClocks}. + */ + private void updateAllValues() { + currentTime.set(currentTime.get().add(delay)); + successor.getState().setTime(currentTime.get()); + setSimVarAndClocks(); + } + + /** + * Sets the value of simulation variables and clocks, based on {@link SimulationHandler#currentConcreteState} + */ + private void setSimVarAndClocks() { + // The variables and clocks are all found in the getVariables array + // the array is always of the following order: variables, clocks. + // The noOfVars variable thus also functions as an offset for the clocks in the getVariables array +// final int noOfClocks = engine.getSystem().getNoOfClocks(); +// final int noOfVars = engine.getSystem().getNoOfVariables(); + +// for (int i = 0; i < noOfVars; i++){ +// simulationVariables.put(engine.getSystem().getVariableName(i), +// currentConcreteState.get().getVariables()[i].getValue(BigDecimal.ZERO)); +// } + + // As the clocks values starts after the variables values in currentConcreteState.get().getVariables() + // Then i needs to start where the variables ends. + // j is needed to map the correct name with the value +// for (int i = noOfVars, j = 0; i < noOfClocks + noOfVars ; i++, j++) { +// simulationClocks.put(engine.getSystem().getClockName(j), +// currentConcreteState.get().getVariables()[i].getValue(BigDecimal.ZERO)); +// } + } + + /** + * Getter for the current concrete state + * + * @return the current {@link SimulationState} + */ + public SimulationState getCurrentState() { + return currentConcreteState.get(); + } + + /** + * The way to get the time in the current state of a simulation + * + * @return the time in the current state + */ + public BigDecimal getCurrentTime() { + return currentTime.get(); + } + + public ObjectProperty currentTimeProperty() { + return currentTime; + } + + /** + * The way to get the delay of the latest step in the simulation + * + * @return the delay of the latest step in the in the simulation + */ + public BigDecimal getDelay() { + return delay; + } + + /** + * The number of total steps taken in the current simulation + * + * @return the number of steps + */ + public int getNumberOfSteps() { + return numberOfSteps; + } + + /** + * All the transitions taken in this simulation + * + * @return an {@link ObservableList} of all the transitions taken in this simulation so far + */ + public ObservableList getTraceLog() { + return traceLog; + } + + /** + * All the available transitions in this state + * + * @return an {@link ObservableList} of all the currently available transitions in this state + */ + public ObservableList getAvailableTransitions() { + return availableTransitions; + } + + /** + * All the variables connected to the current simulation. + * This does not return any clocks, if you need please use {@link SimulationHandler#getSimulationClocks()} instead + * + * @return a {@link Map} where the name (String) is the key, and a {@link BigDecimal} is the value + */ + public ObservableMap getSimulationVariables() { + return simulationVariables; + } + + /** + * All the clocks connected to the current simulation. + * + * @return a {@link Map} where the name (String) is the key, and a {@link BigDecimal} is the clock value + * @see SimulationHandler#getSimulationVariables() + */ + public ObservableMap getSimulationClocks() { + return simulationClocks; + } + + /** + * The initial state of the current simulation + * + * @return the initial {@link SimulationState} of this simulation + */ + public SimulationState getInitialConcreteState() { + return Ecdar.getBackendDriver().getInitialSimulationState(); + } + + public ObjectProperty initialConcreteStateProperty() { + return initialConcreteState; + } + + /** + * Prints all available transitions to {@link System#out}. + * This is very useful for debugging. + * If a string representation is needed please use {@link SimulationHandler#getAvailableTransitionsAsStrings()} + * instead. + */ + public void printAvailableTransitions() { + System.out.println("---------------------------------"); + + System.out.println(numberOfSteps + " Successor state " + currentConcreteState.toString() + " Entry time " + currentTime); + System.out.print("Available transitions: "); + availableTransitions.forEach( + Transition -> System.out.println(Transition.getLabel() + " ")); + + if (!availableTransitions.isEmpty()) { + for (int i = 0; i < availableTransitions.get(0).getEdges().size(); i++) { + // ToDo: Implement +// System.out.println("Edges: " + +// availableTransitions.get(0).getEdges().get(i).getEdge().getSource().getPropertyValue("name") + +// "." + availableTransitions.get(0).getEdges().get(i).getName() + " --> " + +// availableTransitions.get(0).getEdges().get(i).getEdge().getTarget().getPropertyValue("name")); + } + } + + System.out.println("---------------------------------"); + } + + /** + * To get all available transitions as strings + * + * @return an ArrayList of all the enabled transitions + */ + public ArrayList getAvailableTransitionsAsStrings() { + final ArrayList transitions = new ArrayList<>(); + for (final Transition Transition : availableTransitions) { + transitions.add(Transition.getLabel()); + } + return transitions; + } + + public EcdarSystem getSystem() { + return system; + } + + public String getCurrentSimulation() { + return currentSimulation; + } + + public boolean isSimulationRunning() { + return false; // ToDo NIELS: Handle logic for determining whether a simulation is currently running + } +} \ No newline at end of file diff --git a/src/main/java/ecdar/simulation/SimulationState.java b/src/main/java/ecdar/simulation/SimulationState.java new file mode 100644 index 00000000..f246ce5a --- /dev/null +++ b/src/main/java/ecdar/simulation/SimulationState.java @@ -0,0 +1,52 @@ +package ecdar.simulation; + +import EcdarProtoBuf.ObjectProtos; +import ecdar.abstractions.Location; +import javafx.util.Pair; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.stream.Collectors; + +public class SimulationState { + private final ArrayList> locations; + + public SimulationState(ObjectProtos.StateTuple protoBufState) { + if (protoBufState != null) locations = protoBufState.getLocationsList().stream().map(lt -> new Pair<>(lt.getComponentName(), lt.getId())).collect(Collectors.toCollection(ArrayList::new)); + else locations = new ArrayList<>(); + // ToDo: Handle federations + } + + public void setTime(BigDecimal value) { + // ToDo: Implement + } + + public Number getTime() { + // ToDo: Implement + return new Number() { + @Override + public int intValue() { + return 0; + } + + @Override + public long longValue() { + return 0; + } + + @Override + public float floatValue() { + return 0; + } + + @Override + public double doubleValue() { + return 0; + } + }; + } + + public ArrayList> getLocations() { + return locations; + } +} diff --git a/src/main/java/ecdar/simulation/SimulationStateSuccessor.java b/src/main/java/ecdar/simulation/SimulationStateSuccessor.java new file mode 100644 index 00000000..1b0c4cba --- /dev/null +++ b/src/main/java/ecdar/simulation/SimulationStateSuccessor.java @@ -0,0 +1,17 @@ +package ecdar.simulation; + +import ecdar.Ecdar; + +import java.util.ArrayList; + +public class SimulationStateSuccessor { + public ArrayList getTransitions() { + // ToDo: Implement + return new ArrayList<>(); + } + + public SimulationState getState() { + // ToDo: Implement + return Ecdar.getBackendDriver().getInitialSimulationState(); + } +} diff --git a/src/main/java/ecdar/simulation/Transition.java b/src/main/java/ecdar/simulation/Transition.java new file mode 100644 index 00000000..da5bac14 --- /dev/null +++ b/src/main/java/ecdar/simulation/Transition.java @@ -0,0 +1,17 @@ +package ecdar.simulation; + +import ecdar.abstractions.Edge; + +import java.util.ArrayList; + +public class Transition { + public String getLabel() { + // ToDo: Implement + return "Transition label"; + } + + public ArrayList getEdges() { + // ToDo: Implement + return new ArrayList<>(); + } +} diff --git a/src/main/java/ecdar/utility/helpers/BindingHelper.java b/src/main/java/ecdar/utility/helpers/BindingHelper.java index afbddec2..89d1a5e6 100644 --- a/src/main/java/ecdar/utility/helpers/BindingHelper.java +++ b/src/main/java/ecdar/utility/helpers/BindingHelper.java @@ -5,6 +5,7 @@ import ecdar.model_canvas.arrow_heads.ChannelReceiverArrowHead; import ecdar.model_canvas.arrow_heads.ChannelSenderArrowHead; import ecdar.presentations.Link; +import ecdar.presentations.SimTagPresentation; import ecdar.presentations.TagPresentation; import ecdar.utility.mouse.MouseTracker; import javafx.beans.binding.DoubleBinding; @@ -24,6 +25,15 @@ public static void bind(final Line subject, final TagPresentation target) { subject.visibleProperty().bind(target.visibleProperty()); } + public static void bind(final Line subject, final SimTagPresentation target) { + subject.startXProperty().set(0); + subject.startYProperty().set(0); + subject.endXProperty().bind(target.translateXProperty().add(target.minWidthProperty().divide(2))); + subject.endYProperty().bind(target.translateYProperty().add(target.heightProperty().divide(2))); + subject.opacityProperty().bind(target.opacityProperty()); + subject.visibleProperty().bind(target.visibleProperty()); + } + public static void bind(final Circular subject, final ObservableDoubleValue x, final ObservableDoubleValue y) { subject.xProperty().bind(EcdarController.getActiveCanvasPresentation().mouseTracker.gridXProperty().subtract(x)); subject.yProperty().bind(EcdarController.getActiveCanvasPresentation().mouseTracker.gridYProperty().subtract(y)); diff --git a/src/main/resources/ecdar/main.css b/src/main/resources/ecdar/main.css index 67af8bb5..0d99400f 100644 --- a/src/main/resources/ecdar/main.css +++ b/src/main/resources/ecdar/main.css @@ -290,6 +290,24 @@ -fx-font-size: 1em; } +.large-toggle-button { + -jfx-toggle-color:#dddddd; -jfx-untoggle-color:#dddddd; + -jfx-toggle-line-color:#7C8B92; -jfx-untoggle-line-color:#7C8B92; + -fx-opacity: 0.5; + -jfx-size: 1.6em; + -fx-font-size: 1em; +} + +.editor-simulator-toggle { + -fx-min-width: 8em; + -fx-max-width: 8em; + -fx-min-height: 4em; + -fx-max-height: 4em; + -fx-background-color: -blue-grey-800; + -fx-border-radius: 0 0 10 10; + -fx-background-radius: 0 0 10 10; +} + /* Response sizing for elements (icons with these classes will be scaled when scaling changes) */ .icon-size-medium { -fx-font-family: "Material Icons Regular"; @@ -347,4 +365,4 @@ .responsive-circle-radius { -fx-scale-x: 1em; -fx-scale-y: 1em; -} \ No newline at end of file +} diff --git a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml index c7032ddc..dc5c1a14 100644 --- a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml +++ b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml @@ -18,90 +18,17 @@ fx:controller="ecdar.controllers.EcdarController">

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
- - - + - - - + @@ -116,7 +43,8 @@ - + @@ -168,12 +96,14 @@ - + - + @@ -184,13 +114,14 @@ - + - + @@ -205,7 +136,7 @@ - + @@ -245,6 +176,19 @@ + + + + + + + + + + + + @@ -292,6 +236,21 @@ + + + + + + + + + + + + + + + @@ -352,8 +311,8 @@ - - + + @@ -535,4 +494,9 @@ + + + + + diff --git a/src/main/resources/ecdar/presentations/EditorPresentation.fxml b/src/main/resources/ecdar/presentations/EditorPresentation.fxml new file mode 100644 index 00000000..bf3faac1 --- /dev/null +++ b/src/main/resources/ecdar/presentations/EditorPresentation.fxml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ecdar/presentations/LeftSimPanePresentation.fxml b/src/main/resources/ecdar/presentations/LeftSimPanePresentation.fxml new file mode 100755 index 00000000..e80df29e --- /dev/null +++ b/src/main/resources/ecdar/presentations/LeftSimPanePresentation.fxml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ecdar/presentations/ProcessPresentation.fxml b/src/main/resources/ecdar/presentations/ProcessPresentation.fxml new file mode 100755 index 00000000..6b4b8f77 --- /dev/null +++ b/src/main/resources/ecdar/presentations/ProcessPresentation.fxml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + + + + +
+
+
+ +
+
+ + + + + + +
+
+
\ No newline at end of file diff --git a/src/main/resources/ecdar/presentations/ProjectPanePresentation.fxml b/src/main/resources/ecdar/presentations/ProjectPanePresentation.fxml index 342405e6..738311fe 100644 --- a/src/main/resources/ecdar/presentations/ProjectPanePresentation.fxml +++ b/src/main/resources/ecdar/presentations/ProjectPanePresentation.fxml @@ -10,7 +10,8 @@ + fx:controller="ecdar.controllers.ProjectPaneController" + StackPane.alignment="TOP_LEFT"> diff --git a/src/main/resources/ecdar/presentations/QueryPanePresentation.fxml b/src/main/resources/ecdar/presentations/QueryPanePresentation.fxml index 6a984c1b..e50fc7bd 100644 --- a/src/main/resources/ecdar/presentations/QueryPanePresentation.fxml +++ b/src/main/resources/ecdar/presentations/QueryPanePresentation.fxml @@ -9,7 +9,8 @@ + fx:controller="ecdar.controllers.QueryPaneController" + StackPane.alignment="TOP_RIGHT"> diff --git a/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml b/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml new file mode 100755 index 00000000..6f3d588c --- /dev/null +++ b/src/main/resources/ecdar/presentations/RightSimPanePresentation.fxml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + diff --git a/src/main/resources/ecdar/presentations/SimEdgePresentation.fxml b/src/main/resources/ecdar/presentations/SimEdgePresentation.fxml new file mode 100755 index 00000000..1c72435f --- /dev/null +++ b/src/main/resources/ecdar/presentations/SimEdgePresentation.fxml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/ecdar/presentations/SimLocationPresentation.fxml b/src/main/resources/ecdar/presentations/SimLocationPresentation.fxml new file mode 100755 index 00000000..eb6b4543 --- /dev/null +++ b/src/main/resources/ecdar/presentations/SimLocationPresentation.fxml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/ecdar/presentations/SimNailPresentation.fxml b/src/main/resources/ecdar/presentations/SimNailPresentation.fxml new file mode 100755 index 00000000..17a63895 --- /dev/null +++ b/src/main/resources/ecdar/presentations/SimNailPresentation.fxml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/ecdar/presentations/SimTagPresentation.fxml b/src/main/resources/ecdar/presentations/SimTagPresentation.fxml new file mode 100755 index 00000000..f9e40d71 --- /dev/null +++ b/src/main/resources/ecdar/presentations/SimTagPresentation.fxml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml b/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml new file mode 100644 index 00000000..332e082d --- /dev/null +++ b/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + Simulation Initialization + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ecdar/presentations/SimulatorOverviewPresentation.fxml b/src/main/resources/ecdar/presentations/SimulatorOverviewPresentation.fxml new file mode 100755 index 00000000..f854ef33 --- /dev/null +++ b/src/main/resources/ecdar/presentations/SimulatorOverviewPresentation.fxml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/src/main/resources/ecdar/presentations/SimulatorPresentation.fxml b/src/main/resources/ecdar/presentations/SimulatorPresentation.fxml new file mode 100755 index 00000000..7524a380 --- /dev/null +++ b/src/main/resources/ecdar/presentations/SimulatorPresentation.fxml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + diff --git a/src/main/resources/ecdar/presentations/TracePaneElementPresentation.fxml b/src/main/resources/ecdar/presentations/TracePaneElementPresentation.fxml new file mode 100755 index 00000000..0b83620e --- /dev/null +++ b/src/main/resources/ecdar/presentations/TracePaneElementPresentation.fxml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ecdar/presentations/TransitionPaneElementPresentation.fxml b/src/main/resources/ecdar/presentations/TransitionPaneElementPresentation.fxml new file mode 100755 index 00000000..b9b58ff9 --- /dev/null +++ b/src/main/resources/ecdar/presentations/TransitionPaneElementPresentation.fxml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ecdar/presentations/TransitionPresentation.fxml b/src/main/resources/ecdar/presentations/TransitionPresentation.fxml new file mode 100755 index 00000000..3fa1c5cf --- /dev/null +++ b/src/main/resources/ecdar/presentations/TransitionPresentation.fxml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + diff --git a/src/test/java/ecdar/TestFXBase.java b/src/test/java/ecdar/TestFXBase.java new file mode 100644 index 00000000..e54eecec --- /dev/null +++ b/src/test/java/ecdar/TestFXBase.java @@ -0,0 +1,30 @@ +package ecdar; + +import javafx.scene.input.KeyCode; +import javafx.scene.input.MouseButton; +import javafx.stage.Stage; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.testfx.api.FxToolkit; +import org.testfx.framework.junit.ApplicationTest; + +import java.util.concurrent.TimeoutException; + +public class TestFXBase extends ApplicationTest { + @BeforeAll + static void setUp() throws Exception { + ApplicationTest.launch(Ecdar.class); + } + + @Override + public void start(Stage stage) { + stage.show(); + } + + @AfterAll + public void afterEachTest() throws TimeoutException { + FxToolkit.hideStage(); + release(new KeyCode[]{}); + release(new MouseButton[]{}); + } +} diff --git a/src/test/java/ecdar/simulation/SimulationTest.java b/src/test/java/ecdar/simulation/SimulationTest.java new file mode 100644 index 00000000..2acb8136 --- /dev/null +++ b/src/test/java/ecdar/simulation/SimulationTest.java @@ -0,0 +1,101 @@ +package ecdar.simulation; + +import EcdarProtoBuf.EcdarBackendGrpc; +import EcdarProtoBuf.ObjectProtos; +import EcdarProtoBuf.QueryProtos; +import ecdar.TestFXBase; +import ecdar.abstractions.Component; +import ecdar.abstractions.Location; +import io.grpc.BindableService; +import io.grpc.ManagedChannel; +import io.grpc.Server; +import io.grpc.Status; +import io.grpc.inprocess.InProcessChannelBuilder; +import io.grpc.inprocess.InProcessServerBuilder; +import io.grpc.stub.StreamObserver; +import io.grpc.testing.GrpcCleanupRule; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; + +public class SimulationTest extends TestFXBase { + public GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); + private final String serverName = InProcessServerBuilder.generateName(); + + @Test + public void testGetInitialStateHighlightsTheInitialLocation() { + final List components = generateComponentsWithInitialLocations(); + + BindableService testService = new EcdarBackendGrpc.EcdarBackendImplBase() { + @Override + public void startSimulation(QueryProtos.SimulationStartRequest request, + StreamObserver responseObserver) { + try { + ObjectProtos.StateTuple state = ObjectProtos.StateTuple.newBuilder().addAllLocations(components.stream() + .map(c -> ObjectProtos.StateTuple.LocationTuple.newBuilder() + .setComponentName(c.getName()) + .setId(c.getInitialLocation().getId()) + .build()) + .collect(Collectors.toList())).build(); + + QueryProtos.SimulationStepResponse response = QueryProtos.SimulationStepResponse.newBuilder().setState(state).build(); + responseObserver.onNext(response); + responseObserver.onCompleted(); + } catch (Throwable e) { + responseObserver.onError(Status.INVALID_ARGUMENT.withDescription(e.getMessage()).asException()); + } + } + + @Override + public void takeSimulationStep(EcdarProtoBuf.QueryProtos.SimulationStepRequest request, + io.grpc.stub.StreamObserver responseObserver) { + } + }; + + final Server server; + final ManagedChannel channel; + final EcdarBackendGrpc.EcdarBackendBlockingStub stub; + try { + server = grpcCleanup.register(InProcessServerBuilder + .forName(serverName).directExecutor().addService(testService).build().start()); + channel = grpcCleanup.register(InProcessChannelBuilder + .forName(serverName).directExecutor().build()); + stub = EcdarBackendGrpc.newBlockingStub(channel); + QueryProtos.SimulationStartRequest request = QueryProtos.SimulationStartRequest.newBuilder().setSystem("(A || B)").build(); + + var expectedResponse = new ObjectProtos.StateTuple.LocationTuple[components.size()]; + + for (int i = 0; i < components.size(); i++) { + Component comp = components.get(i); + expectedResponse[i] = ObjectProtos.StateTuple.LocationTuple.newBuilder() + .setComponentName(comp.getName()) + .setId(comp.getInitialLocation().getId()).build(); + } + + var result = stub.startSimulation(request).getState().getLocationsList().toArray(); + + Assertions.assertArrayEquals(expectedResponse, result); + } catch (IOException e) { + Assertions.fail("Exception encountered: " + e.getMessage()); + } + } + + private List generateComponentsWithInitialLocations() { + List comps = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + var comp = new Component(); + comp.setName(comp + "_" + i); + var loc = new Location(comp + "_initial"); + loc.setType(Location.Type.INITIAL); + comp.addLocation(loc); + comps.add(comp); + } + + return comps; + } +} From e46356bf915b93348b0dc7789ddb81c84bebabe4 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Thu, 13 Oct 2022 10:44:15 +0200 Subject: [PATCH 029/120] WIP: ''Fix'' to allow for compilation --- src/main/java/ecdar/abstractions/Transition.java | 15 +-------------- .../java/ecdar/simulation/SimulationHandler.java | 2 +- .../java/ecdar/simulation/SimulationState.java | 5 ++--- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Transition.java b/src/main/java/ecdar/abstractions/Transition.java index 9b9d25be..e7b9817a 100644 --- a/src/main/java/ecdar/abstractions/Transition.java +++ b/src/main/java/ecdar/abstractions/Transition.java @@ -12,19 +12,6 @@ public class Transition { public final ArrayList edges = new ArrayList<>(); public Transition(ObjectProtos.Transition protoBufTransition) { - List protoBufEdges = (List) protoBufTransition.getField(ObjectProtos.Transition.getDescriptor().findFieldByName("edges")); - List componentNames = protoBufEdges.stream().map(ObjectProtos.Transition.EdgeTuple::getComponentName).collect(Collectors.toList()); - List edgeIds = protoBufEdges.stream().map(ObjectProtos.Transition.EdgeTuple::getId).collect(Collectors.toList()); - - // For each affected component, find each affected edge within that component, and add these edges to the edges list - List affectedComponents = Ecdar.getProject().getComponents().stream().filter(c -> componentNames.contains(c.getName())).collect(Collectors.toList()); - edges.addAll(affectedComponents.stream() - .map(c -> c.getEdges() - .stream() - .filter(e -> edgeIds.contains(e.getId())) - .collect(Collectors.toList())) - .flatMap(Collection::stream) - .collect(Collectors.toList()) - ); + // ToDo: Construct transition instance based on protoBuf input } } diff --git a/src/main/java/ecdar/simulation/SimulationHandler.java b/src/main/java/ecdar/simulation/SimulationHandler.java index bb894b62..a37b0712 100755 --- a/src/main/java/ecdar/simulation/SimulationHandler.java +++ b/src/main/java/ecdar/simulation/SimulationHandler.java @@ -109,7 +109,7 @@ public void initialStep() { * elements in the {@link SimulationHandler#traceLog}. Otherwise it calls {@link SimulationHandler#initialStep} */ public void resetToInitialLocation() { - //If the simulation has not begone + //If the simulation has not begun if (traceLog.size() == 0) initialStep(); else diff --git a/src/main/java/ecdar/simulation/SimulationState.java b/src/main/java/ecdar/simulation/SimulationState.java index f246ce5a..5a4f2575 100644 --- a/src/main/java/ecdar/simulation/SimulationState.java +++ b/src/main/java/ecdar/simulation/SimulationState.java @@ -12,9 +12,8 @@ public class SimulationState { private final ArrayList> locations; public SimulationState(ObjectProtos.StateTuple protoBufState) { - if (protoBufState != null) locations = protoBufState.getLocationsList().stream().map(lt -> new Pair<>(lt.getComponentName(), lt.getId())).collect(Collectors.toCollection(ArrayList::new)); - else locations = new ArrayList<>(); - // ToDo: Handle federations + locations = new ArrayList<>(); + // ToDo: Initialize with correct locations from protoBuf response } public void setTime(BigDecimal value) { From c2634a3acfdae5f61eb470e93e0fb218510a1192 Mon Sep 17 00:00:00 2001 From: Emilie Steinmann Date: Thu, 13 Oct 2022 11:09:39 +0200 Subject: [PATCH 030/120] align with condition for disabling action button --- src/main/java/ecdar/presentations/QueryPresentation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ecdar/presentations/QueryPresentation.java b/src/main/java/ecdar/presentations/QueryPresentation.java index 48d49609..a864b553 100644 --- a/src/main/java/ecdar/presentations/QueryPresentation.java +++ b/src/main/java/ecdar/presentations/QueryPresentation.java @@ -73,7 +73,7 @@ private void initializeTextFields() { queryTextField.setOnKeyPressed(EcdarController.getActiveCanvasPresentation().getController().getLeaveTextAreaKeyHandler(keyEvent -> { Platform.runLater(() -> { - if (keyEvent.getCode().equals(KeyCode.ENTER) && controller.getQuery().getType().getQueryName() != null) { + if (keyEvent.getCode().equals(KeyCode.ENTER) && controller.getQuery().getType() != null) { runQuery(); } }); From a824223fa1f1dc886eb7277296607631b95ec13e Mon Sep 17 00:00:00 2001 From: "Julie H. Bengtsson" <82820935+jhbengtsson@users.noreply.github.com> Date: Thu, 13 Oct 2022 11:42:16 +0200 Subject: [PATCH 031/120] Hide generated component tab if there are no generated components (#31) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Nail location on all zoom levels not working FIXED * Print statement removed * if statement der sætter visible til true eller false * Arrows changed * forkortet Co-authored-by: Niels Vistisen Co-authored-by: Niels F. S. Vistisen <42961494+Nielswps@users.noreply.github.com> Co-authored-by: Dolmer1 --- src/main/java/ecdar/controllers/EdgeController.java | 4 ++-- .../java/ecdar/controllers/ProjectPaneController.java | 8 +++++--- .../ecdar/presentations/ProjectPanePresentation.fxml | 6 +++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/ecdar/controllers/EdgeController.java b/src/main/java/ecdar/controllers/EdgeController.java index 354c13e4..3610934f 100644 --- a/src/main/java/ecdar/controllers/EdgeController.java +++ b/src/main/java/ecdar/controllers/EdgeController.java @@ -595,8 +595,8 @@ private void addEdgePropertyRow(final DropDownMenu dropDownMenu, final String ro } private Nail getNewNailBasedOnDropdownPosition() { - final double nailX = Math.round(DropDownMenu.x / GRID_SIZE) * GRID_SIZE; - final double nailY = Math.round(DropDownMenu.y / GRID_SIZE) * GRID_SIZE; + final double nailX = Math.round(DropDownMenu.x / EcdarController.getActiveCanvasZoomFactor().get() / GRID_SIZE) * GRID_SIZE; + final double nailY = Math.round(DropDownMenu.y / EcdarController.getActiveCanvasZoomFactor().get() / GRID_SIZE) * GRID_SIZE; return new Nail(nailX, nailY); } diff --git a/src/main/java/ecdar/controllers/ProjectPaneController.java b/src/main/java/ecdar/controllers/ProjectPaneController.java index c80bcbc1..12ff4072 100644 --- a/src/main/java/ecdar/controllers/ProjectPaneController.java +++ b/src/main/java/ecdar/controllers/ProjectPaneController.java @@ -83,6 +83,8 @@ public void onChanged(final Change c) { c.getAddedSubList().forEach(this::handleAddedModel); c.getRemoved().forEach(this::handleRemovedModel); + generatedComponentsDivider.setVisible(!tempFilesList.getChildren().isEmpty()); + // Sort the children alphabetically sortPresentations(); } @@ -390,12 +392,12 @@ private void createSystemClicked() { */ @FXML private void setGeneratedComponentsVisibilityButtonClicked() { - if (generatedComponentsVisibilityButtonIcon.getIconCode() == Material.EXPAND_MORE) { - generatedComponentsVisibilityButtonIcon.setIconCode(Material.EXPAND_LESS); + if (generatedComponentsVisibilityButtonIcon.getIconCode() == Material.ARROW_LEFT) { + generatedComponentsVisibilityButtonIcon.setIconCode(Material.ARROW_DROP_DOWN); this.tempFilesList.setVisible(true); this.tempFilesList.setManaged(true); } else { - generatedComponentsVisibilityButtonIcon.setIconCode(Material.EXPAND_MORE); + generatedComponentsVisibilityButtonIcon.setIconCode(Material.ARROW_LEFT); this.tempFilesList.setVisible(false); this.tempFilesList.setManaged(false); } diff --git a/src/main/resources/ecdar/presentations/ProjectPanePresentation.fxml b/src/main/resources/ecdar/presentations/ProjectPanePresentation.fxml index 342405e6..f6b10689 100644 --- a/src/main/resources/ecdar/presentations/ProjectPanePresentation.fxml +++ b/src/main/resources/ecdar/presentations/ProjectPanePresentation.fxml @@ -56,7 +56,7 @@ - + @@ -68,8 +68,8 @@ - + From d92fe7de60741689807c7345be43c8c2e4f1cbab Mon Sep 17 00:00:00 2001 From: WassawRoki <56611129+WassawRoki@users.noreply.github.com> Date: Tue, 25 Oct 2022 09:54:00 +0200 Subject: [PATCH 032/120] first try --- src/main/java/ecdar/presentations/DropDownMenu.java | 1 + .../java/ecdar/presentations/QueryPresentation.java | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/ecdar/presentations/DropDownMenu.java b/src/main/java/ecdar/presentations/DropDownMenu.java index a631b7ff..0fb91c8f 100644 --- a/src/main/java/ecdar/presentations/DropDownMenu.java +++ b/src/main/java/ecdar/presentations/DropDownMenu.java @@ -90,6 +90,7 @@ public DropDownMenu(final Node src) { * @param width The width of the {@link DropDownMenu} */ public DropDownMenu(final Node src, final int width) { + dropDownMenuWidth.set(width); list = new VBox(); list.setStyle("-fx-background-color: white; -fx-padding: 8 0 8 0;"); diff --git a/src/main/java/ecdar/presentations/QueryPresentation.java b/src/main/java/ecdar/presentations/QueryPresentation.java index db1d9179..df680307 100644 --- a/src/main/java/ecdar/presentations/QueryPresentation.java +++ b/src/main/java/ecdar/presentations/QueryPresentation.java @@ -15,6 +15,7 @@ import javafx.geometry.Pos; import javafx.scene.Cursor; import javafx.scene.control.Label; +import javafx.scene.control.ScrollPane; import javafx.scene.control.TitledPane; import javafx.scene.control.Tooltip; import javafx.scene.input.KeyCode; @@ -23,6 +24,7 @@ import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.material.Material; +import javax.swing.*; import java.util.Map; import java.util.Set; import java.util.function.Consumer; @@ -252,7 +254,7 @@ private void initializeStateIndicator() { statusIcon.setOnMouseClicked(event -> { if (controller.getQuery().getQuery().isEmpty()) return; - + Label label = new Label(tooltip.getText()); JFXDialog dialog = new InformationDialogPresentation("Result from query: " + controller.getQuery().getQuery(), label); dialog.show(Ecdar.getPresentation()); @@ -469,16 +471,20 @@ private void initializeMoreInformationButtonAndQueryTypeSymbol() { controller.queryTypeExpand.setRipplerFill(Color.GREY_BLUE.getColor(Color.Intensity.I500)); final DropDownMenu queryTypeDropDown = new DropDownMenu(controller.queryTypeExpand); - queryTypeDropDown.addListElement("Query Type"); QueryType[] queryTypes = QueryType.values(); for (QueryType type : queryTypes) { addQueryTypeListElement(type, queryTypeDropDown); + } controller.queryTypeExpand.setOnMousePressed((e) -> { e.consume(); - queryTypeDropDown.show(JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, 16, 16); + + double Y=this.getLayoutY(); + System.out.println(Y); + if(Y >= 500){queryTypeDropDown.show(JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, -this.getWidth()/3, -(this.getLayoutY()-this.getHeight()-300));} + else {queryTypeDropDown.show(JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, 16, 16);} }); controller.queryTypeSymbol.setText(controller.getQuery() != null && controller.getQuery().getType() != null ? controller.getQuery().getType().getSymbol() : "---"); From 9dcbd83d18418b6ebfd82e18c36879fb66f8dbf6 Mon Sep 17 00:00:00 2001 From: APaludan Date: Tue, 25 Oct 2022 20:17:49 +0200 Subject: [PATCH 033/120] Update .gitmodules --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 217c921a..2e15cc85 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "src/main/proto"] path = src/main/proto - url = https://github.com/Ecdar/Ecdar-ProtoBuf.git + url = https://github.com/Ecdar-SW5/Ecdar-ProtoBuf.git From bcc3fcef6df7c94776ff771c1e197b032b5bb8a7 Mon Sep 17 00:00:00 2001 From: APaludan Date: Tue, 25 Oct 2022 21:53:41 +0200 Subject: [PATCH 034/120] send query requests with new grpc protocol --- .../java/ecdar/abstractions/Transition.java | 2 +- .../java/ecdar/backend/BackendDriver.java | 140 ++++++++++-------- .../backend/IgnoredInputOutputQuery.java | 4 +- .../ecdar/simulation/SimulationState.java | 2 +- src/main/proto | 2 +- 5 files changed, 81 insertions(+), 69 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Transition.java b/src/main/java/ecdar/abstractions/Transition.java index e7b9817a..9aa71eeb 100644 --- a/src/main/java/ecdar/abstractions/Transition.java +++ b/src/main/java/ecdar/abstractions/Transition.java @@ -11,7 +11,7 @@ public class Transition { public final ArrayList edges = new ArrayList<>(); - public Transition(ObjectProtos.Transition protoBufTransition) { + public Transition(ObjectProtos protoBufTransition) { // ToDo: Construct transition instance based on protoBuf input } } diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index 36418b39..3730812b 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -85,19 +85,22 @@ public void run() { return; } - QueryProtos.ComponentsUpdateRequest.Builder componentsBuilder = QueryProtos.ComponentsUpdateRequest.newBuilder(); + ComponentProtos.ComponentsInfo.Builder componentsInfoBuilder = ComponentProtos.ComponentsInfo.newBuilder(); for (Component c : Ecdar.getProject().getComponents()) { - componentsBuilder.addComponents(ComponentProtos.Component.newBuilder().setJson(c.serialize().toString()).build()); + componentsInfoBuilder.addComponents(ComponentProtos.Component.newBuilder().setJson(c.serialize().toString()).build()); } executeGrpcRequest(query.getQuery().getQuery(), backendConnection, - componentsBuilder, + componentsInfoBuilder, QueryProtos.IgnoredInputOutputs.newBuilder().getDefaultInstanceForType(), (response) -> { - if (response.hasQuery() && response.getQuery().hasIgnoredInputOutputs()) { - var ignoredInputOutputs = response.getQuery().getIgnoredInputOutputs(); - query.addNewElementsToMap(new ArrayList<>(ignoredInputOutputs.getIgnoredInputsList()), new ArrayList<>(ignoredInputOutputs.getIgnoredOutputsList())); + // ToDo ANDREAS: Spørg niels om hvad ignored inputs og outputs er + System.out.println(query.ignoredInputs); + System.out.println(query.ignoredOutputs); + if (response.hasQueryOk() && query.ignoredInputs != null && query.ignoredOutputs != null) { +// var ignoredInputOutputs = response.getQuery().getIgnoredInputOutputs(); + query.addNewElementsToMap(new ArrayList<>(query.ignoredInputs.keySet()), new ArrayList<>(query.ignoredOutputs.keySet())); } else { // Response is unexpected, maybe just ignore } @@ -121,28 +124,31 @@ public void closeAllBackendConnections() throws IOException { private void executeQuery(ExecutableQuery executableQuery, BackendConnection backendConnection) { if (executableQuery.queryListener.getQuery().getQueryState() == QueryState.UNKNOWN) return; - QueryProtos.ComponentsUpdateRequest.Builder componentsBuilder = QueryProtos.ComponentsUpdateRequest.newBuilder(); + ComponentProtos.ComponentsInfo.Builder componentsInfoBuilder = ComponentProtos.ComponentsInfo.newBuilder(); for (Component c : Ecdar.getProject().getComponents()) { - componentsBuilder.addComponents(ComponentProtos.Component.newBuilder().setJson(c.serialize().toString()).build()); + if (executableQuery.query.contains(c.getName())) { + componentsInfoBuilder.addComponents(ComponentProtos.Component.newBuilder().setJson(c.serialize().toString()).build()); + } } + componentsInfoBuilder.setComponentsHash(componentsInfoBuilder.getComponentsList().hashCode()); - executeGrpcRequest(executableQuery, backendConnection, componentsBuilder); + executeGrpcRequest(executableQuery, backendConnection, componentsInfoBuilder); } /** * Executes the specified query as a gRPC request using the specified backend connection. - * componentsBuilder is used to update the components of the engine. + * componentsInfoBuilder is used to update the components of the engine. * * @param executableQuery executable query to be executed by the backend * @param backendConnection connection to the backend - * @param componentsBuilder components builder containing the components relevant to the query execution + * @param componentsInfoBuilder components builder containing the components relevant to the query execution */ private void executeGrpcRequest(ExecutableQuery executableQuery, BackendConnection backendConnection, - QueryProtos.ComponentsUpdateRequest.Builder componentsBuilder) { + ComponentProtos.ComponentsInfo.Builder componentsInfoBuilder) { executeGrpcRequest(executableQuery.query, backendConnection, - componentsBuilder, + componentsInfoBuilder, null, (response) -> handleQueryResponse(response, executableQuery), (error) -> handleQueryBackendError(error, executableQuery) @@ -151,13 +157,13 @@ private void executeGrpcRequest(ExecutableQuery executableQuery, /** * Executes the specified query as a gRPC request using the specified backend connection. - * componentsBuilder is used to update the components of the engine and on completion of this transaction, + * componentsInfoBuilder is used to update the components of the engine and on completion of this transaction, * the query is sent and its response is consumed by responseConsumer. Any error encountered is handled by * the errorConsumer. * * @param query query to be executed by the backend * @param backendConnection connection to the backend - * @param componentsBuilder components builder containing the components relevant to the query execution + * @param componentsInfoBuilder components builder containing the components relevant to the query execution * @param protoBufIgnoredInputOutputs ProtoBuf object containing the inputs and outputs that should be ignored * (can be null) * @param responseConsumer consumer for handling the received response @@ -165,13 +171,14 @@ private void executeGrpcRequest(ExecutableQuery executableQuery, */ private void executeGrpcRequest(String query, BackendConnection backendConnection, - QueryProtos.ComponentsUpdateRequest.Builder componentsBuilder, + ComponentProtos.ComponentsInfo.Builder componentsInfoBuilder, QueryProtos.IgnoredInputOutputs protoBufIgnoredInputOutputs, Consumer responseConsumer, Consumer errorConsumer) { - StreamObserver observer = new StreamObserver<>() { + StreamObserver responseObserver = new StreamObserver<>() { @Override - public void onNext(Empty value) { + public void onNext(QueryProtos.QueryResponse value) { + responseConsumer.accept(value); } @Override @@ -182,38 +189,21 @@ public void onError(Throwable t) { @Override public void onCompleted() { - StreamObserver responseObserver = new StreamObserver<>() { - @Override - public void onNext(QueryProtos.QueryResponse value) { - responseConsumer.accept(value); - } - - @Override - public void onError(Throwable t) { - errorConsumer.accept(t); - addBackendConnection(backendConnection); - } - - @Override - public void onCompleted() { - addBackendConnection(backendConnection); - } - }; - - var queryBuilder = QueryProtos.Query.newBuilder() - .setId(0) - .setQuery(query); - - if (protoBufIgnoredInputOutputs != null) - queryBuilder.setIgnoredInputOutputs(protoBufIgnoredInputOutputs); - - backendConnection.getStub().withDeadlineAfter(deadlineForResponses, TimeUnit.MILLISECONDS) - .sendQuery(queryBuilder.build(), responseObserver); + addBackendConnection(backendConnection); } }; + QueryProtos.QueryRequest.Builder queryRequestBuilder = QueryProtos.QueryRequest.newBuilder() + .setUserId(1) + .setQueryId(1) + .setQuery(query) + .setComponentsInfo(componentsInfoBuilder); + + if (protoBufIgnoredInputOutputs != null) + queryRequestBuilder.setIgnoredInputOutputs(protoBufIgnoredInputOutputs); + backendConnection.getStub().withDeadlineAfter(deadlineForResponses, TimeUnit.MILLISECONDS) - .updateComponents(componentsBuilder.build(), observer); + .sendQuery(queryRequestBuilder.build(), responseObserver); } private void addBackendConnection(BackendConnection backendConnection) { @@ -317,24 +307,46 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, ExecutableQuer // If the query has been cancelled, ignore the result if (executableQuery.queryListener.getQuery().getQueryState() == QueryState.UNKNOWN) return; - if (value.hasRefinement() && value.getRefinement().getSuccess()) { - executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); - executableQuery.success.accept(true); - } else if (value.hasConsistency() && value.getConsistency().getSuccess()) { - executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); - executableQuery.success.accept(true); - } else if (value.hasDeterminism() && value.getDeterminism().getSuccess()) { - executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); - executableQuery.success.accept(true); - } else if (value.hasComponent()) { - executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); - executableQuery.success.accept(true); - JsonObject returnedComponent = (JsonObject) JsonParser.parseString(value.getComponent().getComponent().getJson()); - addGeneratedComponent(new Component(returnedComponent)); - } else { - executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); - executableQuery.success.accept(false); + + switch (value.getResponseCase()) { + case QUERY_OK: + executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); + executableQuery.success.accept(true); + if (value.getQueryOk().hasComponent()) { + JsonObject returnedComponent = (JsonObject) JsonParser.parseString(value.getQueryOk().getComponent().getComponent().getJson()); + addGeneratedComponent(new Component(returnedComponent)); + } + break; + + case USER_TOKEN_ERROR: + executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); + executableQuery.success.accept(false); + break; + + case RESPONSE_NOT_SET: + executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); + executableQuery.success.accept(false); + break; } + +// if (value.hasRefinement() && value.getRefinement().getSuccess()) { +// executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); +// executableQuery.success.accept(true); +// } else if (value.hasConsistency() && value.getConsistency().getSuccess()) { +// executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); +// executableQuery.success.accept(true); +// } else if (value.hasDeterminism() && value.getDeterminism().getSuccess()) { +// executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); +// executableQuery.success.accept(true); +// } else if (value.hasComponent()) { +// executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); +// executableQuery.success.accept(true); +// JsonObject returnedComponent = (JsonObject) JsonParser.parseString(value.getComponent().getComponent().getJson()); +// addGeneratedComponent(new Component(returnedComponent)); +// } else { +// executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); +// executableQuery.success.accept(false); +// } } private void handleQueryBackendError(Throwable t, ExecutableQuery executableQuery) { @@ -388,7 +400,7 @@ private void handleQueryBackendError(Throwable t, ExecutableQuery executableQuer } public SimulationState getInitialSimulationState() { - SimulationState state = new SimulationState(ObjectProtos.StateTuple.newBuilder().getDefaultInstanceForType()); + SimulationState state = new SimulationState(ObjectProtos.State.newBuilder().getDefaultInstanceForType()); state.getLocations().add(new Pair<>(Ecdar.getProject().getComponents().get(0).getName(), Ecdar.getProject().getComponents().get(0).getLocations().get(0).getId())); return state; } diff --git a/src/main/java/ecdar/backend/IgnoredInputOutputQuery.java b/src/main/java/ecdar/backend/IgnoredInputOutputQuery.java index 06843b31..6a351ce4 100644 --- a/src/main/java/ecdar/backend/IgnoredInputOutputQuery.java +++ b/src/main/java/ecdar/backend/IgnoredInputOutputQuery.java @@ -10,9 +10,9 @@ public class IgnoredInputOutputQuery { private final Query query; private final QueryPresentation queryPresentation; - private final HashMap ignoredInputs; + public final HashMap ignoredInputs; private final VBox ignoredInputsVBox; - private final HashMap ignoredOutputs; + public final HashMap ignoredOutputs; private final VBox ignoredOutputsVBox; public int tries = 0; diff --git a/src/main/java/ecdar/simulation/SimulationState.java b/src/main/java/ecdar/simulation/SimulationState.java index 5a4f2575..47144a9a 100644 --- a/src/main/java/ecdar/simulation/SimulationState.java +++ b/src/main/java/ecdar/simulation/SimulationState.java @@ -11,7 +11,7 @@ public class SimulationState { private final ArrayList> locations; - public SimulationState(ObjectProtos.StateTuple protoBufState) { + public SimulationState(ObjectProtos.State protoBufState) { locations = new ArrayList<>(); // ToDo: Initialize with correct locations from protoBuf response } diff --git a/src/main/proto b/src/main/proto index 476f1afd..c181269f 160000 --- a/src/main/proto +++ b/src/main/proto @@ -1 +1 @@ -Subproject commit 476f1afd62596ec6a841227a3caa5fd40abc9c6e +Subproject commit c181269f734c85ec51ae7e09de10cb685e54c595 From a8a9846676f996b50380abf63cfef69fdfda9843 Mon Sep 17 00:00:00 2001 From: APaludan Date: Thu, 27 Oct 2022 10:18:07 +0200 Subject: [PATCH 035/120] more work on grpc protocol --- .gitignore | 4 + .../java/ecdar/backend/BackendDriver.java | 102 +++++++++++++----- 2 files changed, 81 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index 938ad38a..977328e8 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,10 @@ lib/*.dll .idea/ /out/ +#VsCode +/bin/ +.vscode/ + ### Gradle ### .gradle /build/ diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index 3730812b..00c5dae6 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -4,6 +4,8 @@ import EcdarProtoBuf.EcdarBackendGrpc; import EcdarProtoBuf.ObjectProtos; import EcdarProtoBuf.QueryProtos; +import EcdarProtoBuf.QueryProtos.QueryResponse.QueryOk; + import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.protobuf.Empty; @@ -307,19 +309,88 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, ExecutableQuer // If the query has been cancelled, ignore the result if (executableQuery.queryListener.getQuery().getQueryState() == QueryState.UNKNOWN) return; - switch (value.getResponseCase()) { case QUERY_OK: - executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); - executableQuery.success.accept(true); - if (value.getQueryOk().hasComponent()) { - JsonObject returnedComponent = (JsonObject) JsonParser.parseString(value.getQueryOk().getComponent().getComponent().getJson()); - addGeneratedComponent(new Component(returnedComponent)); + QueryOk queryOk = value.getQueryOk(); + switch (queryOk.getResultCase()) { + case REFINEMENT: + if (queryOk.getRefinement().getSuccess()) { + executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); + executableQuery.success.accept(true); + } else { + executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); + executableQuery.failure.accept(new BackendException.QueryErrorException(queryOk.getRefinement().getReason())); + // executableQuery.success.accept(false); + } + break; + + case CONSISTENCY: + if (queryOk.getConsistency().getSuccess()) { + executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); + executableQuery.success.accept(true); + } else { + executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); + executableQuery.failure.accept(new BackendException.QueryErrorException(queryOk.getConsistency().getReason())); + executableQuery.success.accept(false); + } + break; + + case DETERMINISM: + if (queryOk.getDeterminism().getSuccess()) { + executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); + executableQuery.success.accept(true); + } else { + executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); + executableQuery.failure.accept(new BackendException.QueryErrorException(queryOk.getDeterminism().getReason())); + executableQuery.success.accept(false); + } + break; + + case IMPLEMENTATION: + if (queryOk.getImplementation().getSuccess()) { + executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); + executableQuery.success.accept(true); + } else { + executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); + executableQuery.failure.accept(new BackendException.QueryErrorException(queryOk.getImplementation().getReason())); + executableQuery.success.accept(false); + } + break; + + case REACHABILITY: + if (queryOk.getReachability().getSuccess()) { + executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); + executableQuery.success.accept(true); + } else { + executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); + executableQuery.failure.accept(new BackendException.QueryErrorException(queryOk.getReachability().getReason())); + executableQuery.success.accept(false); + } + break; + + case COMPONENT: + executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); + executableQuery.success.accept(true); + JsonObject returnedComponent = (JsonObject) JsonParser.parseString(queryOk.getComponent().getComponent().getJson()); + addGeneratedComponent(new Component(returnedComponent)); + break; + + case ERROR: + executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); + executableQuery.failure.accept(new BackendException.QueryErrorException(queryOk.getError())); + executableQuery.success.accept(false); + break; + + case RESULT_NOT_SET: + executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); + executableQuery.success.accept(false); + break; } break; case USER_TOKEN_ERROR: executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); + executableQuery.failure.accept(new BackendException.QueryErrorException(value.getUserTokenError().getErrorMessage())); executableQuery.success.accept(false); break; @@ -328,25 +399,6 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, ExecutableQuer executableQuery.success.accept(false); break; } - -// if (value.hasRefinement() && value.getRefinement().getSuccess()) { -// executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); -// executableQuery.success.accept(true); -// } else if (value.hasConsistency() && value.getConsistency().getSuccess()) { -// executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); -// executableQuery.success.accept(true); -// } else if (value.hasDeterminism() && value.getDeterminism().getSuccess()) { -// executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); -// executableQuery.success.accept(true); -// } else if (value.hasComponent()) { -// executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); -// executableQuery.success.accept(true); -// JsonObject returnedComponent = (JsonObject) JsonParser.parseString(value.getComponent().getComponent().getJson()); -// addGeneratedComponent(new Component(returnedComponent)); -// } else { -// executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); -// executableQuery.success.accept(false); -// } } private void handleQueryBackendError(Throwable t, ExecutableQuery executableQuery) { From f99bd9ff8201184041eac8666d7091dcec8f7ad3 Mon Sep 17 00:00:00 2001 From: WassawRoki <56611129+WassawRoki@users.noreply.github.com> Date: Thu, 27 Oct 2022 14:43:34 +0200 Subject: [PATCH 036/120] The popup will no longer be place outside the window --- .../ecdar/presentations/QueryPresentation.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/java/ecdar/presentations/QueryPresentation.java b/src/main/java/ecdar/presentations/QueryPresentation.java index df680307..4910e67e 100644 --- a/src/main/java/ecdar/presentations/QueryPresentation.java +++ b/src/main/java/ecdar/presentations/QueryPresentation.java @@ -12,7 +12,9 @@ import javafx.beans.binding.When; import javafx.beans.property.SimpleBooleanProperty; import javafx.geometry.Insets; +import javafx.geometry.Point2D; import javafx.geometry.Pos; +import javafx.geometry.Rectangle2D; import javafx.scene.Cursor; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; @@ -21,6 +23,7 @@ import javafx.scene.input.KeyCode; import javafx.scene.layout.*; import javafx.scene.text.TextAlignment; +import javafx.stage.Screen; import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.material.Material; @@ -481,10 +484,17 @@ private void initializeMoreInformationButtonAndQueryTypeSymbol() { controller.queryTypeExpand.setOnMousePressed((e) -> { e.consume(); - double Y=this.getLayoutY(); - System.out.println(Y); - if(Y >= 500){queryTypeDropDown.show(JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, -this.getWidth()/3, -(this.getLayoutY()-this.getHeight()-300));} - else {queryTypeDropDown.show(JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, 16, 16);} + //height of app window + double height = this.getScene().getHeight(); + //height and width of object relative to the app window + Point2D Y = this.localToScene(this.getWidth(), this.getHeight()); + + //Check if the dropdown can fit the app window. + //340 is the height of the dropdown 27/10-2022 Cant find height before it is rendered + if(Y.getY()+340 >= height) + {queryTypeDropDown.show(JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.RIGHT, -55,-(Y.getY()-height)-50);} + else + {queryTypeDropDown.show(JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, 16, 16);} }); controller.queryTypeSymbol.setText(controller.getQuery() != null && controller.getQuery().getType() != null ? controller.getQuery().getType().getSymbol() : "---"); From 8b8bcbf39752f4c083192401fbab5e0a7afb125e Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Thu, 27 Oct 2022 21:43:15 +0200 Subject: [PATCH 037/120] WIP: Refactoring of BackendDriver started and almost done (query execution is broken) --- src/main/java/ecdar/Ecdar.java | 7 + src/main/java/ecdar/abstractions/Query.java | 164 ++----- .../java/ecdar/backend/BackendConnection.java | 65 +++ .../java/ecdar/backend/BackendDriver.java | 424 ++---------------- .../java/ecdar/backend/BackendHelper.java | 10 - src/main/java/ecdar/backend/GrpcRequest.java | 24 + .../backend/IgnoredInputOutputQuery.java | 49 -- .../java/ecdar/backend/QueryExecutor.java | 155 +++++++ .../controllers/ComponentController.java | 2 +- .../ecdar/controllers/EcdarController.java | 37 +- .../ecdar/controllers/LocationController.java | 2 +- .../controllers/QueryPaneController.java | 2 +- .../presentations/QueryPresentation.java | 197 +------- .../presentations/QueryPresentation.fxml | 59 --- 14 files changed, 341 insertions(+), 856 deletions(-) create mode 100644 src/main/java/ecdar/backend/BackendConnection.java create mode 100644 src/main/java/ecdar/backend/GrpcRequest.java delete mode 100644 src/main/java/ecdar/backend/IgnoredInputOutputQuery.java create mode 100644 src/main/java/ecdar/backend/QueryExecutor.java diff --git a/src/main/java/ecdar/Ecdar.java b/src/main/java/ecdar/Ecdar.java index 05bd9efc..ab793d67 100644 --- a/src/main/java/ecdar/Ecdar.java +++ b/src/main/java/ecdar/Ecdar.java @@ -4,6 +4,7 @@ import ecdar.abstractions.Project; import ecdar.backend.BackendDriver; import ecdar.backend.BackendHelper; +import ecdar.backend.QueryExecutor; import ecdar.code_analysis.CodeAnalysis; import ecdar.controllers.EcdarController; import ecdar.presentations.BackgroundThreadPresentation; @@ -52,6 +53,7 @@ public class Ecdar extends Application { public static BooleanProperty shouldRunBackgroundQueries = new SimpleBooleanProperty(true); private static final BooleanProperty isSplit = new SimpleBooleanProperty(true); //Set to true to ensure correct behaviour at first toggle. private static BackendDriver backendDriver = new BackendDriver(); + private static QueryExecutor queryExecutor = new QueryExecutor(backendDriver); private Stage debugStage; /** @@ -189,6 +191,11 @@ public static BackendDriver getBackendDriver() { return backendDriver; } + public static QueryExecutor getQueryExecutor() { + return queryExecutor; + + } + public static double getDpiScale() { if (!autoScalingEnabled.getValue()) return 1; diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index 2fd0b7f3..15aa9516 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -9,42 +9,63 @@ import javafx.application.Platform; import javafx.beans.property.*; -import java.util.HashMap; -import java.util.Map; +import java.util.function.Consumer; public class Query implements Serializable { private static final String QUERY = "query"; private static final String COMMENT = "comment"; private static final String IS_PERIODIC = "isPeriodic"; - private static final String IGNORED_INPUTS = "ignoredInputs"; - private static final String IGNORED_OUTPUTS = "ignoredOutputs"; private static final String BACKEND = "backend"; - public final HashMap ignoredInputs = new HashMap<>(); - public final HashMap ignoredOutputs = new HashMap<>(); - - private final ObjectProperty queryState = new SimpleObjectProperty<>(QueryState.UNKNOWN); private final StringProperty query = new SimpleStringProperty(""); private final StringProperty comment = new SimpleStringProperty(""); - private final SimpleBooleanProperty isPeriodic = new SimpleBooleanProperty(false); private final StringProperty errors = new SimpleStringProperty(""); + private final SimpleBooleanProperty isPeriodic = new SimpleBooleanProperty(false); + private final ObjectProperty queryState = new SimpleObjectProperty<>(QueryState.UNKNOWN); private final ObjectProperty type = new SimpleObjectProperty<>(); private BackendInstance backend; - private Runnable runQuery; + + + private final Consumer successConsumer = (aBoolean) -> { + if (aBoolean) { + setQueryState(QueryState.SUCCESSFUL); + } else { + setQueryState(QueryState.ERROR); + } + }; + + private Boolean forcedCancel = false; + private final Consumer failureConsumer = (e) -> { + if (forcedCancel) { + setQueryState(QueryState.UNKNOWN); + } else { + setQueryState(QueryState.SYNTAX_ERROR); + if (e instanceof BackendException.MissingFileQueryException) { + Ecdar.showToast("Please save the project before trying to run queries"); + } + + this.addError(e.getMessage()); + final Throwable cause = e.getCause(); + if (cause != null) { + // We had trouble generating the model if we get a NullPointerException + if (cause instanceof NullPointerException) { + setQueryState(QueryState.UNKNOWN); + } else { + Platform.runLater(() -> EcdarController.openQueryDialog(this, cause.toString())); + } + } + } + }; public Query(final String query, final String comment, final QueryState queryState) { this.query.set(query); this.comment.set(comment); this.queryState.set(queryState); setBackend(BackendHelper.getDefaultBackendInstance()); - - initializeRunQuery(); } public Query(final JsonObject jsonElement) { deserialize(jsonElement); - - initializeRunQuery(); } public QueryState getQueryState() { @@ -117,53 +138,12 @@ public ObjectProperty getTypeProperty() { return this.type; } - private Boolean forcedCancel = false; + public Consumer getSuccessConsumer() { + return successConsumer; + } - private void initializeRunQuery() { - runQuery = () -> { - setQueryState(QueryState.RUNNING); - forcedCancel = false; - errors.set(""); - - if (getQuery().isEmpty()) { - setQueryState(QueryState.SYNTAX_ERROR); - this.addError("Query is empty"); - return; - } - - Ecdar.getBackendDriver().addQueryToExecutionQueue(getType().getQueryName() + ": " + getQuery() + " " + getIgnoredInputOutputsOnQuery(), - getBackend(), - aBoolean -> { - if (aBoolean) { - setQueryState(QueryState.SUCCESSFUL); - } else { - setQueryState(QueryState.ERROR); - } - }, - e -> { - if (forcedCancel) { - setQueryState(QueryState.UNKNOWN); - } else { - setQueryState(QueryState.SYNTAX_ERROR); - if (e instanceof BackendException.MissingFileQueryException) { - Ecdar.showToast("Please save the project before trying to run queries"); - } - - this.addError(e.getMessage()); - final Throwable cause = e.getCause(); - if (cause != null) { - // We had trouble generating the model if we get a NullPointerException - if (cause instanceof NullPointerException) { - setQueryState(QueryState.UNKNOWN); - } else { - Platform.runLater(() -> EcdarController.openQueryDialog(this, cause.toString())); - } - } - } - }, - new QueryListener(this) - ); - }; + public Consumer getFailureConsumer() { + return failureConsumer; } @Override @@ -173,23 +153,11 @@ public JsonObject serialize() { result.addProperty(QUERY, getType().getQueryName() + ": " + getQuery()); result.addProperty(COMMENT, getComment()); result.addProperty(IS_PERIODIC, isPeriodic()); - - result.add(IGNORED_INPUTS, getHashMapAsJsonObject(ignoredInputs)); - result.add(IGNORED_OUTPUTS, getHashMapAsJsonObject(ignoredOutputs)); - result.addProperty(BACKEND, backend.getName()); return result; } - private JsonObject getHashMapAsJsonObject(HashMap ignoredOutputs) { - JsonObject resultingJsonObject = new JsonObject(); - for (Map.Entry currentPair : ignoredOutputs.entrySet()) { - resultingJsonObject.addProperty(currentPair.getKey(), currentPair.getValue()); - } - return resultingJsonObject; - } - @Override public void deserialize(final JsonObject json) { String query = json.getAsJsonPrimitive(QUERY).getAsString(); @@ -208,14 +176,6 @@ public void deserialize(final JsonObject json) { setIsPeriodic(json.getAsJsonPrimitive(IS_PERIODIC).getAsBoolean()); } - if (json.has(IGNORED_INPUTS)) { - deserializeJsonObjectToMap(json.getAsJsonObject(IGNORED_INPUTS), ignoredInputs); - } - - if (json.has(IGNORED_OUTPUTS)) { - deserializeJsonObjectToMap(json.getAsJsonObject(IGNORED_OUTPUTS), ignoredOutputs); - } - if(json.has(BACKEND)) { setBackend(BackendHelper.getBackendInstanceByName(json.getAsJsonPrimitive(BACKEND).getAsString())); } else { @@ -223,14 +183,6 @@ public void deserialize(final JsonObject json) { } } - private void deserializeJsonObjectToMap(JsonObject jsonObject, HashMap associatedMap) { - jsonObject.entrySet().forEach((entry) -> associatedMap.put(entry.getKey(), entry.getValue().getAsBoolean())); - } - - public void run() { - if (StringValidator.validateQuery(query.get())) runQuery.run(); - } - public void cancel() { if (getQueryState().equals(QueryState.RUNNING)) { forcedCancel = true; @@ -245,40 +197,4 @@ public void addError(String e) { public String getCurrentErrors() { return errors.getValue(); } - - private String getIgnoredInputOutputsOnQuery() { - if (!BackendHelper.backendSupportsInputOutputs(this.backend) || (!ignoredInputs.containsValue(true) && !ignoredOutputs.containsValue(true))) { - return ""; - } - - // Create StringBuilder starting with a quotation mark to signal start of extra outputs - StringBuilder ignoredInputOutputsStringBuilder = new StringBuilder("--ignored_outputs=\""); - - // Append outputs, comma separated - appendMapItemsWithValueTrue(ignoredInputOutputsStringBuilder, ignoredOutputs); - - // Append quotation marks to signal end of outputs and start of inputs - ignoredInputOutputsStringBuilder.append("\" --ignored_inputs=\""); - - // Append inputs, comma separated - appendMapItemsWithValueTrue(ignoredInputOutputsStringBuilder, ignoredInputs); - - // Append quotation mark to signal end of extra inputs - ignoredInputOutputsStringBuilder.append("\""); - - return ignoredInputOutputsStringBuilder.toString(); - } - - private void appendMapItemsWithValueTrue(StringBuilder stringBuilder, Map map) { - map.forEach((key, value) -> { - if (value) { - stringBuilder.append(key); - stringBuilder.append(","); - } - }); - - if (stringBuilder.lastIndexOf(",") + 1 == stringBuilder.length()) { - stringBuilder.setLength(stringBuilder.length() - 1); - } - } } diff --git a/src/main/java/ecdar/backend/BackendConnection.java b/src/main/java/ecdar/backend/BackendConnection.java new file mode 100644 index 00000000..dd81945e --- /dev/null +++ b/src/main/java/ecdar/backend/BackendConnection.java @@ -0,0 +1,65 @@ +package ecdar.backend; + +import EcdarProtoBuf.EcdarBackendGrpc; +import ecdar.abstractions.BackendInstance; +import io.grpc.ManagedChannel; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +public class BackendConnection { + private final Process process; + private final EcdarBackendGrpc.EcdarBackendStub stub; + private final ManagedChannel channel; + private final BackendInstance backendInstance; + + BackendConnection(BackendInstance backendInstance, Process process, EcdarBackendGrpc.EcdarBackendStub stub, ManagedChannel channel) { + this.process = process; + this.backendInstance = backendInstance; + this.stub = stub; + this.channel = channel; + } + + /** + * Get the gRPC stub of the connection to use for query execution + * + * @return the gRPC stub of this connection + */ + public EcdarBackendGrpc.EcdarBackendStub getStub() { + return stub; + } + + /** + * Get the backend instance that should be used to execute + * the query currently associated with this backend connection + * + * @return the instance of the associated executable query object, + * or null, if no executable query is currently associated + */ + public BackendInstance getBackendInstance() { + return backendInstance; + } + + /** + * Close the gRPC connection and end the process + * + * @throws IOException originally thrown by the destroy method on java.lang.Process + */ + public void close() throws IOException { + if (!channel.isShutdown()) { + try { + channel.shutdown(); + if (!channel.awaitTermination(45, TimeUnit.SECONDS)) { + channel.shutdownNow(); // Forcefully close the connection + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // If the backend-instance is remote, there will not be a process + if (process != null) { + process.destroy(); + } + } +} diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index 8d6c7c09..3272998f 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -3,19 +3,12 @@ import EcdarProtoBuf.ComponentProtos; import EcdarProtoBuf.EcdarBackendGrpc; import EcdarProtoBuf.QueryProtos; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import com.google.protobuf.Empty; import ecdar.Ecdar; import ecdar.abstractions.BackendInstance; import ecdar.abstractions.Component; -import ecdar.abstractions.QueryState; -import ecdar.controllers.EcdarController; -import ecdar.utility.UndoRedoStack; import io.grpc.*; import io.grpc.stub.StreamObserver; -import javafx.application.Platform; -import javafx.collections.ObservableList; import org.springframework.util.SocketUtils; import java.io.*; @@ -23,197 +16,35 @@ import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; public class BackendDriver { - private final BlockingQueue queryQueue = new ArrayBlockingQueue<>(200); - private final Map> openBackendConnections = new HashMap<>(); - private final int deadlineForResponses = 20000; + private final BlockingQueue requestQueue = new ArrayBlockingQueue<>(200); + private final Map> openBackendConnections = new HashMap<>(); // ToDo NIELS: Remove and close when backend is no longer needed + private final int responseDeadline = 20000; private final int rerunQueryDelay = 200; private final int numberOfRetriesPerQuery = 5; public BackendDriver() { // ToDo NIELS: Consider multiple consumer threads using 'for(int i = 0; i < x; i++) {}' - QueryConsumer consumer = new QueryConsumer(queryQueue); + GrpcRequestConsumer consumer = new GrpcRequestConsumer(); Thread consumerThread = new Thread(consumer); consumerThread.start(); } - /** - * Enqueue query for execution with consumers for success and failure, which are executed when a response or - * an error is received from the backend - * - * @param query the query to be executed - * @param backendInstance name of the backend to execute the query with - * @param success consumer for a successful response - * @param failure consumer for a failure response - * @param queryListener query listener for referencing the query for GUI purposes - */ - public void addQueryToExecutionQueue(String query, - BackendInstance backendInstance, - Consumer success, - Consumer failure, - QueryListener queryListener) { - queryQueue.add(new ExecutableQuery(query, backendInstance, success, failure, queryListener)); - } - - /** - * ToDo NIELS: Reimplement this with query queue when backends support this feature - * Asynchronous method for fetching inputs and outputs for the given refinement query and adding these to the - * ignored inputs and outputs pane for the given query. - * - * @param query the ignored input output query containing the query and related GUI elements - * @param backendInstance the backend that should be used to execute the query - */ - public void getInputOutputs(IgnoredInputOutputQuery query, BackendInstance backendInstance) { - final BackendConnection backendConnection; - try { - backendConnection = getBackendConnection(backendInstance); - } catch (BackendException.NoAvailableBackendConnectionException e) { - if (query.tries < numberOfRetriesPerQuery) { - new Timer().schedule(new TimerTask() { - @Override - public void run() { - getInputOutputs(query, backendInstance); - } - }, rerunQueryDelay); - } - - return; - } - - QueryProtos.ComponentsUpdateRequest.Builder componentsBuilder = QueryProtos.ComponentsUpdateRequest.newBuilder(); - for (Component c : Ecdar.getProject().getComponents()) { - componentsBuilder.addComponents(ComponentProtos.Component.newBuilder().setJson(c.serialize().toString()).build()); - } - - executeGrpcRequest(query.getQuery().getQuery(), - backendConnection, - componentsBuilder, - QueryProtos.IgnoredInputOutputs.newBuilder().getDefaultInstanceForType(), - (reponse) -> { - if (reponse.hasQuery() && reponse.getQuery().hasIgnoredInputOutputs()) { - var ignoredInputOutputs = reponse.getQuery().getIgnoredInputOutputs(); - query.addNewElementsToMap(new ArrayList<>(ignoredInputOutputs.getIgnoredInputsList()), new ArrayList<>(ignoredInputOutputs.getIgnoredOutputsList())); - } else { - // Response is unexpected, maybe just ignore - } - }, (t) -> { - } - ); - - } - - /** - * Close all open backend connection and kill all locally running processes - * - * @throws IOException if any of the sockets do not respond - */ - public void closeAllBackendConnections() throws IOException { - for (BlockingQueue bq : openBackendConnections.values()) { - for (BackendConnection bc : bq) bc.close(); - } - } - - private void executeQuery(ExecutableQuery executableQuery, BackendConnection backendConnection) { - if (executableQuery.queryListener.getQuery().getQueryState() == QueryState.UNKNOWN) return; - - QueryProtos.ComponentsUpdateRequest.Builder componentsBuilder = QueryProtos.ComponentsUpdateRequest.newBuilder(); - for (Component c : Ecdar.getProject().getComponents()) { - componentsBuilder.addComponents(ComponentProtos.Component.newBuilder().setJson(c.serialize().toString()).build()); - } - - executeGrpcRequest(executableQuery, backendConnection, componentsBuilder); + public int getResponseDeadline() { + return responseDeadline; } /** - * Executes the specified query as a gRPC request using the specified backend connection. - * componentsBuilder is used to update the components of the engine. + *Add a GrpcRequest to the request queue to be executed when a backend is available * - * @param executableQuery executable query to be executed by the backend - * @param backendConnection connection to the backend - * @param componentsBuilder components builder containing the components relevant to the query execution + * @param request The GrpcRequest to be executed later */ - private void executeGrpcRequest(ExecutableQuery executableQuery, - BackendConnection backendConnection, - QueryProtos.ComponentsUpdateRequest.Builder componentsBuilder) { - executeGrpcRequest(executableQuery.query, - backendConnection, - componentsBuilder, - null, - (response) -> handleQueryResponse(response, executableQuery), - (error) -> handleQueryBackendError(error, executableQuery) - ); + public void addRequestToExecutionQueue(GrpcRequest request) { + requestQueue.add(request); } - /** - * Executes the specified query as a gRPC request using the specified backend connection. - * componentsBuilder is used to update the components of the engine and on completion of this transaction, - * the query is sent and its response is consumed by responseConsumer. Any error encountered is handled by - * the errorConsumer. - * - * @param query query to be executed by the backend - * @param backendConnection connection to the backend - * @param componentsBuilder components builder containing the components relevant to the query execution - * @param protoBufIgnoredInputOutputs ProtoBuf object containing the inputs and outputs that should be ignored - * (can be null) - * @param responseConsumer consumer for handling the received response - * @param errorConsumer consumer for handling a potential error - */ - private void executeGrpcRequest(String query, - BackendConnection backendConnection, - QueryProtos.ComponentsUpdateRequest.Builder componentsBuilder, - QueryProtos.IgnoredInputOutputs protoBufIgnoredInputOutputs, - Consumer responseConsumer, - Consumer errorConsumer) { - StreamObserver observer = new StreamObserver<>() { - @Override - public void onNext(Empty value) { - } - - @Override - public void onError(Throwable t) { - errorConsumer.accept(t); - addBackendConnection(backendConnection); - } - - @Override - public void onCompleted() { - StreamObserver responseObserver = new StreamObserver<>() { - @Override - public void onNext(QueryProtos.QueryResponse value) { - responseConsumer.accept(value); - } - - @Override - public void onError(Throwable t) { - errorConsumer.accept(t); - addBackendConnection(backendConnection); - } - - @Override - public void onCompleted() { - addBackendConnection(backendConnection); - } - }; - - var queryBuilder = QueryProtos.Query.newBuilder() - .setId(0) - .setQuery(query); - - if (protoBufIgnoredInputOutputs != null) - queryBuilder.setIgnoredInputOutputs(protoBufIgnoredInputOutputs); - - backendConnection.getStub().withDeadlineAfter(deadlineForResponses, TimeUnit.MILLISECONDS) - .sendQuery(queryBuilder.build(), responseObserver); - } - }; - - backendConnection.getStub().withDeadlineAfter(deadlineForResponses, TimeUnit.MILLISECONDS) - .updateComponents(componentsBuilder.build(), observer); - } - - private void addBackendConnection(BackendConnection backendConnection) { + public void addBackendConnection(BackendConnection backendConnection) { this.openBackendConnections.get(backendConnection.getBackendInstance()).add(backendConnection); } @@ -308,235 +139,64 @@ private void tryStartNewBackendConnection(BackendInstance backend) { EcdarBackendGrpc.EcdarBackendStub stub = EcdarBackendGrpc.newStub(channel); BackendConnection newConnection = new BackendConnection(backend, p, stub, channel); addBackendConnection(newConnection); - } - - private void handleQueryResponse(QueryProtos.QueryResponse value, ExecutableQuery executableQuery) { - // If the query has been cancelled, ignore the result - if (executableQuery.queryListener.getQuery().getQueryState() == QueryState.UNKNOWN) return; - - if (value.hasRefinement() && value.getRefinement().getSuccess()) { - executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); - executableQuery.success.accept(true); - } else if (value.hasConsistency() && value.getConsistency().getSuccess()) { - executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); - executableQuery.success.accept(true); - } else if (value.hasDeterminism() && value.getDeterminism().getSuccess()) { - executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); - executableQuery.success.accept(true); - } else if (value.hasComponent()) { - executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); - executableQuery.success.accept(true); - JsonObject returnedComponent = (JsonObject) JsonParser.parseString(value.getComponent().getComponent().getJson()); - addGeneratedComponent(new Component(returnedComponent)); - } else { - executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); - executableQuery.success.accept(false); - } - } - - private void handleQueryBackendError(Throwable t, ExecutableQuery executableQuery) { - // If the query has been cancelled, ignore the error - if (executableQuery.queryListener.getQuery().getQueryState() == QueryState.UNKNOWN) return; - - // Each error starts with a capitalized description of the error equal to the gRPC error type encountered - String errorType = t.getMessage().split(":\\s+", 2)[0]; - switch (errorType) { - case "CANCELLED": - executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); - executableQuery.failure.accept(new BackendException.QueryErrorException("The query was cancelled")); - break; - - case "DEADLINE_EXCEEDED": - executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); - executableQuery.failure.accept(new BackendException.QueryErrorException("The backend did not answer the request in time")); - queryQueue.add(executableQuery); - break; - - case "UNIMPLEMENTED": - executableQuery.queryListener.getQuery().setQueryState(QueryState.SYNTAX_ERROR); - executableQuery.failure.accept(new BackendException.QueryErrorException("The query type is not supported by the backend")); - break; - - case "INTERNAL": - executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); - executableQuery.failure.accept(new BackendException.QueryErrorException("The backend was unable to execute this query:\n" + t.getMessage().split(": ", 2)[1])); - break; - - case "UNKNOWN": - executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); - executableQuery.failure.accept(new BackendException.QueryErrorException("The backend encountered an unknown error")); - break; - - case "UNAVAILABLE": - executableQuery.queryListener.getQuery().setQueryState(QueryState.SYNTAX_ERROR); - executableQuery.failure.accept(new BackendException.QueryErrorException("The backend could not be reached")); - break; - - default: - try { - executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); - executableQuery.failure.accept(new BackendException.QueryErrorException("The query failed and gave the following error: " + errorType)); - } catch (Exception e) { - e.printStackTrace(); - } - break; + QueryProtos.ComponentsUpdateRequest.Builder componentsBuilder = QueryProtos.ComponentsUpdateRequest.newBuilder(); + for (Component c : Ecdar.getProject().getComponents()) { + componentsBuilder.addComponents(ComponentProtos.Component.newBuilder().setJson(c.serialize().toString()).build()); } - } - private void addGeneratedComponent(Component newComponent) { - Platform.runLater(() -> { - newComponent.setTemporary(true); - - ObservableList listOfGeneratedComponents = Ecdar.getProject().getTempComponents(); - Component matchedComponent = null; - - for (Component currentGeneratedComponent : listOfGeneratedComponents) { - int comparisonOfNames = currentGeneratedComponent.getName().compareTo(newComponent.getName()); - - if (comparisonOfNames == 0) { - matchedComponent = currentGeneratedComponent; - break; - } else if (comparisonOfNames < 0) { - break; - } - } - - if (matchedComponent == null) { - UndoRedoStack.pushAndPerform(() -> { // Perform - Ecdar.getProject().getTempComponents().add(newComponent); - }, () -> { // Undo - Ecdar.getProject().getTempComponents().remove(newComponent); - }, "Created new component: " + newComponent.getName(), "add-circle"); - } else { - // Remove current component with name and add the newly generated one - Component finalMatchedComponent = matchedComponent; - UndoRedoStack.pushAndPerform(() -> { // Perform - Ecdar.getProject().getTempComponents().remove(finalMatchedComponent); - Ecdar.getProject().getTempComponents().add(newComponent); - }, () -> { // Undo - Ecdar.getProject().getTempComponents().remove(newComponent); - Ecdar.getProject().getTempComponents().add(finalMatchedComponent); - }, "Created new component: " + newComponent.getName(), "add-circle"); + StreamObserver observer = new StreamObserver<>() { + @Override + public void onNext(Empty value) { } - EcdarController.getActiveCanvasPresentation().getController().setActiveModel(newComponent); - }); - } - - private class ExecutableQuery { - private final String query; - private final BackendInstance backend; - private final Consumer success; - private final Consumer failure; - private final QueryListener queryListener; - public int tries = 0; - - ExecutableQuery(String query, BackendInstance backendInstance, Consumer success, Consumer failure, QueryListener queryListener) { - this.query = query; - this.backend = backendInstance; - this.success = success; - this.failure = failure; - this.queryListener = queryListener; - } - - /** - * Execute the query using the backend driver - */ - void execute(BackendConnection backendConnection) { - tries++; - executeQuery(this, backendConnection); - } - } - - private class BackendConnection { - private final Process process; - private final EcdarBackendGrpc.EcdarBackendStub stub; - private final ManagedChannel channel; - private final BackendInstance backendInstance; - - BackendConnection(BackendInstance backendInstance, Process process, EcdarBackendGrpc.EcdarBackendStub stub, ManagedChannel channel) { - this.process = process; - this.stub = stub; - this.backendInstance = backendInstance; - this.channel = channel; - } - - /** - * Get the gRPC stub of the connection to use for query execution - * - * @return the gRPC stub of this connection - */ - public EcdarBackendGrpc.EcdarBackendStub getStub() { - return stub; - } - - /** - * Get the backend instance that should be used to execute - * the query currently associated with this backend connection - * - * @return the instance of the associated executable query object, - * or null, if no executable query is currently associated - */ - public BackendInstance getBackendInstance() { - return backendInstance; - } - - /** - * Close the gRPC connection and end the process - * - * @throws IOException originally thrown by the destroy method on java.lang.Process - */ - public void close() throws IOException { - if (!channel.isShutdown()) { - try { - channel.shutdown(); - if (!channel.awaitTermination(45, TimeUnit.SECONDS)) { - channel.shutdownNow(); // Forcefully close the connection - } - } catch (Exception e) { - e.printStackTrace(); - } + @Override + public void onError(Throwable t) { + addBackendConnection(newConnection); + // ToDo NIELS: Handle failed component update } - // If the backend-instance is remote, there will not be a process - if (process != null) { - process.destroy(); + @Override + public void onCompleted() { } + }; - openBackendConnections.get(backendInstance).remove(this); - } + newConnection.getStub().withDeadlineAfter(responseDeadline, TimeUnit.MILLISECONDS) + .updateComponents(componentsBuilder.build(), observer); } - private class QueryConsumer implements Runnable { - BlockingQueue queue; - - private QueryConsumer(BlockingQueue queue) { - this.queue = queue; + /** + * Close all open backend connection and kill all locally running processes + * + * @throws IOException if any of the sockets do not respond + */ + public void closeAllBackendConnections() throws IOException { + for (BlockingQueue bq : openBackendConnections.values()) { + for (BackendConnection bc : bq) bc.close(); } + } + private class GrpcRequestConsumer implements Runnable { @Override public void run() { while (true) { try { - ExecutableQuery executableQuery = this.queue.take(); + GrpcRequest request = requestQueue.take(); - final BackendConnection backendConnection; try { - backendConnection = getBackendConnection(executableQuery.backend); - executableQuery.execute(backendConnection); + request.tries++; + request.execute(getBackendConnection(request.getBackend())); } catch (BackendException.NoAvailableBackendConnectionException e) { e.printStackTrace(); - if (executableQuery.tries < numberOfRetriesPerQuery) { + if (request.tries < numberOfRetriesPerQuery) { new Timer().schedule(new TimerTask() { @Override public void run() { - queryQueue.add(executableQuery); + requestQueue.add(request); } }, rerunQueryDelay); } else { - executableQuery.failure.accept(new BackendException("Failed to execute query after five tries")); - executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); + Ecdar.showToast("Unable to find a connection to the requested engine"); } return; } diff --git a/src/main/java/ecdar/backend/BackendHelper.java b/src/main/java/ecdar/backend/BackendHelper.java index f9ee6a35..b75e8a7a 100644 --- a/src/main/java/ecdar/backend/BackendHelper.java +++ b/src/main/java/ecdar/backend/BackendHelper.java @@ -46,16 +46,6 @@ public static String storeQuery(String query, String fileName) throws URISyntaxE return path; } - /** - * Check if the given backend supports ignored inputs and outputs as parameters. - * - * @param backend the name of the backend to check - * @return true if the backend supports ignored inputs and outputs, else false - */ - public static Boolean backendSupportsInputOutputs(BackendInstance backend) { - return true; - } - /** * Gets the directory path for storing temporary files. * diff --git a/src/main/java/ecdar/backend/GrpcRequest.java b/src/main/java/ecdar/backend/GrpcRequest.java new file mode 100644 index 00000000..100bd810 --- /dev/null +++ b/src/main/java/ecdar/backend/GrpcRequest.java @@ -0,0 +1,24 @@ +package ecdar.backend; + +import ecdar.abstractions.BackendInstance; + +import java.util.function.Consumer; + +public class GrpcRequest { + private final Consumer request; + private final BackendInstance backend; + public int tries = 0; + + public GrpcRequest(Consumer request, BackendInstance backend) { + this.request = request; + this.backend = backend; + } + + public void execute(BackendConnection backendConnection) { + this.request.accept(backendConnection); + } + + public BackendInstance getBackend() { + return backend; + } +} \ No newline at end of file diff --git a/src/main/java/ecdar/backend/IgnoredInputOutputQuery.java b/src/main/java/ecdar/backend/IgnoredInputOutputQuery.java deleted file mode 100644 index 06843b31..00000000 --- a/src/main/java/ecdar/backend/IgnoredInputOutputQuery.java +++ /dev/null @@ -1,49 +0,0 @@ -package ecdar.backend; - -import ecdar.abstractions.Query; -import ecdar.presentations.QueryPresentation; -import javafx.scene.layout.VBox; - -import java.util.ArrayList; -import java.util.HashMap; - -public class IgnoredInputOutputQuery { - private final Query query; - private final QueryPresentation queryPresentation; - private final HashMap ignoredInputs; - private final VBox ignoredInputsVBox; - private final HashMap ignoredOutputs; - private final VBox ignoredOutputsVBox; - public int tries = 0; - - public IgnoredInputOutputQuery(Query query, QueryPresentation queryPresentation, HashMap ignoredInputs, VBox ignoredInputsVBox, HashMap ignoredOutputs, VBox ignoredOutputsVBox) { - this.query = query; - this.queryPresentation = queryPresentation; - this.ignoredInputs = ignoredInputs; - this.ignoredInputsVBox = ignoredInputsVBox; - this.ignoredOutputs = ignoredOutputs; - this.ignoredOutputsVBox = ignoredOutputsVBox; - } - - public Query getQuery() { - return query; - } - - public void addNewElementsToMap(ArrayList inputs, ArrayList outputs) { - // Add inputs to list and as checkboxes in UI - for (String key : inputs) { - if (!this.ignoredInputs.containsKey(key)) { - this.queryPresentation.addInputOrOutput(key, false, this.ignoredInputs, this.ignoredInputsVBox); - this.ignoredInputs.put(key, false); - } - } - - // Add inputs to list and as checkboxes in UI - for (String key : outputs) { - if (!this.ignoredInputs.containsKey(key)) { - this.queryPresentation.addInputOrOutput(key, false, this.ignoredInputs, this.ignoredInputsVBox); - this.ignoredInputs.put(key, false); - } - } - } -} diff --git a/src/main/java/ecdar/backend/QueryExecutor.java b/src/main/java/ecdar/backend/QueryExecutor.java new file mode 100644 index 00000000..1aa9ca54 --- /dev/null +++ b/src/main/java/ecdar/backend/QueryExecutor.java @@ -0,0 +1,155 @@ +package ecdar.backend; + +import EcdarProtoBuf.QueryProtos; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import ecdar.Ecdar; +import ecdar.abstractions.Component; +import ecdar.abstractions.Query; +import ecdar.abstractions.QueryState; +import ecdar.controllers.EcdarController; +import ecdar.utility.UndoRedoStack; +import ecdar.utility.helpers.StringValidator; +import io.grpc.stub.StreamObserver; +import javafx.application.Platform; +import javafx.collections.ObservableList; + +import java.util.NoSuchElementException; +import java.util.concurrent.TimeUnit; + +public class QueryExecutor { + private final BackendDriver backendDriver; + + public QueryExecutor(BackendDriver backendDriver) { + this.backendDriver = backendDriver; + } + + /** + * Executes the specified query + * @param query query to be executed + */ + public void executeQuery(Query query) throws NoSuchElementException { + if (query.getQueryState().equals(QueryState.RUNNING) || !StringValidator.validateQuery(query.getQuery())) return; + + if (query.getQuery().isEmpty()) { + query.setQueryState(QueryState.SYNTAX_ERROR); + query.addError("Query is empty"); + return; + } + + query.setQueryState(QueryState.RUNNING); + query.errors().set(""); + + GrpcRequest request = new GrpcRequest(backendConnection -> { + StreamObserver responseObserver = new StreamObserver<>() { + @Override + public void onNext(QueryProtos.QueryResponse value) { + handleQueryResponse(value, query); + } + + @Override + public void onError(Throwable t) { + handleQueryBackendError(t, query); + } + + @Override + public void onCompleted() { + backendDriver.addBackendConnection(backendConnection); + } + }; + + var queryBuilder = QueryProtos.Query.newBuilder() + .setId(0) + .setQuery(query.getQuery()); + + backendConnection.getStub().withDeadlineAfter(backendDriver.getResponseDeadline(), TimeUnit.MILLISECONDS) + .sendQuery(queryBuilder.build(), responseObserver); + }, query.getBackend()); + + backendDriver.addRequestToExecutionQueue(request); + } + + private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { + // If the query has been cancelled, ignore the result + if (query.getQueryState() == QueryState.UNKNOWN) return; + + if (value.hasRefinement() && value.getRefinement().getSuccess()) { + query.setQueryState(QueryState.SUCCESSFUL); + query.getSuccessConsumer().accept(true); + } else if (value.hasConsistency() && value.getConsistency().getSuccess()) { + query.setQueryState(QueryState.SUCCESSFUL); + query.getSuccessConsumer().accept(true); + } else if (value.hasDeterminism() && value.getDeterminism().getSuccess()) { + query.setQueryState(QueryState.SUCCESSFUL); + query.getSuccessConsumer().accept(true); + } else if (value.hasComponent()) { + query.setQueryState(QueryState.SUCCESSFUL); + query.getSuccessConsumer().accept(true); + JsonObject returnedComponent = (JsonObject) JsonParser.parseString(value.getComponent().getComponent().getJson()); + addGeneratedComponent(new Component(returnedComponent)); + } else { + query.setQueryState(QueryState.ERROR); + query.getSuccessConsumer().accept(false); + } + } + + private void handleQueryBackendError(Throwable t, Query query) { + // If the query has been cancelled, ignore the error + if (query.getQueryState() == QueryState.UNKNOWN) return; + + // Each error starts with a capitalized description of the error equal to the gRPC error type encountered + String errorType = t.getMessage().split(":\\s+", 2)[0]; + + if ("DEADLINE_EXCEEDED".equals(errorType)) { + query.setQueryState(QueryState.ERROR); + query.getFailureConsumer().accept(new BackendException.QueryErrorException("The backend did not answer the request in time")); + } else { + try { + query.setQueryState(QueryState.ERROR); + query.getFailureConsumer().accept(new BackendException.QueryErrorException("The query failed and gave the following error: " + errorType)); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + private void addGeneratedComponent(Component newComponent) { + Platform.runLater(() -> { + newComponent.setTemporary(true); + + ObservableList listOfGeneratedComponents = Ecdar.getProject().getTempComponents(); // ToDo NIELS: Refactor + Component matchedComponent = null; + + for (Component currentGeneratedComponent : listOfGeneratedComponents) { + int comparisonOfNames = currentGeneratedComponent.getName().compareTo(newComponent.getName()); + + if (comparisonOfNames == 0) { + matchedComponent = currentGeneratedComponent; + break; + } else if (comparisonOfNames < 0) { + break; + } + } + + if (matchedComponent == null) { + UndoRedoStack.pushAndPerform(() -> { // Perform + Ecdar.getProject().getTempComponents().add(newComponent); + }, () -> { // Undo + Ecdar.getProject().getTempComponents().remove(newComponent); + }, "Created new component: " + newComponent.getName(), "add-circle"); + } else { + // Remove current component with name and add the newly generated one + Component finalMatchedComponent = matchedComponent; + UndoRedoStack.pushAndPerform(() -> { // Perform + Ecdar.getProject().getTempComponents().remove(finalMatchedComponent); + Ecdar.getProject().getTempComponents().add(newComponent); + }, () -> { // Undo + Ecdar.getProject().getTempComponents().remove(newComponent); + Ecdar.getProject().getTempComponents().add(finalMatchedComponent); + }, "Created new component: " + newComponent.getName(), "add-circle"); + } + + EcdarController.getActiveCanvasPresentation().getController().setActiveModel(newComponent); + }); + } +} diff --git a/src/main/java/ecdar/controllers/ComponentController.java b/src/main/java/ecdar/controllers/ComponentController.java index 40d239b6..48b593c9 100644 --- a/src/main/java/ecdar/controllers/ComponentController.java +++ b/src/main/java/ecdar/controllers/ComponentController.java @@ -359,7 +359,7 @@ private void initializeContextMenu() { final Query query = new Query(deadlockQuery, deadlockComment, QueryState.UNKNOWN); query.setType(QueryType.REACHABILITY); Ecdar.getProject().getQueries().add(query); - query.run(); + Ecdar.getQueryExecutor().executeQuery(query); contextMenu.hide(); }); diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index e350b296..e1885005 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -6,7 +6,6 @@ import ecdar.Ecdar; import ecdar.abstractions.*; import ecdar.backend.BackendHelper; -import ecdar.backend.QueryListener; import ecdar.code_analysis.CodeAnalysis; import ecdar.mutation.models.MutationTestPlan; import ecdar.presentations.*; @@ -440,7 +439,7 @@ private void startBackgroundQueriesThread() { if (!Ecdar.shouldRunBackgroundQueries.get()) return; Ecdar.getProject().getQueries().forEach(query -> { - if (query.isPeriodic()) query.run(); + if (query.isPeriodic()) Ecdar.getQueryExecutor().executeQuery(query); }); // List of threads to start @@ -458,37 +457,9 @@ private void startBackgroundQueriesThread() { Query reachabilityQuery = new Query(locationReachableQuery, "", QueryState.UNKNOWN); reachabilityQuery.setType(QueryType.REACHABILITY); - Ecdar.getBackendDriver().addQueryToExecutionQueue(locationReachableQuery, - BackendHelper.getDefaultBackendInstance(), - (result -> { - if (result) { - location.setReachability(Location.Reachability.REACHABLE); - } else { - location.setReachability(Location.Reachability.UNREACHABLE); - } - Debug.removeThread(Thread.currentThread()); - }), - (e) -> { - location.setReachability(Location.Reachability.UNKNOWN); - Debug.removeThread(Thread.currentThread()); - }, - new QueryListener(reachabilityQuery)); - - final Thread verifyThread = new Thread(() -> Ecdar.getBackendDriver().addQueryToExecutionQueue(locationReachableQuery, - BackendHelper.getDefaultBackendInstance(), - (result -> { - if (result) { - location.setReachability(Location.Reachability.REACHABLE); - } else { - location.setReachability(Location.Reachability.UNREACHABLE); - } - Debug.removeThread(Thread.currentThread()); - }), - (e) -> { - location.setReachability(Location.Reachability.UNKNOWN); - Debug.removeThread(Thread.currentThread()); - }, - new QueryListener(reachabilityQuery))); + Ecdar.getQueryExecutor().executeQuery(reachabilityQuery); + + final Thread verifyThread = new Thread(() -> Ecdar.getQueryExecutor().executeQuery(reachabilityQuery)); verifyThread.setName(locationReachableQuery + " (" + verifyThread.getName() + ")"); Debug.addThread(verifyThread); diff --git a/src/main/java/ecdar/controllers/LocationController.java b/src/main/java/ecdar/controllers/LocationController.java index 64bf12d5..0e36a07e 100644 --- a/src/main/java/ecdar/controllers/LocationController.java +++ b/src/main/java/ecdar/controllers/LocationController.java @@ -206,7 +206,7 @@ public void initializeDropDownMenu() { final Query query = new Query(reachabilityQuery, reachabilityComment, QueryState.UNKNOWN); query.setType(QueryType.REACHABILITY); Ecdar.getProject().getQueries().add(query); - query.run(); + Ecdar.getQueryExecutor().executeQuery(query); dropDownMenu.hide(); }); diff --git a/src/main/java/ecdar/controllers/QueryPaneController.java b/src/main/java/ecdar/controllers/QueryPaneController.java index 3e4a621d..26c003dd 100644 --- a/src/main/java/ecdar/controllers/QueryPaneController.java +++ b/src/main/java/ecdar/controllers/QueryPaneController.java @@ -87,7 +87,7 @@ private void runAllQueriesButtonClicked() { Ecdar.getProject().getQueries().forEach(query -> { if (query.getType() == null) return; query.cancel(); - query.run(); + Ecdar.getQueryExecutor().executeQuery(query); }); } diff --git a/src/main/java/ecdar/presentations/QueryPresentation.java b/src/main/java/ecdar/presentations/QueryPresentation.java index db1d9179..57c74011 100644 --- a/src/main/java/ecdar/presentations/QueryPresentation.java +++ b/src/main/java/ecdar/presentations/QueryPresentation.java @@ -12,14 +12,11 @@ import javafx.beans.binding.When; import javafx.beans.property.SimpleBooleanProperty; import javafx.geometry.Insets; -import javafx.geometry.Pos; import javafx.scene.Cursor; import javafx.scene.control.Label; -import javafx.scene.control.TitledPane; import javafx.scene.control.Tooltip; import javafx.scene.input.KeyCode; import javafx.scene.layout.*; -import javafx.scene.text.TextAlignment; import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.material.Material; @@ -43,7 +40,6 @@ public QueryPresentation(final Query query) { initializeActionButton(); initializeDetailsButton(); initializeTextFields(); - initializeInputOutputPaneAndAddIgnoredInputOutputs(); initializeMoreInformationButtonAndQueryTypeSymbol(); initializeBackendsDropdown(); @@ -270,197 +266,6 @@ private void setStatusIndicatorContentColor(javafx.scene.paint.Color color, Font } } - private void initializeInputOutputPaneAndAddIgnoredInputOutputs() { - Platform.runLater(() -> { - final TitledPane inputOutputPane = (TitledPane) lookup("#inputOutputPane"); - inputOutputPane.setAnimated(true); - final Runnable changeTitledPaneVisibility = () -> updateTitlePaneVisibility(inputOutputPane); - - // Run the consumer to ensure that the input/output pane is displayed for existing refinement queries - changeTitledPaneVisibility.run(); - - // Bind the expand icon to the expand property of the pane - inputOutputPane.expandedProperty().addListener((observable, oldValue, newValue) -> { - FontIcon expandIcon = (FontIcon) inputOutputPane.lookup("#inputOutputPaneExpandIcon"); - if (!newValue) { - expandIcon.setIconLiteral("gmi-keyboard-arrow-down"); - } else { - expandIcon.setIconLiteral("gmi-keyboard-arrow-up"); - } - }); - - // Make sure the input/output pane state is updated whenever the query text field loses focus - final JFXTextField queryTextField = (JFXTextField) lookup("#query"); - queryTextField.focusedProperty().addListener((observable, oldValue, newValue) -> { - if (!newValue) { - changeTitledPaneVisibility.run(); - } - }); - - // Change visibility of input/output Pane when backend is changed for the query ToDo NIELS - // lookup("#swapBackendButton").setOnMousePressed(event -> changeTitledPaneVisibility.accept(controller.getQuery().getQuery())); - Platform.runLater(() -> addIgnoredInputOutputsFromQuery(inputOutputPane)); - }); - } - - private void initiateResetInputOutputButton(TitledPane inputOutputPane) { - Platform.runLater(() -> { - final JFXRippler resetInputOutputPaneButton = (JFXRippler) inputOutputPane.lookup("#inputOutputPaneUpdateButton"); - - initializeResetInputOutputPaneButton(inputOutputPane, resetInputOutputPaneButton); - - // Get the inputs and outputs automatically, when executing a refinement query - controller.actionButton.setOnMousePressed(event -> { - // Update the ignored inputs and outputs without clearing the lists - updateInputOutputs(inputOutputPane, false); - }); - - // Install tooltip on the reset button - final Tooltip buttonTooltip = new Tooltip("Refresh inputs and outputs (resets selections)"); - buttonTooltip.setWrapText(true); - Tooltip.install(resetInputOutputPaneButton, buttonTooltip); - }); - } - - private void initializeResetInputOutputPaneButton(TitledPane inputOutputPane, - JFXRippler resetInputOutputPaneButton) { - Platform.runLater(() -> { - final FontIcon resetInputOutputPaneButtonIcon = (FontIcon) lookup("#inputOutputPaneUpdateButtonIcon"); - final JFXSpinner progressIndicator = (JFXSpinner) lookup("#inputOutputProgressIndicator"); - - progressIndicator.setVisible(false); - - // Set the initial state of the reset button - resetInputOutputPaneButton.setCursor(Cursor.HAND); - resetInputOutputPaneButton.setRipplerFill(Color.GREY.getColor(Color.Intensity.I500)); - resetInputOutputPaneButton.setMaskType(JFXRippler.RipplerMask.CIRCLE); - resetInputOutputPaneButtonIcon.setIconColor(Color.GREY.getColor(Color.Intensity.I900)); - - resetInputOutputPaneButton.setOnMousePressed(event -> { - // Disable the button on click - progressIndicator.setVisible(true); - resetInputOutputPaneButton.setDisable(true); - resetInputOutputPaneButtonIcon.setIconColor(Color.GREY.getColor(Color.Intensity.I700)); - - updateInputOutputs(inputOutputPane, true); - - // Enable the button after inputs and outputs have been updated - progressIndicator.setVisible(false); - resetInputOutputPaneButton.setDisable(false); - resetInputOutputPaneButtonIcon.setIconColor(Color.GREY.getColor(Color.Intensity.I900)); - }); - }); - } - - private void updateTitlePaneVisibility(TitledPane inputOutputPane) { - // Check if the query is a refinement and that the engine is set to Reveaal - if (controller.getQuery().getQuery().startsWith("refinement") && BackendHelper.backendSupportsInputOutputs(controller.getQuery().getBackend())) { - initiateResetInputOutputButton(inputOutputPane); - - // Make the input/output pane visible - inputOutputPaneVisibility(true); - } else { - inputOutputPaneVisibility(false); - } - } - - private void updateInputOutputs(TitledPane inputOutputPane, Boolean shouldResetSelections) { - final VBox inputBox = (VBox) inputOutputPane.lookup("#inputBox"); - final VBox outputBox = (VBox) inputOutputPane.lookup("#outputBox"); - - IgnoredInputOutputQuery query = new IgnoredInputOutputQuery(this.controller.getQuery(), this, controller.getQuery().ignoredInputs, inputBox, controller.getQuery().ignoredOutputs, outputBox); - - if (shouldResetSelections) { - // Reset selections for ignored inputs and outputs - clearIgnoredInputsAndOutputs(inputBox, outputBox); - } - - Ecdar.getBackendDriver().getInputOutputs(query, controller.getQuery().getBackend()); - } - - private void clearIgnoredInputsAndOutputs(VBox inputBox, VBox outputBox) { - controller.getQuery().ignoredInputs.clear(); - controller.getQuery().ignoredOutputs.clear(); - - Platform.runLater(() -> { - inputBox.getChildren().clear(); - outputBox.getChildren().clear(); - }); - } - - public void addInputOrOutput(String name, Boolean state, Map associatedMap, VBox associatedBox) { - HBox sliderBox = new HBox(); - sliderBox.setAlignment(Pos.CENTER_LEFT); - - Label label = new Label(name); - label.setWrapText(true); - - // Initialize the toggle slider - JFXToggleButton slider = new JFXToggleButton(); - slider.setStyle("-jfx-toggle-color:#dddddd; -jfx-untoggle-color:#dddddd; -jfx-toggle-line-color:#" + Color.YELLOW.getColor(Color.Intensity.I700).toString().substring(2, 8) + ";-jfx-untoggle-line-color:#" + Color.GREY.getColor(Color.Intensity.I400).toString().substring(2, 8) + "; -fx-padding: 0 0 0 0;"); - slider.setSelected(state); - - // Add label beneath toggle slider to display state - Label stateLabel = new Label(); - stateLabel.setText(state ? "Ignored" : "Included"); - stateLabel.setTextAlignment(TextAlignment.CENTER); - - // Enforce changes of the slider - slider.setOnMouseClicked((event) -> { - associatedMap.replace(name, slider.isSelected()); - stateLabel.setText(slider.isSelected() ? "Ignored" : "Included"); - }); - - // Add toggle slider and state label to VBox and set its width - VBox sliderAndStateLabel = new VBox(); - sliderAndStateLabel.setMinWidth(64); - sliderAndStateLabel.setMaxWidth(64); - sliderAndStateLabel.setSpacing(-7.5); - sliderAndStateLabel.getChildren().addAll(slider, stateLabel); - - // Horizontal space to ensure that the toggle slider and input/output label is not intertwined - Region horizontalSpace = new Region(); - horizontalSpace.setMinWidth(16); - horizontalSpace.setMaxWidth(16); - - sliderBox.getChildren().addAll(sliderAndStateLabel, horizontalSpace, label); - - Platform.runLater(() -> { - // Add checkbox to the scene - associatedBox.getChildren().add(sliderBox); - }); - } - - private void inputOutputPaneVisibility(Boolean visibility) { - Platform.runLater(() -> { - final TitledPane inputOutputPane = (TitledPane) lookup("#inputOutputPane"); - - // Hide/show the inputOutputPane and remove/add the space it would occupy respectively - inputOutputPane.setVisible(visibility); - inputOutputPane.setManaged(visibility); - - // Set expand property only on visibility false to avoid auto expand - if (!visibility) { - inputOutputPane.setExpanded(false); - } - }); - } - - private void addIgnoredInputOutputsFromQuery(TitledPane inputOutputPane) { - final VBox inputBox = (VBox) inputOutputPane.lookup("#inputBox"); - final VBox outputBox = (VBox) inputOutputPane.lookup("#outputBox"); - - // Add inputs as toggles in the GUI - for (Map.Entry entry : controller.getQuery().ignoredInputs.entrySet()) { - addInputOrOutput(entry.getKey(), entry.getValue(), controller.getQuery().ignoredInputs, inputBox); - } - - // Add outputs as toggles in the GUI - for (Map.Entry entry : controller.getQuery().ignoredOutputs.entrySet()) { - addInputOrOutput(entry.getKey(), entry.getValue(), controller.getQuery().ignoredOutputs, outputBox); - } - } - private void initializeMoreInformationButtonAndQueryTypeSymbol() { Platform.runLater(() -> { controller.queryTypeExpand.setVisible(true); @@ -508,6 +313,6 @@ private void addQueryTypeListElement(final QueryType type, final DropDownMenu dr } private void runQuery() { - controller.getQuery().run(); + Ecdar.getQueryExecutor().executeQuery(this.controller.getQuery()); } } diff --git a/src/main/resources/ecdar/presentations/QueryPresentation.fxml b/src/main/resources/ecdar/presentations/QueryPresentation.fxml index 223e15df..baa26e81 100644 --- a/src/main/resources/ecdar/presentations/QueryPresentation.fxml +++ b/src/main/resources/ecdar/presentations/QueryPresentation.fxml @@ -68,65 +68,6 @@
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Ignored outputs on left side - - - - - - - - - - Ignored inputs on right side - - - - - - - - From b89c943932ab95f1661296bef6f75b88bf14c426 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Fri, 28 Oct 2022 09:07:49 +0200 Subject: [PATCH 038/120] WIP: Query execution FIXED --- src/main/java/ecdar/Ecdar.java | 2 + .../java/ecdar/backend/BackendDriver.java | 12 ++--- .../java/ecdar/backend/QueryExecutor.java | 23 +++++++++- .../java/ecdar/backend/QueryListener.java | 45 ------------------- .../BackendOptionsDialogController.java | 1 + 5 files changed, 30 insertions(+), 53 deletions(-) delete mode 100644 src/main/java/ecdar/backend/QueryListener.java diff --git a/src/main/java/ecdar/Ecdar.java b/src/main/java/ecdar/Ecdar.java index ab793d67..bd1bc351 100644 --- a/src/main/java/ecdar/Ecdar.java +++ b/src/main/java/ecdar/Ecdar.java @@ -313,6 +313,7 @@ public void start(final Stage stage) { try { backendDriver.closeAllBackendConnections(); + queryExecutor.closeAllBackendConnections(); } catch (IOException e) { e.printStackTrace(); } @@ -326,6 +327,7 @@ public void start(final Stage stage) { // to prevent dangling connections and queries try { backendDriver.closeAllBackendConnections(); + queryExecutor.closeAllBackendConnections(); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index 3272998f..b4e330c7 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -21,7 +21,7 @@ public class BackendDriver { private final BlockingQueue requestQueue = new ArrayBlockingQueue<>(200); private final Map> openBackendConnections = new HashMap<>(); // ToDo NIELS: Remove and close when backend is no longer needed private final int responseDeadline = 20000; - private final int rerunQueryDelay = 200; + private final int rerunRequestDelay = 200; private final int numberOfRetriesPerQuery = 5; public BackendDriver() { @@ -36,7 +36,7 @@ public int getResponseDeadline() { } /** - *Add a GrpcRequest to the request queue to be executed when a backend is available + * Add a GrpcRequest to the request queue to be executed when a backend is available * * @param request The GrpcRequest to be executed later */ @@ -45,7 +45,8 @@ public void addRequestToExecutionQueue(GrpcRequest request) { } public void addBackendConnection(BackendConnection backendConnection) { - this.openBackendConnections.get(backendConnection.getBackendInstance()).add(backendConnection); + var relatedQueue = this.openBackendConnections.get(backendConnection.getBackendInstance()); + if (!relatedQueue.contains(backendConnection)) relatedQueue.add(backendConnection); } /** @@ -152,12 +153,11 @@ public void onNext(Empty value) { @Override public void onError(Throwable t) { - addBackendConnection(newConnection); - // ToDo NIELS: Handle failed component update } @Override public void onCompleted() { + addBackendConnection(newConnection); } }; @@ -194,7 +194,7 @@ public void run() { public void run() { requestQueue.add(request); } - }, rerunQueryDelay); + }, rerunRequestDelay); } else { Ecdar.showToast("Unable to find a connection to the requested engine"); } diff --git a/src/main/java/ecdar/backend/QueryExecutor.java b/src/main/java/ecdar/backend/QueryExecutor.java index 1aa9ca54..68d7a2d5 100644 --- a/src/main/java/ecdar/backend/QueryExecutor.java +++ b/src/main/java/ecdar/backend/QueryExecutor.java @@ -14,11 +14,14 @@ import javafx.application.Platform; import javafx.collections.ObservableList; +import java.io.IOException; +import java.util.ArrayList; import java.util.NoSuchElementException; import java.util.concurrent.TimeUnit; public class QueryExecutor { private final BackendDriver backendDriver; + private final ArrayList connections = new ArrayList<>(); public QueryExecutor(BackendDriver backendDriver) { this.backendDriver = backendDriver; @@ -41,6 +44,7 @@ public void executeQuery(Query query) throws NoSuchElementException { query.errors().set(""); GrpcRequest request = new GrpcRequest(backendConnection -> { + connections.add(backendConnection); // Sae reference for closing connection on exit StreamObserver responseObserver = new StreamObserver<>() { @Override public void onNext(QueryProtos.QueryResponse value) { @@ -50,17 +54,21 @@ public void onNext(QueryProtos.QueryResponse value) { @Override public void onError(Throwable t) { handleQueryBackendError(t, query); + backendDriver.addBackendConnection(backendConnection); + connections.remove(backendConnection); } @Override public void onCompleted() { + // Release backend connection backendDriver.addBackendConnection(backendConnection); + connections.remove(backendConnection); } }; var queryBuilder = QueryProtos.Query.newBuilder() .setId(0) - .setQuery(query.getQuery()); + .setQuery(query.getType().getQueryName() + ": " + query.getQuery()); backendConnection.getStub().withDeadlineAfter(backendDriver.getResponseDeadline(), TimeUnit.MILLISECONDS) .sendQuery(queryBuilder.build(), responseObserver); @@ -106,7 +114,7 @@ private void handleQueryBackendError(Throwable t, Query query) { } else { try { query.setQueryState(QueryState.ERROR); - query.getFailureConsumer().accept(new BackendException.QueryErrorException("The query failed and gave the following error: " + errorType)); + query.getFailureConsumer().accept(new BackendException.QueryErrorException("The execution of this query failed with message:\n" + t.getLocalizedMessage())); } catch (Exception e) { e.printStackTrace(); } @@ -152,4 +160,15 @@ private void addGeneratedComponent(Component newComponent) { EcdarController.getActiveCanvasPresentation().getController().setActiveModel(newComponent); }); } + + /** + * Close all open backend connection and kill all locally running processes + * + * @throws IOException if any of the sockets do not respond + */ + public void closeAllBackendConnections() throws IOException { + for (BackendConnection con : connections) { + con.close(); + } + } } diff --git a/src/main/java/ecdar/backend/QueryListener.java b/src/main/java/ecdar/backend/QueryListener.java deleted file mode 100644 index ffdccc3a..00000000 --- a/src/main/java/ecdar/backend/QueryListener.java +++ /dev/null @@ -1,45 +0,0 @@ -package ecdar.backend; - -import ecdar.abstractions.Query; -import ecdar.abstractions.QueryState; -import ecdar.controllers.EcdarController; -import javafx.application.Platform; - -public class QueryListener { - - private final Query query; - - public QueryListener() { - this(new Query("Unknown", "Unknown", QueryState.UNKNOWN)); - } - - public QueryListener(final Query query) { - this.query = query; - } - - public void setLength(final int i) { - - } - - public void setCurrent(final int i) { - - } - - public Query getQuery() { - return query; - } - - /* - public void setTrace(char c, String s, Vector vector, int i) { - - } - */ - - public void setFeedback(final String s) { - if (s.contains("inf") || s.contains("sup")) { - Platform.runLater(() -> { - EcdarController.openQueryDialog(query, s.split("\n")[1]); - }); - } - } -} diff --git a/src/main/java/ecdar/controllers/BackendOptionsDialogController.java b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java index 8d880858..4810e8cd 100644 --- a/src/main/java/ecdar/controllers/BackendOptionsDialogController.java +++ b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java @@ -73,6 +73,7 @@ public boolean saveChangesToBackendOptions() { // Close all backend connections to avoid dangling backend connections when port range is changed try { Ecdar.getBackendDriver().closeAllBackendConnections(); + Ecdar.getQueryExecutor().closeAllBackendConnections(); } catch (IOException e) { e.printStackTrace(); } From 852c024c117130ed39734b18d902c66ff1e832e4 Mon Sep 17 00:00:00 2001 From: APaludan Date: Fri, 28 Oct 2022 09:26:36 +0200 Subject: [PATCH 039/120] more simulation work --- .../java/ecdar/backend/BackendDriver.java | 67 +++++++++++++++++++ ...ulationInitializationDialogController.java | 2 + .../ecdar/simulation/SimulationHandler.java | 6 ++ 3 files changed, 75 insertions(+) diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index 00c5dae6..0d7fd88c 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -4,6 +4,7 @@ import EcdarProtoBuf.EcdarBackendGrpc; import EcdarProtoBuf.ObjectProtos; import EcdarProtoBuf.QueryProtos; +import EcdarProtoBuf.QueryProtos.SimulationStepResponse; import EcdarProtoBuf.QueryProtos.QueryResponse.QueryOk; import com.google.gson.JsonObject; @@ -13,6 +14,7 @@ import ecdar.abstractions.BackendInstance; import ecdar.abstractions.Component; import ecdar.abstractions.QueryState; +import ecdar.backend.BackendException.NoAvailableBackendConnectionException; import ecdar.simulation.SimulationState; import io.grpc.*; import io.grpc.stub.StreamObserver; @@ -157,6 +159,51 @@ private void executeGrpcRequest(ExecutableQuery executableQuery, ); } + public void executeStartSimRequest(String componentComposition, Consumer responseConsumer, Consumer errorConsumer) { + BackendConnection backendConnection; + try { + backendConnection = getBackendConnection(BackendHelper.getDefaultBackendInstance()); + } catch (NoAvailableBackendConnectionException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return; + } + + StreamObserver responseObserver = new StreamObserver<>() { + @Override + public void onNext(QueryProtos.SimulationStepResponse value) { + System.out.println(value); + responseConsumer.accept(value); + } + + @Override + public void onError(Throwable t) { + errorConsumer.accept(t); + addBackendConnection(backendConnection); + } + + @Override + public void onCompleted() { + addBackendConnection(backendConnection); + } + }; + + var comInfo = ComponentProtos.ComponentsInfo.newBuilder(); + for (Component c : Ecdar.getProject().getComponents()) { + if (componentComposition.contains(c.getName())) { + comInfo.addComponents(ComponentProtos.Component.newBuilder().setJson(c.serialize().toString()).build()); + } + } + comInfo.setComponentsHash(comInfo.getComponentsList().hashCode()); + var simStartRequest = QueryProtos.SimulationStartRequest.newBuilder(); + var simInfo = QueryProtos.SimulationInfo.newBuilder() + .setComponentComposition(componentComposition) + .setComponentsInfo(comInfo); + simStartRequest.setSimulationInfo(simInfo); + backendConnection.getStub().withDeadlineAfter(deadlineForResponses, TimeUnit.MILLISECONDS) + .startSimulation(simStartRequest.build(), responseObserver); + } + /** * Executes the specified query as a gRPC request using the specified backend connection. * componentsInfoBuilder is used to update the components of the engine and on completion of this transaction, @@ -497,6 +544,26 @@ private void addGeneratedComponent(Component newComponent) { }); } + // private class ExecutableStartSimRequest { + // private final String componentComposition; + // private final BackendInstance backendInstance; + // private final Consumer success; + // private final Consumer failure; + // private final StartSimListener startSimListener; + // public int tries = 0; + + // public ExecutableStartSimRequest(String componentComposition, BackendInstance backendInstance, + // Consumer success, Consumer failure, StartSimListener startSimListener, + // int tries) { + // this.componentComposition = componentComposition; + // this.backendInstance = backendInstance; + // this.success = success; + // this.failure = failure; + // this.startSimListener = startSimListener; + // this.tries = tries; + // } + // } + private class ExecutableQuery { private final String query; private final BackendInstance backend; diff --git a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java index 1eb1529c..144a5bd9 100644 --- a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java +++ b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java @@ -2,6 +2,8 @@ import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXComboBox; +import ecdar.Ecdar; +import javafx.fxml.FXML; import javafx.fxml.Initializable; import java.net.URL; diff --git a/src/main/java/ecdar/simulation/SimulationHandler.java b/src/main/java/ecdar/simulation/SimulationHandler.java index a37b0712..2396232e 100755 --- a/src/main/java/ecdar/simulation/SimulationHandler.java +++ b/src/main/java/ecdar/simulation/SimulationHandler.java @@ -12,6 +12,8 @@ import java.util.ArrayList; import java.util.Map; +import EcdarProtoBuf.QueryProtos.SimulationStepResponse; + /** * Handles state changes, updates of values / clocks, and keeps track of all the transitions that * have been taken throughout a simulation. @@ -26,6 +28,7 @@ public class SimulationHandler { private EcdarSystem system; private SimulationStateSuccessor successor; private int numberOfSteps; + private SimulationStepResponse currentResponse; /** * A string to keep track what is currently being simulated @@ -89,9 +92,12 @@ private void initializeSimulation() { */ public void initialStep() { initializeSimulation(); + final SimulationState currentState = currentConcreteState.get(); successor = getStateSuccessor(); + Ecdar.getBackendDriver().executeStartSimRequest("Administration || Machine || Researcher", (res) -> currentResponse = res, (err) -> System.out.println(err)); + //Save the previous states, and get the new currentConcreteState.set(successor.getState()); this.traceLog.add(currentState); From e07c6f3bb1c2afcdf23f54146af449beab89013d Mon Sep 17 00:00:00 2001 From: WassawRoki <56611129+WassawRoki@users.noreply.github.com> Date: Fri, 28 Oct 2022 10:48:46 +0200 Subject: [PATCH 040/120] Update QueryPresentation.java --- .../presentations/QueryPresentation.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/java/ecdar/presentations/QueryPresentation.java b/src/main/java/ecdar/presentations/QueryPresentation.java index 4910e67e..eb15f7e5 100644 --- a/src/main/java/ecdar/presentations/QueryPresentation.java +++ b/src/main/java/ecdar/presentations/QueryPresentation.java @@ -24,6 +24,7 @@ import javafx.scene.layout.*; import javafx.scene.text.TextAlignment; import javafx.stage.Screen; +import javafx.stage.StageStyle; import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.material.Material; @@ -478,23 +479,24 @@ private void initializeMoreInformationButtonAndQueryTypeSymbol() { QueryType[] queryTypes = QueryType.values(); for (QueryType type : queryTypes) { addQueryTypeListElement(type, queryTypeDropDown); - } controller.queryTypeExpand.setOnMousePressed((e) -> { e.consume(); - //height of app window - double height = this.getScene().getHeight(); - //height and width of object relative to the app window - Point2D Y = this.localToScene(this.getWidth(), this.getHeight()); - + double windowHeight = this.getScene().getHeight(); + //Location of dropdown relative to the app window + Point2D Origin = this.localToScene(this.getWidth(), this.getHeight()); + //Generate the popups properties before displaying + queryTypeDropDown.show(this); //Check if the dropdown can fit the app window. - //340 is the height of the dropdown 27/10-2022 Cant find height before it is rendered - if(Y.getY()+340 >= height) - {queryTypeDropDown.show(JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.RIGHT, -55,-(Y.getY()-height)-50);} - else - {queryTypeDropDown.show(JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, 16, 16);} + if(Origin.getY()+queryTypeDropDown.getHeight() >= windowHeight){ + queryTypeDropDown.show(JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.RIGHT, -55,-(Origin.getY()-windowHeight)-50); + } + else{ + queryTypeDropDown.show(JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, 16, 16); + } + }); controller.queryTypeSymbol.setText(controller.getQuery() != null && controller.getQuery().getType() != null ? controller.getQuery().getType().getSymbol() : "---"); From 83aed20736ec9e302b9c7b4b8725d45814c0277d Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Tue, 1 Nov 2022 06:57:01 +0100 Subject: [PATCH 041/120] WIP: Minor changes --- .../java/ecdar/backend/BackendDriver.java | 31 +++++++------------ .../java/ecdar/backend/QueryExecutor.java | 22 ++++++------- .../controllers/ComponentController.java | 2 +- 3 files changed, 23 insertions(+), 32 deletions(-) diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index b4e330c7..b9b83156 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -49,6 +49,17 @@ public void addBackendConnection(BackendConnection backendConnection) { if (!relatedQueue.contains(backendConnection)) relatedQueue.add(backendConnection); } + /** + * Close all open backend connection and kill all locally running processes + * + * @throws IOException if any of the sockets do not respond + */ + public void closeAllBackendConnections() throws IOException { + for (BlockingQueue bq : openBackendConnections.values()) { + for (BackendConnection bc : bq) bc.close(); + } + } + /** * Filters the list of open {@link BackendConnection}s to the specified {@link BackendInstance} and returns the * first match or attempts to start a new connection if none is found. @@ -165,17 +176,6 @@ public void onCompleted() { .updateComponents(componentsBuilder.build(), observer); } - /** - * Close all open backend connection and kill all locally running processes - * - * @throws IOException if any of the sockets do not respond - */ - public void closeAllBackendConnections() throws IOException { - for (BlockingQueue bq : openBackendConnections.values()) { - for (BackendConnection bc : bq) bc.close(); - } - } - private class GrpcRequestConsumer implements Runnable { @Override public void run() { @@ -206,13 +206,4 @@ public void run() { } } } - - enum TraceType { - NONE, SOME, SHORTEST, FASTEST; - - @Override - public String toString() { - return "trace " + this.ordinal(); - } - } } diff --git a/src/main/java/ecdar/backend/QueryExecutor.java b/src/main/java/ecdar/backend/QueryExecutor.java index 68d7a2d5..9c68f2f8 100644 --- a/src/main/java/ecdar/backend/QueryExecutor.java +++ b/src/main/java/ecdar/backend/QueryExecutor.java @@ -77,6 +77,17 @@ public void onCompleted() { backendDriver.addRequestToExecutionQueue(request); } + /** + * Close all open backend connection and kill all locally running processes + * + * @throws IOException if any of the sockets do not respond + */ + public void closeAllBackendConnections() throws IOException { + for (BackendConnection con : connections) { + con.close(); + } + } + private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { // If the query has been cancelled, ignore the result if (query.getQueryState() == QueryState.UNKNOWN) return; @@ -160,15 +171,4 @@ private void addGeneratedComponent(Component newComponent) { EcdarController.getActiveCanvasPresentation().getController().setActiveModel(newComponent); }); } - - /** - * Close all open backend connection and kill all locally running processes - * - * @throws IOException if any of the sockets do not respond - */ - public void closeAllBackendConnections() throws IOException { - for (BackendConnection con : connections) { - con.close(); - } - } } diff --git a/src/main/java/ecdar/controllers/ComponentController.java b/src/main/java/ecdar/controllers/ComponentController.java index 48b593c9..9d67e2aa 100644 --- a/src/main/java/ecdar/controllers/ComponentController.java +++ b/src/main/java/ecdar/controllers/ComponentController.java @@ -329,7 +329,7 @@ private void initializeContextMenu() { }, "Added universal location '" + newLocation + "' to component '" + component.getName() + "'", "add-circle"); }); - // Adds the add inconsistent location element to the drop down menu, this element adds an inconsistent location + // Adds the add inconsistent location element to the dropdown menu, this element adds an inconsistent location contextMenu.addClickableListElement("Add Inconsistent Location", event -> { contextMenu.hide(); double x = DropDownMenu.x - LocationPresentation.RADIUS / 2; From 011c89c28c95752c43b86d28b0a52fb2f5cddf0f Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Tue, 1 Nov 2022 09:22:51 +0100 Subject: [PATCH 042/120] Moved SimulationHandler --- src/main/java/ecdar/Ecdar.java | 2 +- src/main/java/ecdar/backend/QueryHandler.java | 6 ++++- .../SimulationHandler.java | 25 ++++++++++--------- .../controllers/SimulatorController.java | 2 +- .../TracePaneElementController.java | 2 +- .../TransitionPaneElementController.java | 2 +- 6 files changed, 22 insertions(+), 17 deletions(-) rename src/main/java/ecdar/{simulation => backend}/SimulationHandler.java (93%) diff --git a/src/main/java/ecdar/Ecdar.java b/src/main/java/ecdar/Ecdar.java index 64d69102..01775eac 100644 --- a/src/main/java/ecdar/Ecdar.java +++ b/src/main/java/ecdar/Ecdar.java @@ -5,7 +5,7 @@ import ecdar.backend.BackendDriver; import ecdar.backend.BackendHelper; import ecdar.backend.QueryHandler; -import ecdar.simulation.SimulationHandler; +import ecdar.backend.SimulationHandler; import ecdar.code_analysis.CodeAnalysis; import ecdar.controllers.EcdarController; import ecdar.presentations.BackgroundThreadPresentation; diff --git a/src/main/java/ecdar/backend/QueryHandler.java b/src/main/java/ecdar/backend/QueryHandler.java index 84c58a9c..ed3eb401 100644 --- a/src/main/java/ecdar/backend/QueryHandler.java +++ b/src/main/java/ecdar/backend/QueryHandler.java @@ -58,6 +58,8 @@ public void onNext(QueryProtos.QueryResponse value) { @Override public void onError(Throwable t) { handleQueryBackendError(t, query); + + // Release backend connection backendDriver.addBackendConnection(backendConnection); connections.remove(backendConnection); } @@ -70,8 +72,10 @@ public void onCompleted() { } }; + // ToDo SW5: Not working with the updated gRPC Protos var queryBuilder = QueryProtos.QueryRequest.newBuilder() - .setQueryId(0) + .setUserId(1) + .setQueryId(1) .setQuery(query.getType().getQueryName() + ": " + query.getQuery()) .setComponentsInfo(componentsInfoBuilder); diff --git a/src/main/java/ecdar/simulation/SimulationHandler.java b/src/main/java/ecdar/backend/SimulationHandler.java similarity index 93% rename from src/main/java/ecdar/simulation/SimulationHandler.java rename to src/main/java/ecdar/backend/SimulationHandler.java index 5f3d6f61..4da6b04d 100755 --- a/src/main/java/ecdar/simulation/SimulationHandler.java +++ b/src/main/java/ecdar/backend/SimulationHandler.java @@ -1,13 +1,11 @@ -package ecdar.simulation; +package ecdar.backend; import EcdarProtoBuf.ComponentProtos; import EcdarProtoBuf.QueryProtos; import ecdar.Ecdar; import ecdar.abstractions.*; -import ecdar.backend.BackendConnection; -import ecdar.backend.BackendDriver; -import ecdar.backend.BackendHelper; -import ecdar.backend.GrpcRequest; +import ecdar.simulation.SimulationState; +import ecdar.simulation.SimulationStateSuccessor; import io.grpc.stub.StreamObserver; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; @@ -53,9 +51,9 @@ public class SimulationHandler { * that are available, when running the initial step. * That is why we need to keep track of the initial transitions. */ - private final ObservableList initialTransitions = FXCollections.observableArrayList(); + private final ObservableList initialTransitions = FXCollections.observableArrayList(); public ObservableList traceLog = FXCollections.observableArrayList(); - public ObservableList availableTransitions = FXCollections.observableArrayList(); + public ObservableList availableTransitions = FXCollections.observableArrayList(); private final BackendDriver backendDriver; private final ArrayList connections = new ArrayList<>(); @@ -107,7 +105,7 @@ public void initialStep() { final SimulationState currentState = currentConcreteState.get(); successor = getStateSuccessor(); - GrpcRequest newRequest = new GrpcRequest(backendConnection -> { + GrpcRequest request = new GrpcRequest(backendConnection -> { StreamObserver responseObserver = new StreamObserver<>() { @Override public void onNext(QueryProtos.SimulationStepResponse value) { @@ -118,6 +116,7 @@ public void onNext(QueryProtos.SimulationStepResponse value) { @Override public void onError(Throwable t) { System.out.println(t.getMessage()); + // Release backend connection backendDriver.addBackendConnection(backendConnection); connections.remove(backendConnection); @@ -147,6 +146,8 @@ public void onCompleted() { .startSimulation(simStartRequest.build(), responseObserver); }, BackendHelper.getDefaultBackendInstance()); + backendDriver.addRequestToExecutionQueue(request); + //Save the previous states, and get the new currentConcreteState.set(successor.getState()); this.traceLog.add(currentState); @@ -230,7 +231,7 @@ public void nextStep(final int selectedTransitionIndex, final BigDecimal delay) return; } - final Transition selectedTransition = availableTransitions.get(selectedTransitionIndex); + final ecdar.simulation.Transition selectedTransition = availableTransitions.get(selectedTransitionIndex); edgesSelected = new ArrayList<>(); //Preparing for the step @@ -278,7 +279,7 @@ public void nextStep(final int selectedTransition) { nextStep(selectedTransition, BigDecimal.ZERO); } - public void nextStep(final Transition transition, final BigDecimal delay) { + public void nextStep(final ecdar.simulation.Transition transition, final BigDecimal delay) { int index = availableTransitions.indexOf(transition); if (index != -1) { nextStep(index, delay); @@ -374,7 +375,7 @@ public ObservableList getTraceLog() { * * @return an {@link ObservableList} of all the currently available transitions in this state */ - public ObservableList getAvailableTransitions() { + public ObservableList getAvailableTransitions() { return availableTransitions; } @@ -446,7 +447,7 @@ public void printAvailableTransitions() { */ public ArrayList getAvailableTransitionsAsStrings() { final ArrayList transitions = new ArrayList<>(); - for (final Transition Transition : availableTransitions) { + for (final ecdar.simulation.Transition Transition : availableTransitions) { transitions.add(Transition.getLabel()); } return transitions; diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index bdba66b4..f3305934 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -2,7 +2,7 @@ import ecdar.Ecdar; import ecdar.abstractions.*; -import ecdar.simulation.SimulationHandler; +import ecdar.backend.SimulationHandler; import ecdar.presentations.SimulatorOverviewPresentation; import ecdar.simulation.SimulationState; import ecdar.simulation.Transition; diff --git a/src/main/java/ecdar/controllers/TracePaneElementController.java b/src/main/java/ecdar/controllers/TracePaneElementController.java index 6ace31df..1c2e82fa 100755 --- a/src/main/java/ecdar/controllers/TracePaneElementController.java +++ b/src/main/java/ecdar/controllers/TracePaneElementController.java @@ -4,7 +4,7 @@ import ecdar.Ecdar; import ecdar.abstractions.Location; import ecdar.simulation.SimulationState; -import ecdar.simulation.SimulationHandler; +import ecdar.backend.SimulationHandler; import ecdar.presentations.TransitionPresentation; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleIntegerProperty; diff --git a/src/main/java/ecdar/controllers/TransitionPaneElementController.java b/src/main/java/ecdar/controllers/TransitionPaneElementController.java index 8a985a92..0233e335 100755 --- a/src/main/java/ecdar/controllers/TransitionPaneElementController.java +++ b/src/main/java/ecdar/controllers/TransitionPaneElementController.java @@ -5,7 +5,7 @@ import ecdar.Ecdar; import ecdar.abstractions.Edge; import ecdar.simulation.Transition; -import ecdar.simulation.SimulationHandler; +import ecdar.backend.SimulationHandler; import ecdar.presentations.TransitionPresentation; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; From 622dbb46241b1d9303a445238472fefd37191a9e Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Tue, 1 Nov 2022 09:26:53 +0100 Subject: [PATCH 043/120] getComponentsInfoBuilder changed to require string instead of query to generalize usage --- src/main/java/ecdar/backend/BackendHelper.java | 4 ++-- src/main/java/ecdar/backend/QueryHandler.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/ecdar/backend/BackendHelper.java b/src/main/java/ecdar/backend/BackendHelper.java index 72b56810..b93756b5 100644 --- a/src/main/java/ecdar/backend/BackendHelper.java +++ b/src/main/java/ecdar/backend/BackendHelper.java @@ -148,10 +148,10 @@ public static void addBackendInstanceListener(Runnable runnable) { BackendHelper.backendInstancesUpdatedListeners.add(runnable); } - public static ComponentProtos.ComponentsInfo.Builder getComponentsInfoBuilder(Query query) { + public static ComponentProtos.ComponentsInfo.Builder getComponentsInfoBuilder(String query) { ComponentProtos.ComponentsInfo.Builder componentsInfoBuilder = ComponentProtos.ComponentsInfo.newBuilder(); for (Component c : Ecdar.getProject().getComponents()) { - if (query.getQuery().contains(c.getName())) { + if (query.contains(c.getName())) { componentsInfoBuilder.addComponents(ComponentProtos.Component.newBuilder().setJson(c.serialize().toString()).build()); } } diff --git a/src/main/java/ecdar/backend/QueryHandler.java b/src/main/java/ecdar/backend/QueryHandler.java index ed3eb401..5f76ce1e 100644 --- a/src/main/java/ecdar/backend/QueryHandler.java +++ b/src/main/java/ecdar/backend/QueryHandler.java @@ -47,7 +47,7 @@ public void executeQuery(Query query) throws NoSuchElementException { GrpcRequest request = new GrpcRequest(backendConnection -> { connections.add(backendConnection); // Save reference for closing connection on exit - var componentsInfoBuilder = BackendHelper.getComponentsInfoBuilder(query); + var componentsInfoBuilder = BackendHelper.getComponentsInfoBuilder(query.getQuery()); StreamObserver responseObserver = new StreamObserver<>() { @Override From 808652d6ffe240841d1bc620e45c6d4eab2d4722 Mon Sep 17 00:00:00 2001 From: WassawRoki <56611129+WassawRoki@users.noreply.github.com> Date: Thu, 3 Nov 2022 13:49:09 +0100 Subject: [PATCH 044/120] We might have it --- .../ecdar/controllers/EcdarController.java | 1 + ...ulationInitializationDialogController.java | 23 ++++++++++++- .../controllers/SimulatorController.java | 33 +++++++++++-------- ...ationInitializationDialogPresentation.java | 2 ++ ...ationInitializationDialogPresentation.fxml | 2 +- 5 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index 8c432ed5..f0ecab80 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -293,6 +293,7 @@ private void initializeDialogs() { }); simulationInitializationDialog.getController().startButton.setOnMouseClicked(event -> { + // ToDo NIELS: Start simulation of selected query currentMode.setValue(Mode.Simulator); simulationInitializationDialog.close(); diff --git a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java index 1eb1529c..f085a649 100644 --- a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java +++ b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java @@ -5,13 +5,34 @@ import javafx.fxml.Initializable; import java.net.URL; -import java.util.ResourceBundle; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class SimulationInitializationDialogController implements Initializable { public JFXComboBox simulationComboBox; public JFXButton cancelButton; public JFXButton startButton; + public static List ListOfComponents = new ArrayList<>(); + + public void GetListOfComponentsToSimulate(){ + //Function gets list of components to simulation + String componentsToSimulate =simulationComboBox.getSelectionModel().getSelectedItem(); + + //componentsToSimulate = componentsToSimulate.replace("([\\w]*)([^\\w])",""); + + Pattern pattern = Pattern.compile("([\\w]*)", Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(componentsToSimulate); + List listOfComponents = new ArrayList<>(); + while(matcher.find()){ + if(matcher.group().length() != 0) + {listOfComponents.add(matcher.group());} + } + + + ListOfComponents = listOfComponents; + } public void initialize(URL location, ResourceBundle resources) { } diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index bdba66b4..bb58258e 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -2,6 +2,7 @@ import ecdar.Ecdar; import ecdar.abstractions.*; +import ecdar.presentations.SimulationInitializationDialogPresentation; import ecdar.simulation.SimulationHandler; import ecdar.presentations.SimulatorOverviewPresentation; import ecdar.simulation.SimulationState; @@ -54,10 +55,10 @@ public void willShow() { shouldSimulationBeReset = false; } - if (!firstTimeInSimulator && !new HashSet<>(overviewPresentation.getController().getComponentObservableList()) - .containsAll(findComponentsInCurrentSimulation())) { - shouldSimulationBeReset = true; - } + //if (!firstTimeInSimulator && !new HashSet<>(overviewPresentation.getController().getComponentObservableList()) + // .containsAll(findComponentsInCurrentSimulation())) { + // shouldSimulationBeReset = true; + //} if (shouldSimulationBeReset || firstTimeInSimulator) { @@ -75,9 +76,10 @@ public void willShow() { private void resetSimulation() { final SimulationHandler sm = Ecdar.getSimulationHandler(); sm.initializeDefaultSystem(); + overviewPresentation.getController().clearOverview(); overviewPresentation.getController().getComponentObservableList().clear(); - overviewPresentation.getController().getComponentObservableList().addAll(findComponentsInCurrentSimulation()); + overviewPresentation.getController().getComponentObservableList().addAll(findComponentsInCurrentSimulation(SimulationInitializationDialogController.ListOfComponents)); firstTimeInSimulator = false; } @@ -89,19 +91,22 @@ private void resetSimulation() { * * @return all the components used in the current simulation */ - private List findComponentsInCurrentSimulation() { + private List findComponentsInCurrentSimulation(List queryComponents) { //Show components from the system final SimulationHandler sm = Ecdar.getSimulationHandler(); List components = new ArrayList<>(); components = Ecdar.getProject().getComponents(); -// for (int i = 0; i < sm.getSystem().getNoOfProcesses(); i++) { -// final int finalI = i; // when using a var in lambda it has to be final -// final List filteredList = Ecdar.getProject().getComponents().filtered(component -> { -// return component.getName().contentEquals(sm.getSystem().getProcess(finalI).getName()); -// }); -// components.addAll(filteredList); -// } - return components; + + List SelectedComponents = new ArrayList<>(); + for(Component comp:components) { + for(String componentInQuery : queryComponents) { + if((comp.getName().equals(componentInQuery))) { + SelectedComponents.add(comp); + } + } + + } + return SelectedComponents; } /** diff --git a/src/main/java/ecdar/presentations/SimulationInitializationDialogPresentation.java b/src/main/java/ecdar/presentations/SimulationInitializationDialogPresentation.java index 677217bd..100d3b52 100644 --- a/src/main/java/ecdar/presentations/SimulationInitializationDialogPresentation.java +++ b/src/main/java/ecdar/presentations/SimulationInitializationDialogPresentation.java @@ -1,5 +1,6 @@ package ecdar.presentations; +import com.jfoenix.controls.JFXComboBox; import com.jfoenix.controls.JFXDialog; import ecdar.controllers.BackendOptionsDialogController; import ecdar.controllers.SimulationInitializationDialogController; @@ -14,4 +15,5 @@ public SimulationInitializationDialogPresentation() { public SimulationInitializationDialogController getController() { return controller; } + } diff --git a/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml b/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml index 332e082d..6df548fa 100644 --- a/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml +++ b/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml @@ -36,7 +36,7 @@ - + From a8bfef6d1de63919df92d159573b26992df170b3 Mon Sep 17 00:00:00 2001 From: WassawRoki <56611129+WassawRoki@users.noreply.github.com> Date: Thu, 3 Nov 2022 13:57:43 +0100 Subject: [PATCH 045/120] Clena up --- .../SimulationInitializationDialogController.java | 13 ++++++------- .../java/ecdar/controllers/SimulatorController.java | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java index f085a649..f1e3d1a0 100644 --- a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java +++ b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java @@ -18,19 +18,18 @@ public class SimulationInitializationDialogController implements Initializable { public void GetListOfComponentsToSimulate(){ //Function gets list of components to simulation - String componentsToSimulate =simulationComboBox.getSelectionModel().getSelectedItem(); - - //componentsToSimulate = componentsToSimulate.replace("([\\w]*)([^\\w])",""); - + String componentsToSimulate = simulationComboBox.getSelectionModel().getSelectedItem(); + //filters out all components by ignoring operators. Pattern pattern = Pattern.compile("([\\w]*)", Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(componentsToSimulate); List listOfComponents = new ArrayList<>(); + //Adds all found components to list. while(matcher.find()){ - if(matcher.group().length() != 0) - {listOfComponents.add(matcher.group());} + if(matcher.group().length() != 0) { + listOfComponents.add(matcher.group()); + } } - ListOfComponents = listOfComponents; } public void initialize(URL location, ResourceBundle resources) { diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index bb58258e..fb7f07d4 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -97,6 +97,7 @@ private List findComponentsInCurrentSimulation(List queryComp List components = new ArrayList<>(); components = Ecdar.getProject().getComponents(); + //Matches query components against with existing components and adds them to simulation List SelectedComponents = new ArrayList<>(); for(Component comp:components) { for(String componentInQuery : queryComponents) { @@ -104,7 +105,6 @@ private List findComponentsInCurrentSimulation(List queryComp SelectedComponents.add(comp); } } - } return SelectedComponents; } From a2828cd738f26d5301f27e880da9509a765be367 Mon Sep 17 00:00:00 2001 From: Sigurd Date: Thu, 3 Nov 2022 14:01:16 +0100 Subject: [PATCH 046/120] WIP - Consume state in failing query --- src/main/java/ecdar/abstractions/Query.java | 26 ++++++++++++++++++- src/main/java/ecdar/backend/QueryHandler.java | 8 +++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index bb7863a2..85d25ff8 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -1,14 +1,17 @@ package ecdar.abstractions; +import EcdarProtoBuf.ComponentProtos; +import EcdarProtoBuf.ObjectProtos; import ecdar.Ecdar; import ecdar.backend.*; import ecdar.controllers.EcdarController; -import ecdar.utility.helpers.StringValidator; +import ecdar.utility.colors.Color; import ecdar.utility.serialize.Serializable; import com.google.gson.JsonObject; import javafx.application.Platform; import javafx.beans.property.*; +import java.io.FilenameFilter; import java.util.function.Consumer; public class Query implements Serializable { @@ -23,6 +26,7 @@ public class Query implements Serializable { private final SimpleBooleanProperty isPeriodic = new SimpleBooleanProperty(false); private final ObjectProperty queryState = new SimpleObjectProperty<>(QueryState.UNKNOWN); private final ObjectProperty type = new SimpleObjectProperty<>(); + private final StringProperty failingLocation = new SimpleStringProperty(""); private BackendInstance backend; @@ -57,6 +61,15 @@ public class Query implements Serializable { } }; + private final Consumer stateConsumer = (state) -> { + for (Component c : Ecdar.getProject().getComponents()) { + if (query.getValue().equals(c.getName())) { + Location location = c.findLocation(state.getLocationTuple().getLocations(0).getId()); + location.setColor(Color.RED); + } + } + }; + public Query(final String query, final String comment, final QueryState queryState) { this.setQuery(query); this.comment.set(comment); @@ -84,6 +97,14 @@ public void setQueryState(final QueryState queryState) { this.queryState.set(queryState); } + public void setFailingLocation(final String location) { + this.failingLocation.set(location); + } + + public String getFailingLocation() { + return this.failingLocation.get(); + } + public ObjectProperty queryStateProperty() { return queryState; } @@ -153,6 +174,9 @@ public Consumer getSuccessConsumer() { public Consumer getFailureConsumer() { return failureConsumer; } + public Consumer getStateConsumer() { + return stateConsumer; + } @Override public JsonObject serialize() { diff --git a/src/main/java/ecdar/backend/QueryHandler.java b/src/main/java/ecdar/backend/QueryHandler.java index 5f76ce1e..671f77ae 100644 --- a/src/main/java/ecdar/backend/QueryHandler.java +++ b/src/main/java/ecdar/backend/QueryHandler.java @@ -100,6 +100,7 @@ public void closeAllBackendConnections() throws IOException { private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { // If the query has been cancelled, ignore the result if (query.getQueryState() == QueryState.UNKNOWN) return; + System.out.println(value); switch (value.getResponseCase()) { case QUERY_OK: @@ -112,7 +113,8 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { } else { query.setQueryState(QueryState.ERROR); query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getRefinement().getReason())); - // query.getSuccessConsumer().accept(false); + query.getSuccessConsumer().accept(false); + query.getStateConsumer().accept(value.getQueryOk().getConsistency().getState()); } break; @@ -124,6 +126,7 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.setQueryState(QueryState.ERROR); query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getConsistency().getReason())); query.getSuccessConsumer().accept(false); + query.getStateConsumer().accept(value.getQueryOk().getConsistency().getState()); } break; @@ -135,6 +138,7 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.setQueryState(QueryState.ERROR); query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getDeterminism().getReason())); query.getSuccessConsumer().accept(false); + query.getStateConsumer().accept(value.getQueryOk().getConsistency().getState()); } break; @@ -146,6 +150,7 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.setQueryState(QueryState.ERROR); query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getImplementation().getReason())); query.getSuccessConsumer().accept(false); + query.getStateConsumer().accept(value.getQueryOk().getConsistency().getState()); } break; @@ -157,6 +162,7 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.setQueryState(QueryState.ERROR); query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getReachability().getReason())); query.getSuccessConsumer().accept(false); + query.getStateConsumer().accept(value.getQueryOk().getConsistency().getState()); } break; From 050ba26d25cadf1a5566c8e094d509281bf79f00 Mon Sep 17 00:00:00 2001 From: WassawRoki <56611129+WassawRoki@users.noreply.github.com> Date: Thu, 3 Nov 2022 14:30:07 +0100 Subject: [PATCH 047/120] review fixes --- .../SimulationInitializationDialogController.java | 6 ++++-- src/main/java/ecdar/controllers/SimulatorController.java | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java index f1e3d1a0..368a00e8 100644 --- a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java +++ b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java @@ -15,9 +15,11 @@ public class SimulationInitializationDialogController implements Initializable { public JFXButton startButton; public static List ListOfComponents = new ArrayList<>(); - + /** + * Function gets list of components to simulation + * and saves it in the public static ListOfComponents + */ public void GetListOfComponentsToSimulate(){ - //Function gets list of components to simulation String componentsToSimulate = simulationComboBox.getSelectionModel().getSelectedItem(); //filters out all components by ignoring operators. Pattern pattern = Pattern.compile("([\\w]*)", Pattern.CASE_INSENSITIVE); diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index fb7f07d4..18423f20 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -55,10 +55,10 @@ public void willShow() { shouldSimulationBeReset = false; } - //if (!firstTimeInSimulator && !new HashSet<>(overviewPresentation.getController().getComponentObservableList()) - // .containsAll(findComponentsInCurrentSimulation())) { - // shouldSimulationBeReset = true; - //} + if (!firstTimeInSimulator && !new HashSet<>(overviewPresentation.getController().getComponentObservableList()) + .containsAll(findComponentsInCurrentSimulation(SimulationInitializationDialogController.ListOfComponents))) { + shouldSimulationBeReset = true; + } if (shouldSimulationBeReset || firstTimeInSimulator) { From 0b8e3c33ee100b6aba1d9c3ffb3e69f90ca0ab6f Mon Sep 17 00:00:00 2001 From: APaludan Date: Thu, 3 Nov 2022 21:40:54 +0100 Subject: [PATCH 048/120] initialize sim --- .../java/ecdar/backend/SimulationHandler.java | 16 ++++++++-------- .../java/ecdar/controllers/EcdarController.java | 2 +- .../controllers/SimulatorOverviewController.java | 1 + .../controllers/TracePaneElementController.java | 3 +++ 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/ecdar/backend/SimulationHandler.java b/src/main/java/ecdar/backend/SimulationHandler.java index 4da6b04d..d44ebf22 100755 --- a/src/main/java/ecdar/backend/SimulationHandler.java +++ b/src/main/java/ecdar/backend/SimulationHandler.java @@ -4,6 +4,7 @@ import EcdarProtoBuf.QueryProtos; import ecdar.Ecdar; import ecdar.abstractions.*; +import ecdar.controllers.SimulationInitializationDialogController; import ecdar.simulation.SimulationState; import ecdar.simulation.SimulationStateSuccessor; import io.grpc.stub.StreamObserver; @@ -27,6 +28,7 @@ */ public class SimulationHandler { public static final String QUERY_PREFIX = "Query: "; + public String composition; private ObjectProperty currentConcreteState = new SimpleObjectProperty<>(); private ObjectProperty initialConcreteState = new SimpleObjectProperty<>(); private ObjectProperty currentTime = new SimpleObjectProperty<>(); @@ -42,7 +44,7 @@ public class SimulationHandler { * For now the string is prefixed with {@link #QUERY_PREFIX} when doing a query simulation * and kept empty when doing system simulations */ - private String currentSimulation = ""; + public String currentSimulation = ""; private final ObservableMap simulationVariables = FXCollections.observableHashMap(); private final ObservableMap simulationClocks = FXCollections.observableHashMap(); @@ -91,7 +93,7 @@ private void initializeSimulation() { //Preparation for the simulation this.system = getSystem(); - this.currentConcreteState.get().setTime(currentTime.getValue()); + //this.currentConcreteState.get().setTime(currentTime.getValue()); this.initialTransitions.clear(); this.successor = null; } @@ -132,14 +134,12 @@ public void onCompleted() { var comInfo = ComponentProtos.ComponentsInfo.newBuilder(); for (Component c : Ecdar.getProject().getComponents()) { - if (currentSimulation.contains(c.getName())) { - comInfo.addComponents(ComponentProtos.Component.newBuilder().setJson(c.serialize().toString()).build()); - } + comInfo.addComponents(ComponentProtos.Component.newBuilder().setJson(c.serialize().toString()).build()); } comInfo.setComponentsHash(comInfo.getComponentsList().hashCode()); var simStartRequest = QueryProtos.SimulationStartRequest.newBuilder(); var simInfo = QueryProtos.SimulationInfo.newBuilder() - .setComponentComposition(currentSimulation) + .setComponentComposition(composition) .setComponentsInfo(comInfo); simStartRequest.setSimulationInfo(simInfo); backendConnection.getStub().withDeadlineAfter(this.backendDriver.getResponseDeadline(), TimeUnit.MILLISECONDS) @@ -293,7 +293,7 @@ public void nextStep(final ecdar.simulation.Transition transition, final BigDeci */ private void updateAllValues() { currentTime.set(currentTime.get().add(delay)); - successor.getState().setTime(currentTime.get()); + //successor.getState().setTime(currentTime.get()); setSimVarAndClocks(); } @@ -406,7 +406,7 @@ public ObservableMap getSimulationClocks() { */ public SimulationState getInitialConcreteState() { // ToDo: Implement - return null; + return initialConcreteState.get(); } public ObjectProperty initialConcreteStateProperty() { diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index ab6efe6f..04a108f4 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -293,6 +293,7 @@ private void initializeDialogs() { simulationInitializationDialog.getController().startButton.setOnMouseClicked(event -> { // ToDo NIELS: Start simulation of selected query + Ecdar.getSimulationHandler().composition = simulationInitializationDialog.getController().simulationComboBox.getSelectionModel().getSelectedItem(); currentMode.setValue(Mode.Simulator); simulationInitializationDialog.close(); }); @@ -665,7 +666,6 @@ private void initializeViewMenu() { if (!Ecdar.getSimulationHandler().isSimulationRunning()) { ArrayList queryOptions = Ecdar.getProject().getQueries().stream().map(Query::getQuery).collect(Collectors.toCollection(ArrayList::new)); - if (!simulationInitializationDialog.getController().simulationComboBox.getItems().equals(queryOptions)) { simulationInitializationDialog.getController().simulationComboBox.getItems().setAll(queryOptions); } diff --git a/src/main/java/ecdar/controllers/SimulatorOverviewController.java b/src/main/java/ecdar/controllers/SimulatorOverviewController.java index 9af4af76..90386d82 100644 --- a/src/main/java/ecdar/controllers/SimulatorOverviewController.java +++ b/src/main/java/ecdar/controllers/SimulatorOverviewController.java @@ -366,6 +366,7 @@ public void unhighlightProcesses() { * @param state The state with the locations to highlight */ public void highlightProcessState(final SimulationState state) { + if (state == null) return; for (int i = 0; i < state.getLocations().size(); i++) { final Pair loc = state.getLocations().get(i); diff --git a/src/main/java/ecdar/controllers/TracePaneElementController.java b/src/main/java/ecdar/controllers/TracePaneElementController.java index 1c2e82fa..5fe116bd 100755 --- a/src/main/java/ecdar/controllers/TracePaneElementController.java +++ b/src/main/java/ecdar/controllers/TracePaneElementController.java @@ -151,6 +151,9 @@ private void insertTraceState(final SimulationState state, final boolean shouldA * @return A string representing the state */ private String traceString(SimulationState state) { + if (state == null) { + return "Initial state"; + } StringBuilder title = new StringBuilder("("); int length = state.getLocations().size(); for (int i = 0; i < length; i++) { From e00cfada026c9aa27083014f8c4e175eea45068d Mon Sep 17 00:00:00 2001 From: APaludan Date: Fri, 4 Nov 2022 09:11:34 +0100 Subject: [PATCH 049/120] det virker en lille smule nu --- .../java/ecdar/backend/SimulationHandler.java | 24 +++++++++---------- .../ecdar/simulation/SimulationState.java | 4 +++- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/main/java/ecdar/backend/SimulationHandler.java b/src/main/java/ecdar/backend/SimulationHandler.java index d44ebf22..4dfa14f9 100755 --- a/src/main/java/ecdar/backend/SimulationHandler.java +++ b/src/main/java/ecdar/backend/SimulationHandler.java @@ -1,10 +1,10 @@ package ecdar.backend; import EcdarProtoBuf.ComponentProtos; +import EcdarProtoBuf.ObjectProtos; import EcdarProtoBuf.QueryProtos; import ecdar.Ecdar; import ecdar.abstractions.*; -import ecdar.controllers.SimulationInitializationDialogController; import ecdar.simulation.SimulationState; import ecdar.simulation.SimulationStateSuccessor; import io.grpc.stub.StreamObserver; @@ -37,7 +37,6 @@ public class SimulationHandler { private EcdarSystem system; private SimulationStateSuccessor successor; private int numberOfSteps; - private SimulationStepResponse currentResponse; /** * A string to keep track what is currently being simulated @@ -90,7 +89,7 @@ private void initializeSimulation() { this.currentConcreteState.set(getInitialConcreteState()); this.initialConcreteState.set(getInitialConcreteState()); this.currentTime = new SimpleObjectProperty<>(BigDecimal.ZERO); - + //Preparation for the simulation this.system = getSystem(); //this.currentConcreteState.get().setTime(currentTime.getValue()); @@ -103,22 +102,22 @@ private void initializeSimulation() { */ public void initialStep() { initializeSimulation(); - + final SimulationState currentState = currentConcreteState.get(); successor = getStateSuccessor(); - + GrpcRequest request = new GrpcRequest(backendConnection -> { StreamObserver responseObserver = new StreamObserver<>() { @Override public void onNext(QueryProtos.SimulationStepResponse value) { System.out.println(value); - currentResponse = value; } - + @Override public void onError(Throwable t) { System.out.println(t.getMessage()); - + Ecdar.showToast("Could not start simulation"); + // Release backend connection backendDriver.addBackendConnection(backendConnection); connections.remove(backendConnection); @@ -145,20 +144,21 @@ public void onCompleted() { backendConnection.getStub().withDeadlineAfter(this.backendDriver.getResponseDeadline(), TimeUnit.MILLISECONDS) .startSimulation(simStartRequest.build(), responseObserver); }, BackendHelper.getDefaultBackendInstance()); - + backendDriver.addRequestToExecutionQueue(request); - + //Save the previous states, and get the new currentConcreteState.set(successor.getState()); this.traceLog.add(currentState); numberOfSteps++; - + //Updates the transitions available availableTransitions.addAll(FXCollections.observableArrayList(successor.getTransitions())); initialTransitions.addAll(availableTransitions); updateAllValues(); + } - + /** * Resets the simulation to the initial location * where the SimulationState is the {@link SimulationHandler#initialConcreteState}, when there are diff --git a/src/main/java/ecdar/simulation/SimulationState.java b/src/main/java/ecdar/simulation/SimulationState.java index 47144a9a..935ec5de 100644 --- a/src/main/java/ecdar/simulation/SimulationState.java +++ b/src/main/java/ecdar/simulation/SimulationState.java @@ -13,7 +13,9 @@ public class SimulationState { public SimulationState(ObjectProtos.State protoBufState) { locations = new ArrayList<>(); - // ToDo: Initialize with correct locations from protoBuf response + for (ObjectProtos.Location location : protoBufState.getLocationTuple().getLocationsList()) { + locations.add(new Pair<>(location.getId(), location.getSpecificComponent().getComponentName())); + } } public void setTime(BigDecimal value) { From 464e4df5ff34baa66ed85ca6c22b793245982f34 Mon Sep 17 00:00:00 2001 From: APaludan Date: Fri, 4 Nov 2022 09:16:47 +0100 Subject: [PATCH 050/120] Update SimulationHandler.java --- src/main/java/ecdar/backend/SimulationHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ecdar/backend/SimulationHandler.java b/src/main/java/ecdar/backend/SimulationHandler.java index 4dfa14f9..d58d7b3b 100755 --- a/src/main/java/ecdar/backend/SimulationHandler.java +++ b/src/main/java/ecdar/backend/SimulationHandler.java @@ -43,7 +43,7 @@ public class SimulationHandler { * For now the string is prefixed with {@link #QUERY_PREFIX} when doing a query simulation * and kept empty when doing system simulations */ - public String currentSimulation = ""; + private String currentSimulation = ""; private final ObservableMap simulationVariables = FXCollections.observableHashMap(); private final ObservableMap simulationClocks = FXCollections.observableHashMap(); From aec21441152f10d3642514d8bd8fad37adb65932 Mon Sep 17 00:00:00 2001 From: Sigurd Date: Fri, 4 Nov 2022 11:23:55 +0100 Subject: [PATCH 051/120] Now sets failing location for all locations in the response --- src/main/java/ecdar/abstractions/Location.java | 14 ++++++++++++++ src/main/java/ecdar/abstractions/Query.java | 5 +++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Location.java b/src/main/java/ecdar/abstractions/Location.java index dd3d0ebe..f0e760d7 100644 --- a/src/main/java/ecdar/abstractions/Location.java +++ b/src/main/java/ecdar/abstractions/Location.java @@ -58,6 +58,7 @@ public class Location implements Circular, Serializable, Nearable, DropDownMenu. private final ObjectProperty reachability = new SimpleObjectProperty<>(); private final SimpleBooleanProperty isLocked = new SimpleBooleanProperty(false); + private boolean failing; public Location() { } @@ -468,6 +469,19 @@ public void deserialize(final JsonObject json) { public String generateNearString() { return "Location " + (!Strings.isNullOrEmpty(getNickname()) ? (getNickname() + " (" + getId() + ")") : getId()); } + + /** + * Sets whether this location failed for the last query + * @param bool if a query responded failure with the location, bool should be true. + */ + public void setFailing(boolean bool) { + this.failing = bool; + } + + public boolean getFailing() { + return this.failing; + } + public enum Type { NORMAL, INITIAL, UNIVERSAL, INCONSISTENT } diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index 85d25ff8..6e97fe4c 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -64,8 +64,9 @@ public class Query implements Serializable { private final Consumer stateConsumer = (state) -> { for (Component c : Ecdar.getProject().getComponents()) { if (query.getValue().equals(c.getName())) { - Location location = c.findLocation(state.getLocationTuple().getLocations(0).getId()); - location.setColor(Color.RED); + for (ObjectProtos.Location location : state.getLocationTuple().getLocationsList()) { + c.findLocation(location.getId()).setFailing(true); + }; } } }; From bbcc2eec668690eff14bbda42fe1b711691a5706 Mon Sep 17 00:00:00 2001 From: Ibra4i <82802664+Ibra4i@users.noreply.github.com> Date: Fri, 28 Oct 2022 09:33:56 +0200 Subject: [PATCH 052/120] Variante, nickname sign replacenment <= >= :) --- src/main/java/ecdar/backend/BackendDriver.java | 1 + src/main/java/ecdar/issues/Error.java | 1 + .../presentations/LocationPresentation.java | 4 ++++ .../ecdar/presentations/TagPresentation.java | 17 +++++++++++++++++ 4 files changed, 23 insertions(+) diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index a3ab0e1a..a7f2fff9 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -341,6 +341,7 @@ private void handleQueryBackendError(Throwable t, ExecutableQuery executableQuer // Each error starts with a capitalized description of the error equal to the gRPC error type encountered String errorType = t.getMessage().split(":\\s+", 2)[0]; + switch (errorType) { case "CANCELLED": executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); diff --git a/src/main/java/ecdar/issues/Error.java b/src/main/java/ecdar/issues/Error.java index 197842eb..f9aedc49 100644 --- a/src/main/java/ecdar/issues/Error.java +++ b/src/main/java/ecdar/issues/Error.java @@ -15,5 +15,6 @@ protected Material getIcon() { public Error(final Predicate presentPredicate, final T subject, final Observable... observables) { super(presentPredicate, subject, observables); + } } diff --git a/src/main/java/ecdar/presentations/LocationPresentation.java b/src/main/java/ecdar/presentations/LocationPresentation.java index 8d2a55af..0fd08d48 100644 --- a/src/main/java/ecdar/presentations/LocationPresentation.java +++ b/src/main/java/ecdar/presentations/LocationPresentation.java @@ -182,6 +182,10 @@ private void initializeTags() { } controller.nicknameTag.replaceSpace(); + controller.invariantTag.replaceSigns(); + controller.nicknameTag.replaceSigns(); + + // Set the layout from the model (if they are not both 0) final Location loc = controller.getLocation(); diff --git a/src/main/java/ecdar/presentations/TagPresentation.java b/src/main/java/ecdar/presentations/TagPresentation.java index d0075be8..f9efd73a 100644 --- a/src/main/java/ecdar/presentations/TagPresentation.java +++ b/src/main/java/ecdar/presentations/TagPresentation.java @@ -283,6 +283,23 @@ public void replaceSpace() { initializeTextAid((JFXTextField) lookup("#textField")); } + void initializeTextAid2(JFXTextField textField) { + textField.textProperty().addListener((obs, oldText, newText) -> { + if (newText.contains("<=")) { + final String updatedString = newText.replace("<=", "\u2264"); + textField.setText(updatedString); + } + if (newText.contains(">=")) { + final String updatedString = newText.replace(">=", "\u2265"); + textField.setText(updatedString); + } + }); + } + public void replaceSigns() { + initializeTextAid2((JFXTextField) lookup("#textField")); + } + + public void requestTextFieldFocus() { final JFXTextField textField = (JFXTextField) lookup("#textField"); Platform.runLater(textField::requestFocus); From b8c9e5eb21dc9dae276ef4d8d8c0719132fa21af Mon Sep 17 00:00:00 2001 From: Emilie Steinmann Date: Fri, 28 Oct 2022 10:41:00 +0200 Subject: [PATCH 053/120] Re-replace unicode to <= and >= before serializing --- src/main/java/ecdar/abstractions/Location.java | 2 +- src/main/java/ecdar/backend/BackendDriver.java | 1 - src/main/java/ecdar/issues/Error.java | 1 - src/main/java/ecdar/presentations/LocationPresentation.java | 3 +-- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Location.java b/src/main/java/ecdar/abstractions/Location.java index b490f5b9..02941b4c 100644 --- a/src/main/java/ecdar/abstractions/Location.java +++ b/src/main/java/ecdar/abstractions/Location.java @@ -161,7 +161,7 @@ public StringProperty idProperty() { } public String getInvariant() { - return invariant.get(); + return invariant.get().replace("\u2264", "<=").replace("\u2665", ">="); } public void setInvariant(final String invariant) { diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index a7f2fff9..a3ab0e1a 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -341,7 +341,6 @@ private void handleQueryBackendError(Throwable t, ExecutableQuery executableQuer // Each error starts with a capitalized description of the error equal to the gRPC error type encountered String errorType = t.getMessage().split(":\\s+", 2)[0]; - switch (errorType) { case "CANCELLED": executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); diff --git a/src/main/java/ecdar/issues/Error.java b/src/main/java/ecdar/issues/Error.java index f9aedc49..197842eb 100644 --- a/src/main/java/ecdar/issues/Error.java +++ b/src/main/java/ecdar/issues/Error.java @@ -15,6 +15,5 @@ protected Material getIcon() { public Error(final Predicate presentPredicate, final T subject, final Observable... observables) { super(presentPredicate, subject, observables); - } } diff --git a/src/main/java/ecdar/presentations/LocationPresentation.java b/src/main/java/ecdar/presentations/LocationPresentation.java index 0fd08d48..b89b5536 100644 --- a/src/main/java/ecdar/presentations/LocationPresentation.java +++ b/src/main/java/ecdar/presentations/LocationPresentation.java @@ -182,9 +182,8 @@ private void initializeTags() { } controller.nicknameTag.replaceSpace(); - controller.invariantTag.replaceSigns(); controller.nicknameTag.replaceSigns(); - + controller.invariantTag.replaceSigns(); // Set the layout from the model (if they are not both 0) From 2610d4f2b1d584682d9baa6370fc10c1460ce1a5 Mon Sep 17 00:00:00 2001 From: Ibra4i <82802664+Ibra4i@users.noreply.github.com> Date: Fri, 4 Nov 2022 09:22:46 +0100 Subject: [PATCH 054/120] Replace Signes to guard fields W --- .../java/ecdar/abstractions/DisplayableEdge.java | 15 ++++++++++++--- src/main/java/ecdar/abstractions/Edge.java | 1 + .../java/ecdar/controllers/EdgeController.java | 1 - .../ecdar/presentations/EdgePresentation.java | 1 - .../ecdar/presentations/NailPresentation.java | 2 +- .../ecdar/presentations/NailPresentation.fxml | 2 +- 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/main/java/ecdar/abstractions/DisplayableEdge.java b/src/main/java/ecdar/abstractions/DisplayableEdge.java index 7a5d62d2..0c6b08f2 100644 --- a/src/main/java/ecdar/abstractions/DisplayableEdge.java +++ b/src/main/java/ecdar/abstractions/DisplayableEdge.java @@ -80,13 +80,22 @@ public void setSelect(final String select) { public StringProperty selectProperty() { return select; } - + // Husk, convert back to <=, >= public String getGuard() { - return guard.get(); + return guard.get().replace("\u2264", "<=").replace("\u2665", ">="); + //return guard.get(); } public void setGuard(final String guard) { - this.guard.set(guard); + /*if(guard.contains("<=")){ + final String updatedString = guard.replace("<=", "\u2264"); + guardProperty().setValue(updatedString); + } + if (guard.contains(">=")) { + final String updatedString = guard.replace(">=", "\u2265"); + guardProperty().setValue(updatedString); + }*/ + this.guard.set(guard); } public StringProperty guardProperty() { diff --git a/src/main/java/ecdar/abstractions/Edge.java b/src/main/java/ecdar/abstractions/Edge.java index 995e2ce8..eea50f04 100644 --- a/src/main/java/ecdar/abstractions/Edge.java +++ b/src/main/java/ecdar/abstractions/Edge.java @@ -161,6 +161,7 @@ public JsonObject serialize() { result.addProperty(STATUS, ioStatus.get().toString()); result.addProperty(SELECT, getSelect()); + //result.addProperty(GUARD, getGuard().replace("\u2264", "<=").replace("\u2665", ">=")); result.addProperty(GUARD, getGuard()); result.addProperty(UPDATE, getUpdate()); result.addProperty(SYNC, getSync()); diff --git a/src/main/java/ecdar/controllers/EdgeController.java b/src/main/java/ecdar/controllers/EdgeController.java index 3610934f..c2135c4c 100644 --- a/src/main/java/ecdar/controllers/EdgeController.java +++ b/src/main/java/ecdar/controllers/EdgeController.java @@ -73,7 +73,6 @@ public void initialize(final URL location, final ResourceBundle resources) { } }); }); - initializeLinksListener(); ensureNailsInFront(); initializeSelectListener(); diff --git a/src/main/java/ecdar/presentations/EdgePresentation.java b/src/main/java/ecdar/presentations/EdgePresentation.java index 993743bd..d2a93642 100644 --- a/src/main/java/ecdar/presentations/EdgePresentation.java +++ b/src/main/java/ecdar/presentations/EdgePresentation.java @@ -15,7 +15,6 @@ public class EdgePresentation extends Group { public EdgePresentation(final DisplayableEdge edge, final Component component) { controller = new EcdarFXMLLoader().loadAndGetController("EdgePresentation.fxml", this); - controller.setEdge(edge); this.edge.bind(controller.edgeProperty()); diff --git a/src/main/java/ecdar/presentations/NailPresentation.java b/src/main/java/ecdar/presentations/NailPresentation.java index 2ab5823f..9a567f95 100644 --- a/src/main/java/ecdar/presentations/NailPresentation.java +++ b/src/main/java/ecdar/presentations/NailPresentation.java @@ -51,6 +51,7 @@ public NailPresentation(final Nail nail, final DisplayableEdge edge, final Compo } controller.propertyTag.setTranslateX(10); + controller.propertyTag.replaceSigns(); controller.propertyTag.setTranslateY(-controller.propertyTag.getHeight()); this.getChildren().add(controller.propertyTag); @@ -74,7 +75,6 @@ private void initializeRadius() { radiusUpdater.accept(controller.getNail().getPropertyType()); } - private void initializePropertyTag() { final TagPresentation propertyTag = controller.propertyTag; final Line propertyTagLine = controller.propertyTagLine; diff --git a/src/main/resources/ecdar/presentations/NailPresentation.fxml b/src/main/resources/ecdar/presentations/NailPresentation.fxml index e0f80e07..63b19881 100644 --- a/src/main/resources/ecdar/presentations/NailPresentation.fxml +++ b/src/main/resources/ecdar/presentations/NailPresentation.fxml @@ -10,7 +10,7 @@ fx:controller="ecdar.controllers.NailController" fx:id="root"> - + From 820e90045a4e095bffaa145e5f4d31b6ea0a1871 Mon Sep 17 00:00:00 2001 From: Emilie Steinmann Date: Fri, 4 Nov 2022 09:35:40 +0100 Subject: [PATCH 055/120] lav static class to refactor code (using two methods only) --- src/main/java/ecdar/abstractions/DisplayableEdge.java | 10 +--------- src/main/java/ecdar/abstractions/Query.java | 8 -------- src/main/java/ecdar/utility/helpers/StringHelper.java | 11 +++++++++++ 3 files changed, 12 insertions(+), 17 deletions(-) create mode 100644 src/main/java/ecdar/utility/helpers/StringHelper.java diff --git a/src/main/java/ecdar/abstractions/DisplayableEdge.java b/src/main/java/ecdar/abstractions/DisplayableEdge.java index 0c6b08f2..c41ec750 100644 --- a/src/main/java/ecdar/abstractions/DisplayableEdge.java +++ b/src/main/java/ecdar/abstractions/DisplayableEdge.java @@ -82,19 +82,11 @@ public StringProperty selectProperty() { } // Husk, convert back to <=, >= public String getGuard() { - return guard.get().replace("\u2264", "<=").replace("\u2665", ">="); + return guard.get().replace("\u2264", "<=").replace("\u2265", ">="); //return guard.get(); } public void setGuard(final String guard) { - /*if(guard.contains("<=")){ - final String updatedString = guard.replace("<=", "\u2264"); - guardProperty().setValue(updatedString); - } - if (guard.contains(">=")) { - final String updatedString = guard.replace(">=", "\u2265"); - guardProperty().setValue(updatedString); - }*/ this.guard.set(guard); } diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index c156e9b5..805ef90e 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -46,14 +46,6 @@ public Query(final JsonObject jsonElement) { initializeRunQuery(); } - public static String RefinementSymbolToUnicode(String stringToReplace){ - return stringToReplace.replace(">=","\u2265").replace("<=","\u2264"); - } - - public static String UnicodeToRefinementSymbol(String stringToReplace){ - return stringToReplace.replace("\u2264","<=").replace("\u2265",">="); - } - public QueryState getQueryState() { return queryState.get(); } diff --git a/src/main/java/ecdar/utility/helpers/StringHelper.java b/src/main/java/ecdar/utility/helpers/StringHelper.java new file mode 100644 index 00000000..e158a4fb --- /dev/null +++ b/src/main/java/ecdar/utility/helpers/StringHelper.java @@ -0,0 +1,11 @@ +package ecdar.utility.helpers; + +public final class StringHelper { + public static String ConvertSymbolsToUnicode(String stringToReplace){ + return stringToReplace.replace(">=","\u2265").replace("<=","\u2264"); + } + + public static String ConvertUnicodeToSymbols(String stringToReplace){ + return stringToReplace.replace("\u2264","<=").replace("\u2265",">="); + } +} From 60d98566fbac01720b05898ee01fab83247a27e5 Mon Sep 17 00:00:00 2001 From: Emilie Steinmann Date: Fri, 4 Nov 2022 10:04:01 +0100 Subject: [PATCH 056/120] Use StringHelper for queries --- src/main/java/ecdar/abstractions/Query.java | 3 ++- src/main/java/ecdar/controllers/QueryController.java | 3 ++- src/main/java/ecdar/presentations/QueryPresentation.java | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index 805ef90e..869f7ead 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -3,6 +3,7 @@ import ecdar.Ecdar; import ecdar.backend.*; import ecdar.controllers.EcdarController; +import ecdar.utility.helpers.StringHelper; import ecdar.utility.helpers.StringValidator; import ecdar.utility.serialize.Serializable; import com.google.gson.JsonObject; @@ -59,7 +60,7 @@ public ObjectProperty queryStateProperty() { } public String getQuery() { - return UnicodeToRefinementSymbol(this.query.get()); + return StringHelper.ConvertUnicodeToSymbols(this.query.get()); } public void setQuery(final String query) { diff --git a/src/main/java/ecdar/controllers/QueryController.java b/src/main/java/ecdar/controllers/QueryController.java index b1da784f..6c584aa4 100644 --- a/src/main/java/ecdar/controllers/QueryController.java +++ b/src/main/java/ecdar/controllers/QueryController.java @@ -8,6 +8,7 @@ import ecdar.abstractions.QueryType; import ecdar.backend.BackendHelper; import ecdar.utility.colors.Color; +import ecdar.utility.helpers.StringHelper; import javafx.application.Platform; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ObservableValue; @@ -40,7 +41,7 @@ public void initialize(URL location, ResourceBundle resources) { queryText.textProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, String oldValue, String newValue) { - queryText.setText(Query.RefinementSymbolToUnicode(newValue)); + queryText.setText(StringHelper.ConvertSymbolsToUnicode(newValue)); } }); } diff --git a/src/main/java/ecdar/presentations/QueryPresentation.java b/src/main/java/ecdar/presentations/QueryPresentation.java index 3af5caed..bf2b96c9 100644 --- a/src/main/java/ecdar/presentations/QueryPresentation.java +++ b/src/main/java/ecdar/presentations/QueryPresentation.java @@ -7,6 +7,7 @@ import ecdar.controllers.QueryController; import ecdar.controllers.EcdarController; import ecdar.utility.colors.Color; +import ecdar.utility.helpers.StringHelper; import ecdar.utility.helpers.StringValidator; import javafx.application.Platform; import javafx.beans.binding.When; @@ -265,7 +266,7 @@ else if (queryState.getStatusCode() == 3) { Label label = new Label(tooltip.getText()); - JFXDialog dialog = new InformationDialogPresentation("Result from query: " + Query.RefinementSymbolToUnicode(controller.getQuery().getQuery()), label); + JFXDialog dialog = new InformationDialogPresentation("Result from query: " + StringHelper.ConvertSymbolsToUnicode(controller.getQuery().getQuery()), label); dialog.show(Ecdar.getPresentation()); }); }); From 32cb66885838d612e872f6fe139adda3123f91d3 Mon Sep 17 00:00:00 2001 From: Ibra4i <82802664+Ibra4i@users.noreply.github.com> Date: Fri, 4 Nov 2022 10:16:46 +0100 Subject: [PATCH 057/120] Refacorizing Guard/Invarient code Yes --- .../ecdar/abstractions/DisplayableEdge.java | 8 ++++---- src/main/java/ecdar/abstractions/Edge.java | 1 - src/main/java/ecdar/abstractions/Location.java | 3 ++- .../ecdar/presentations/TagPresentation.java | 18 +++++------------- .../ecdar/presentations/NailPresentation.fxml | 2 +- 5 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/main/java/ecdar/abstractions/DisplayableEdge.java b/src/main/java/ecdar/abstractions/DisplayableEdge.java index c41ec750..e2bfd7f0 100644 --- a/src/main/java/ecdar/abstractions/DisplayableEdge.java +++ b/src/main/java/ecdar/abstractions/DisplayableEdge.java @@ -6,6 +6,7 @@ import ecdar.utility.colors.Color; import ecdar.utility.helpers.Circular; import ecdar.utility.helpers.MouseCircular; +import ecdar.utility.helpers.StringHelper; import javafx.beans.property.*; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -80,14 +81,13 @@ public void setSelect(final String select) { public StringProperty selectProperty() { return select; } - // Husk, convert back to <=, >= + public String getGuard() { - return guard.get().replace("\u2264", "<=").replace("\u2265", ">="); - //return guard.get(); + return StringHelper.ConvertSymbolsToUnicode(guard.get()); } public void setGuard(final String guard) { - this.guard.set(guard); + this.guard.set(guard); } public StringProperty guardProperty() { diff --git a/src/main/java/ecdar/abstractions/Edge.java b/src/main/java/ecdar/abstractions/Edge.java index eea50f04..995e2ce8 100644 --- a/src/main/java/ecdar/abstractions/Edge.java +++ b/src/main/java/ecdar/abstractions/Edge.java @@ -161,7 +161,6 @@ public JsonObject serialize() { result.addProperty(STATUS, ioStatus.get().toString()); result.addProperty(SELECT, getSelect()); - //result.addProperty(GUARD, getGuard().replace("\u2264", "<=").replace("\u2665", ">=")); result.addProperty(GUARD, getGuard()); result.addProperty(UPDATE, getUpdate()); result.addProperty(SYNC, getSync()); diff --git a/src/main/java/ecdar/abstractions/Location.java b/src/main/java/ecdar/abstractions/Location.java index 02941b4c..18d4cbfd 100644 --- a/src/main/java/ecdar/abstractions/Location.java +++ b/src/main/java/ecdar/abstractions/Location.java @@ -8,6 +8,7 @@ import ecdar.utility.colors.Color; import ecdar.utility.colors.EnabledColor; import ecdar.utility.helpers.Circular; +import ecdar.utility.helpers.StringHelper; import ecdar.utility.serialize.Serializable; import com.google.common.base.Strings; import com.google.gson.Gson; @@ -161,7 +162,7 @@ public StringProperty idProperty() { } public String getInvariant() { - return invariant.get().replace("\u2264", "<=").replace("\u2665", ">="); + return StringHelper.ConvertUnicodeToSymbols(invariant.get()); } public void setInvariant(final String invariant) { diff --git a/src/main/java/ecdar/presentations/TagPresentation.java b/src/main/java/ecdar/presentations/TagPresentation.java index f9efd73a..a4d4610f 100644 --- a/src/main/java/ecdar/presentations/TagPresentation.java +++ b/src/main/java/ecdar/presentations/TagPresentation.java @@ -8,6 +8,7 @@ import ecdar.utility.helpers.LocationAware; import com.jfoenix.controls.JFXTextField; import ecdar.utility.helpers.SelectHelper; +import ecdar.utility.helpers.StringHelper; import javafx.application.Platform; import javafx.beans.binding.When; import javafx.beans.property.*; @@ -283,23 +284,14 @@ public void replaceSpace() { initializeTextAid((JFXTextField) lookup("#textField")); } - void initializeTextAid2(JFXTextField textField) { + public void replaceSigns() { + var textField = (JFXTextField) lookup("#textField"); textField.textProperty().addListener((obs, oldText, newText) -> { - if (newText.contains("<=")) { - final String updatedString = newText.replace("<=", "\u2264"); - textField.setText(updatedString); - } - if (newText.contains(">=")) { - final String updatedString = newText.replace(">=", "\u2265"); - textField.setText(updatedString); - } + textField.setText(StringHelper.ConvertSymbolsToUnicode(newText)); }); } - public void replaceSigns() { - initializeTextAid2((JFXTextField) lookup("#textField")); - } - + public void requestTextFieldFocus() { final JFXTextField textField = (JFXTextField) lookup("#textField"); Platform.runLater(textField::requestFocus); diff --git a/src/main/resources/ecdar/presentations/NailPresentation.fxml b/src/main/resources/ecdar/presentations/NailPresentation.fxml index 63b19881..e0f80e07 100644 --- a/src/main/resources/ecdar/presentations/NailPresentation.fxml +++ b/src/main/resources/ecdar/presentations/NailPresentation.fxml @@ -10,7 +10,7 @@ fx:controller="ecdar.controllers.NailController" fx:id="root"> - + From 4282b1a4bde4883e4cd4cbb07ce5867a129e7333 Mon Sep 17 00:00:00 2001 From: Ibra4i <82802664+Ibra4i@users.noreply.github.com> Date: Fri, 4 Nov 2022 10:21:38 +0100 Subject: [PATCH 058/120] Ooops! --- src/main/java/ecdar/abstractions/DisplayableEdge.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ecdar/abstractions/DisplayableEdge.java b/src/main/java/ecdar/abstractions/DisplayableEdge.java index e2bfd7f0..a93090cd 100644 --- a/src/main/java/ecdar/abstractions/DisplayableEdge.java +++ b/src/main/java/ecdar/abstractions/DisplayableEdge.java @@ -83,7 +83,7 @@ public StringProperty selectProperty() { } public String getGuard() { - return StringHelper.ConvertSymbolsToUnicode(guard.get()); + return StringHelper.ConvertUnicodeToSymbols(guard.get()); } public void setGuard(final String guard) { From 3af14355b08584d16da8a3b0c6e0b921d4ea7453 Mon Sep 17 00:00:00 2001 From: Ibra4i <82802664+Ibra4i@users.noreply.github.com> Date: Fri, 4 Nov 2022 11:23:31 +0100 Subject: [PATCH 059/120] StringHelperTest --- .../java/ecdar/utility/StringHelperTest.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/test/java/ecdar/utility/StringHelperTest.java diff --git a/src/test/java/ecdar/utility/StringHelperTest.java b/src/test/java/ecdar/utility/StringHelperTest.java new file mode 100644 index 00000000..cfe6887d --- /dev/null +++ b/src/test/java/ecdar/utility/StringHelperTest.java @@ -0,0 +1,21 @@ +package ecdar.utility; + +import ecdar.abstractions.Query; +import ecdar.abstractions.QueryState; +import ecdar.utility.helpers.StringHelper; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.platform.commons.util.StringUtils; +import org.junit.runners.Parameterized; + +public class StringHelperTest { + + @Test + public void ConvertSymbolsToUnicodeTest() { + String stringToReplace = "2 >= 4"; + String result = StringHelper.ConvertSymbolsToUnicode(stringToReplace); + String expected = "2 \u2265 4"; + + Assertions.assertEquals(expected, result); + } +} From c6b8485ccd9eb97cf843a69c6a01927abc696625 Mon Sep 17 00:00:00 2001 From: Emilie Steinmann Date: Fri, 4 Nov 2022 11:57:19 +0100 Subject: [PATCH 060/120] Parameterized test --- build.gradle | 1 + .../java/ecdar/utility/StringHelperTest.java | 11 ++++- .../StringHelperTestParameterized.java | 45 +++++++++++++++++++ src/test/java/ecdar/utility/TestRunner.java | 17 +++++++ 4 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 src/test/java/ecdar/utility/StringHelperTestParameterized.java create mode 100644 src/test/java/ecdar/utility/TestRunner.java diff --git a/build.gradle b/build.gradle index 1b049e61..eb688667 100644 --- a/build.gradle +++ b/build.gradle @@ -66,6 +66,7 @@ dependencies { //GRPC: implementation "io.grpc:grpc-protobuf:${grpcVersion}" implementation "io.grpc:grpc-stub:${grpcVersion}" + testImplementation 'junit:junit:4.13.1' compileOnly "org.apache.tomcat:annotations-api:6.0.53" // examples/advanced need this for JsonFormat diff --git a/src/test/java/ecdar/utility/StringHelperTest.java b/src/test/java/ecdar/utility/StringHelperTest.java index cfe6887d..a70527be 100644 --- a/src/test/java/ecdar/utility/StringHelperTest.java +++ b/src/test/java/ecdar/utility/StringHelperTest.java @@ -3,19 +3,26 @@ import ecdar.abstractions.Query; import ecdar.abstractions.QueryState; import ecdar.utility.helpers.StringHelper; +import org.junit.Before; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.platform.commons.util.StringUtils; +import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -public class StringHelperTest { +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.assertEquals; +public class StringHelperTest { @Test public void ConvertSymbolsToUnicodeTest() { String stringToReplace = "2 >= 4"; String result = StringHelper.ConvertSymbolsToUnicode(stringToReplace); String expected = "2 \u2265 4"; - Assertions.assertEquals(expected, result); + assertEquals(expected, result); } } + diff --git a/src/test/java/ecdar/utility/StringHelperTestParameterized.java b/src/test/java/ecdar/utility/StringHelperTestParameterized.java new file mode 100644 index 00000000..89cf59fb --- /dev/null +++ b/src/test/java/ecdar/utility/StringHelperTestParameterized.java @@ -0,0 +1,45 @@ +package ecdar.utility; + +import ecdar.utility.helpers.StringHelper; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@RunWith(Parameterized.class) +public class StringHelperTestParameterized { + private String inputString; + private String expectedResult; + + // Each parameter should be placed as an argument here + // Every time runner triggers, it will pass the arguments + // from parameters we defined in primeNumbers() method + + public StringHelperTestParameterized(String inputString, String expectedResult) { + this.inputString = inputString; + this.expectedResult = expectedResult; + } + + @Parameterized.Parameters + public static Collection input() { + return Arrays.asList(new Object[][]{ + {"2 >= 4", "2 \u2265 4"}, + {"2 <= 4", "2 \u2264 4"}, + {"2 == 4", "2 == 4"}, + {"2 > 4", "2 \u2265 4"}, + {"2 < 4", "2 \u2264 4"} + }); + } + + @Test + public void testStringHelperConversionToUnicode() { + System.out.println(inputString + " bliver sammenlignet med " + expectedResult); + assertEquals(expectedResult, + StringHelper.ConvertSymbolsToUnicode(inputString)); + } +} diff --git a/src/test/java/ecdar/utility/TestRunner.java b/src/test/java/ecdar/utility/TestRunner.java new file mode 100644 index 00000000..24f4bbf2 --- /dev/null +++ b/src/test/java/ecdar/utility/TestRunner.java @@ -0,0 +1,17 @@ +package ecdar.utility; + +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; + +public class TestRunner { + public static void main(String[] args) { + Result result = JUnitCore.runClasses(StringHelperTestParameterized.class); + + for (Failure failure : result.getFailures()) { + System.out.println(failure.toString()); + } + + System.out.println(result.wasSuccessful()); + } +} From 2c3a197a35913e077d578fc0865d3023f27117c5 Mon Sep 17 00:00:00 2001 From: Emilie Steinmann Date: Fri, 4 Nov 2022 12:07:48 +0100 Subject: [PATCH 061/120] Parameterized test af convert unicode to symbol --- ...a => StringHelperTestSymbolToUnicode.java} | 9 ++-- .../StringHelperTestUnicodeToSymbol.java | 44 +++++++++++++++++++ src/test/java/ecdar/utility/TestRunner.java | 14 ++++-- 3 files changed, 58 insertions(+), 9 deletions(-) rename src/test/java/ecdar/utility/{StringHelperTestParameterized.java => StringHelperTestSymbolToUnicode.java} (83%) create mode 100644 src/test/java/ecdar/utility/StringHelperTestUnicodeToSymbol.java diff --git a/src/test/java/ecdar/utility/StringHelperTestParameterized.java b/src/test/java/ecdar/utility/StringHelperTestSymbolToUnicode.java similarity index 83% rename from src/test/java/ecdar/utility/StringHelperTestParameterized.java rename to src/test/java/ecdar/utility/StringHelperTestSymbolToUnicode.java index 89cf59fb..c32cfcb2 100644 --- a/src/test/java/ecdar/utility/StringHelperTestParameterized.java +++ b/src/test/java/ecdar/utility/StringHelperTestSymbolToUnicode.java @@ -1,7 +1,6 @@ package ecdar.utility; import ecdar.utility.helpers.StringHelper; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -12,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; @RunWith(Parameterized.class) -public class StringHelperTestParameterized { +public class StringHelperTestSymbolToUnicode { private String inputString; private String expectedResult; @@ -20,7 +19,7 @@ public class StringHelperTestParameterized { // Every time runner triggers, it will pass the arguments // from parameters we defined in primeNumbers() method - public StringHelperTestParameterized(String inputString, String expectedResult) { + public StringHelperTestSymbolToUnicode(String inputString, String expectedResult) { this.inputString = inputString; this.expectedResult = expectedResult; } @@ -31,8 +30,8 @@ public static Collection input() { {"2 >= 4", "2 \u2265 4"}, {"2 <= 4", "2 \u2264 4"}, {"2 == 4", "2 == 4"}, - {"2 > 4", "2 \u2265 4"}, - {"2 < 4", "2 \u2264 4"} + {"2 > 4", "2 > 4"}, + {"2 < 4", "2 < 4"} }); } diff --git a/src/test/java/ecdar/utility/StringHelperTestUnicodeToSymbol.java b/src/test/java/ecdar/utility/StringHelperTestUnicodeToSymbol.java new file mode 100644 index 00000000..6872a197 --- /dev/null +++ b/src/test/java/ecdar/utility/StringHelperTestUnicodeToSymbol.java @@ -0,0 +1,44 @@ +package ecdar.utility; + +import ecdar.utility.helpers.StringHelper; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@RunWith(Parameterized.class) +public class StringHelperTestUnicodeToSymbol { + private String inputString; + private String expectedResult; + + // Each parameter should be placed as an argument here + // Every time runner triggers, it will pass the arguments + // from parameters we defined in primeNumbers() method + + public StringHelperTestUnicodeToSymbol(String inputString, String expectedResult) { + this.inputString = inputString; + this.expectedResult = expectedResult; + } + + @Parameterized.Parameters + public static Collection input() { + return Arrays.asList(new Object[][]{ + {"2 \u2265 4", "2 >= 4"}, + {"2 \u2264 4", "2 <= 4"}, + {"2 == 4", "2 == 4"}, + {"2 > 4", "2 > 4"}, + {"2 < 4", "2 < 4"} + }); + } + + @Test + public void testStringHelperConversionToUnicode() { + System.out.println(inputString + " bliver sammenlignet med " + expectedResult); + assertEquals(expectedResult, + StringHelper.ConvertUnicodeToSymbols(inputString)); + } +} diff --git a/src/test/java/ecdar/utility/TestRunner.java b/src/test/java/ecdar/utility/TestRunner.java index 24f4bbf2..7b4366c5 100644 --- a/src/test/java/ecdar/utility/TestRunner.java +++ b/src/test/java/ecdar/utility/TestRunner.java @@ -6,12 +6,18 @@ public class TestRunner { public static void main(String[] args) { - Result result = JUnitCore.runClasses(StringHelperTestParameterized.class); - - for (Failure failure : result.getFailures()) { + // ------------ Test 1: symbol to unicode ------------------ + Result result1 = JUnitCore.runClasses(StringHelperTestSymbolToUnicode.class); + for (Failure failure : result1.getFailures()) { System.out.println(failure.toString()); } + System.out.println("Result of test StringHelperTestSymbolToUnicode: " + result1.wasSuccessful()); - System.out.println(result.wasSuccessful()); + // ------------ Test 2: unicode to symbol ------------------ + Result result2 = JUnitCore.runClasses(StringHelperTestUnicodeToSymbol.class); + for (Failure failure : result2.getFailures()) { + System.out.println(failure.toString()); + } + System.out.println("Result of test StringHelperTestUnicodeToSymbol: " + result2.wasSuccessful()); } } From 629affbe3cd1e80fe7bf4603870ab05d11f82bc4 Mon Sep 17 00:00:00 2001 From: Emilie Steinmann Date: Fri, 4 Nov 2022 14:33:46 +0100 Subject: [PATCH 062/120] minor fixes for PR --- build.gradle | 1 - src/test/java/ecdar/utility/StringHelperTestSymbolToUnicode.java | 1 - src/test/java/ecdar/utility/StringHelperTestUnicodeToSymbol.java | 1 - 3 files changed, 3 deletions(-) diff --git a/build.gradle b/build.gradle index eb688667..1b049e61 100644 --- a/build.gradle +++ b/build.gradle @@ -66,7 +66,6 @@ dependencies { //GRPC: implementation "io.grpc:grpc-protobuf:${grpcVersion}" implementation "io.grpc:grpc-stub:${grpcVersion}" - testImplementation 'junit:junit:4.13.1' compileOnly "org.apache.tomcat:annotations-api:6.0.53" // examples/advanced need this for JsonFormat diff --git a/src/test/java/ecdar/utility/StringHelperTestSymbolToUnicode.java b/src/test/java/ecdar/utility/StringHelperTestSymbolToUnicode.java index c32cfcb2..4b179ee6 100644 --- a/src/test/java/ecdar/utility/StringHelperTestSymbolToUnicode.java +++ b/src/test/java/ecdar/utility/StringHelperTestSymbolToUnicode.java @@ -37,7 +37,6 @@ public static Collection input() { @Test public void testStringHelperConversionToUnicode() { - System.out.println(inputString + " bliver sammenlignet med " + expectedResult); assertEquals(expectedResult, StringHelper.ConvertSymbolsToUnicode(inputString)); } diff --git a/src/test/java/ecdar/utility/StringHelperTestUnicodeToSymbol.java b/src/test/java/ecdar/utility/StringHelperTestUnicodeToSymbol.java index 6872a197..501cd76f 100644 --- a/src/test/java/ecdar/utility/StringHelperTestUnicodeToSymbol.java +++ b/src/test/java/ecdar/utility/StringHelperTestUnicodeToSymbol.java @@ -37,7 +37,6 @@ public static Collection input() { @Test public void testStringHelperConversionToUnicode() { - System.out.println(inputString + " bliver sammenlignet med " + expectedResult); assertEquals(expectedResult, StringHelper.ConvertUnicodeToSymbols(inputString)); } From 02e3d5b74622c09883a75ec2753e65b591895631 Mon Sep 17 00:00:00 2001 From: Ibra4i <82802664+Ibra4i@users.noreply.github.com> Date: Fri, 4 Nov 2022 14:11:39 +0100 Subject: [PATCH 063/120] Deleted --- .../java/ecdar/utility/StringHelperTest.java | 28 ------------------- 1 file changed, 28 deletions(-) delete mode 100644 src/test/java/ecdar/utility/StringHelperTest.java diff --git a/src/test/java/ecdar/utility/StringHelperTest.java b/src/test/java/ecdar/utility/StringHelperTest.java deleted file mode 100644 index a70527be..00000000 --- a/src/test/java/ecdar/utility/StringHelperTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package ecdar.utility; - -import ecdar.abstractions.Query; -import ecdar.abstractions.QueryState; -import ecdar.utility.helpers.StringHelper; -import org.junit.Before; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.platform.commons.util.StringUtils; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import java.util.Arrays; -import java.util.Collection; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class StringHelperTest { - @Test - public void ConvertSymbolsToUnicodeTest() { - String stringToReplace = "2 >= 4"; - String result = StringHelper.ConvertSymbolsToUnicode(stringToReplace); - String expected = "2 \u2265 4"; - - assertEquals(expected, result); - } -} - From b08d731eb688e57c922e04cf1ea1b6ac03858ad6 Mon Sep 17 00:00:00 2001 From: Mads Mogensen Date: Mon, 7 Nov 2022 13:53:47 +0100 Subject: [PATCH 064/120] Make parameterized tests run with gradle - Import Junit parameterized tests - Remove TestRunner - Merge StringHelper tests into one file. --- build.gradle | 1 + .../java/ecdar/utility/StringHelperTest.java | 45 +++++++++++++++++++ .../StringHelperTestSymbolToUnicode.java | 43 ------------------ .../StringHelperTestUnicodeToSymbol.java | 43 ------------------ src/test/java/ecdar/utility/TestRunner.java | 23 ---------- 5 files changed, 46 insertions(+), 109 deletions(-) create mode 100644 src/test/java/ecdar/utility/StringHelperTest.java delete mode 100644 src/test/java/ecdar/utility/StringHelperTestSymbolToUnicode.java delete mode 100644 src/test/java/ecdar/utility/StringHelperTestUnicodeToSymbol.java delete mode 100644 src/test/java/ecdar/utility/TestRunner.java diff --git a/build.gradle b/build.gradle index 1b049e61..d6591641 100644 --- a/build.gradle +++ b/build.gradle @@ -79,6 +79,7 @@ dependencies { testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.8.2' testImplementation group: 'org.testfx', name: 'testfx-junit', version: '4.0.15-alpha' testImplementation group: 'org.testfx', name: 'openjfx-monocle', version: 'jdk-12.0.1+2' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '5.8.2' } diff --git a/src/test/java/ecdar/utility/StringHelperTest.java b/src/test/java/ecdar/utility/StringHelperTest.java new file mode 100644 index 00000000..36cef770 --- /dev/null +++ b/src/test/java/ecdar/utility/StringHelperTest.java @@ -0,0 +1,45 @@ +package ecdar.utility; + +import ecdar.utility.helpers.StringHelper; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class StringHelperTest { + @ParameterizedTest + @ValueSource(strings = { + "2 >= 4;2 \u2265 4", + "2 <= 4;2 \u2264 4", + "2 == 4;2 == 4", + "2 > 4;2 > 4", + "2 < 4;2 < 4", + }) + void convertSymbolsToUnicode(String inputAndExpectedOutput) { + var split = inputAndExpectedOutput.split(";"); + var input = split[0]; + var expectedOutput = split[1]; + + var actualOutput = StringHelper.ConvertSymbolsToUnicode(input); + + assertEquals(expectedOutput, actualOutput); + } + + @ParameterizedTest + @ValueSource(strings = { + "2 \u2265 4;2 >= 4", + "2 \u2264 4;2 <= 4", + "2 == 4;2 == 4", + "2 > 4;2 > 4", + "2 < 4;2 < 4" + }) + void convertUnicodeToSymbols(String inputAndExpectedOutput) { + var split = inputAndExpectedOutput.split(";"); + var input = split[0]; + var expectedOutput = split[1]; + + var actualOutput = StringHelper.ConvertUnicodeToSymbols(input); + + assertEquals(expectedOutput, actualOutput); + } +} diff --git a/src/test/java/ecdar/utility/StringHelperTestSymbolToUnicode.java b/src/test/java/ecdar/utility/StringHelperTestSymbolToUnicode.java deleted file mode 100644 index 4b179ee6..00000000 --- a/src/test/java/ecdar/utility/StringHelperTestSymbolToUnicode.java +++ /dev/null @@ -1,43 +0,0 @@ -package ecdar.utility; - -import ecdar.utility.helpers.StringHelper; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import java.util.Arrays; -import java.util.Collection; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -@RunWith(Parameterized.class) -public class StringHelperTestSymbolToUnicode { - private String inputString; - private String expectedResult; - - // Each parameter should be placed as an argument here - // Every time runner triggers, it will pass the arguments - // from parameters we defined in primeNumbers() method - - public StringHelperTestSymbolToUnicode(String inputString, String expectedResult) { - this.inputString = inputString; - this.expectedResult = expectedResult; - } - - @Parameterized.Parameters - public static Collection input() { - return Arrays.asList(new Object[][]{ - {"2 >= 4", "2 \u2265 4"}, - {"2 <= 4", "2 \u2264 4"}, - {"2 == 4", "2 == 4"}, - {"2 > 4", "2 > 4"}, - {"2 < 4", "2 < 4"} - }); - } - - @Test - public void testStringHelperConversionToUnicode() { - assertEquals(expectedResult, - StringHelper.ConvertSymbolsToUnicode(inputString)); - } -} diff --git a/src/test/java/ecdar/utility/StringHelperTestUnicodeToSymbol.java b/src/test/java/ecdar/utility/StringHelperTestUnicodeToSymbol.java deleted file mode 100644 index 501cd76f..00000000 --- a/src/test/java/ecdar/utility/StringHelperTestUnicodeToSymbol.java +++ /dev/null @@ -1,43 +0,0 @@ -package ecdar.utility; - -import ecdar.utility.helpers.StringHelper; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import java.util.Arrays; -import java.util.Collection; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -@RunWith(Parameterized.class) -public class StringHelperTestUnicodeToSymbol { - private String inputString; - private String expectedResult; - - // Each parameter should be placed as an argument here - // Every time runner triggers, it will pass the arguments - // from parameters we defined in primeNumbers() method - - public StringHelperTestUnicodeToSymbol(String inputString, String expectedResult) { - this.inputString = inputString; - this.expectedResult = expectedResult; - } - - @Parameterized.Parameters - public static Collection input() { - return Arrays.asList(new Object[][]{ - {"2 \u2265 4", "2 >= 4"}, - {"2 \u2264 4", "2 <= 4"}, - {"2 == 4", "2 == 4"}, - {"2 > 4", "2 > 4"}, - {"2 < 4", "2 < 4"} - }); - } - - @Test - public void testStringHelperConversionToUnicode() { - assertEquals(expectedResult, - StringHelper.ConvertUnicodeToSymbols(inputString)); - } -} diff --git a/src/test/java/ecdar/utility/TestRunner.java b/src/test/java/ecdar/utility/TestRunner.java deleted file mode 100644 index 7b4366c5..00000000 --- a/src/test/java/ecdar/utility/TestRunner.java +++ /dev/null @@ -1,23 +0,0 @@ -package ecdar.utility; - -import org.junit.runner.JUnitCore; -import org.junit.runner.Result; -import org.junit.runner.notification.Failure; - -public class TestRunner { - public static void main(String[] args) { - // ------------ Test 1: symbol to unicode ------------------ - Result result1 = JUnitCore.runClasses(StringHelperTestSymbolToUnicode.class); - for (Failure failure : result1.getFailures()) { - System.out.println(failure.toString()); - } - System.out.println("Result of test StringHelperTestSymbolToUnicode: " + result1.wasSuccessful()); - - // ------------ Test 2: unicode to symbol ------------------ - Result result2 = JUnitCore.runClasses(StringHelperTestUnicodeToSymbol.class); - for (Failure failure : result2.getFailures()) { - System.out.println(failure.toString()); - } - System.out.println("Result of test StringHelperTestUnicodeToSymbol: " + result2.wasSuccessful()); - } -} From 82f2969d2f2a054736c274a77068280b34241b42 Mon Sep 17 00:00:00 2001 From: Sigurd Date: Fri, 4 Nov 2022 14:08:31 +0100 Subject: [PATCH 065/120] Paint locations red on failure response. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Victor Doré Hansen --- .../java/ecdar/abstractions/Location.java | 8 ++++++-- .../presentations/LocationPresentation.java | 20 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Location.java b/src/main/java/ecdar/abstractions/Location.java index f0e760d7..613a65e5 100644 --- a/src/main/java/ecdar/abstractions/Location.java +++ b/src/main/java/ecdar/abstractions/Location.java @@ -58,7 +58,7 @@ public class Location implements Circular, Serializable, Nearable, DropDownMenu. private final ObjectProperty reachability = new SimpleObjectProperty<>(); private final SimpleBooleanProperty isLocked = new SimpleBooleanProperty(false); - private boolean failing; + private final BooleanProperty failing = new SimpleBooleanProperty(false); public Location() { } @@ -475,10 +475,14 @@ public String generateNearString() { * @param bool if a query responded failure with the location, bool should be true. */ public void setFailing(boolean bool) { - this.failing = bool; + this.failing.set(bool); } public boolean getFailing() { + return this.failing.get(); + } + + public BooleanProperty failingProperty() { return this.failing; } diff --git a/src/main/java/ecdar/presentations/LocationPresentation.java b/src/main/java/ecdar/presentations/LocationPresentation.java index 8d2a55af..2c471390 100644 --- a/src/main/java/ecdar/presentations/LocationPresentation.java +++ b/src/main/java/ecdar/presentations/LocationPresentation.java @@ -158,6 +158,7 @@ private void initializeIdLabel() { final ObjectProperty color = location.colorProperty(); final ObjectProperty colorIntensity = location.colorIntensityProperty(); + final BooleanProperty failing = location.failingProperty(); // Delegate to style the label based on the color of the location final BiConsumer updateColor = (newColor, newIntensity) -> { @@ -165,6 +166,14 @@ private void initializeIdLabel() { ds.setColor(newColor.getColor(newIntensity)); }; + final Consumer handleFailingUpdate = (isFailing) -> { + if(isFailing) { + updateColor.accept(Color.RED, colorIntensity.get()); + } else { + //ToDo: Paint to previous color + } + }; + updateColorDelegates.add(updateColor); // Set the initial color @@ -172,6 +181,7 @@ private void initializeIdLabel() { // Update the color of the circle when the color of the location is updated color.addListener((obs, old, newColor) -> updateColor.accept(newColor, colorIntensity.get())); + failing.addListener((obs, old, newFailing) -> handleFailingUpdate.accept(newFailing)); } private void initializeTags() { @@ -405,6 +415,7 @@ protected void interpolate(final double frac) { // Update the colors final ObjectProperty color = location.colorProperty(); final ObjectProperty colorIntensity = location.colorIntensityProperty(); + final BooleanProperty failing = location.failingProperty(); // Delegate to style the label based on the color of the location final BiConsumer updateColor = (newColor, newIntensity) -> { @@ -418,6 +429,14 @@ protected void interpolate(final double frac) { committedShape.setStroke(newColor.getColor(newIntensity.next(2))); }; + final Consumer handleFailingUpdate = (isFailing) -> { + if(isFailing) { + updateColor.accept(Color.RED, colorIntensity.get()); + } else { + //ToDo: Paint to previous color + } + }; + updateColorDelegates.add(updateColor); // Set the initial color @@ -425,6 +444,7 @@ protected void interpolate(final double frac) { // Update the color of the circle when the color of the location is updated color.addListener((obs, old, newColor) -> updateColor.accept(newColor, colorIntensity.get())); + failing.addListener((obs, old, newFailing) -> handleFailingUpdate.accept(newFailing)); } private void initializeTypeGraphics() { From bcab56722e19a6833bba3e76da73b027e5b76b3f Mon Sep 17 00:00:00 2001 From: Sigurd Date: Mon, 7 Nov 2022 14:10:27 +0100 Subject: [PATCH 066/120] Reset color and failing state on new query. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Victor Doré Hanse --- .../java/ecdar/abstractions/Component.java | 18 ++++++++++++++++++ src/main/java/ecdar/abstractions/Query.java | 10 +++++++--- .../ecdar/controllers/EditorController.java | 2 +- .../presentations/LocationPresentation.java | 5 +++-- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Component.java b/src/main/java/ecdar/abstractions/Component.java index d3da9db1..36201250 100644 --- a/src/main/java/ecdar/abstractions/Component.java +++ b/src/main/java/ecdar/abstractions/Component.java @@ -38,6 +38,7 @@ public class Component extends HighLevelModelObject implements Boxed { // Verification properties private final ObservableList locations = FXCollections.observableArrayList(); + private final ObservableList failingLocations = FXCollections.observableArrayList(); private final ObservableList edges = FXCollections.observableArrayList(); private final ObservableList inputStrings = FXCollections.observableArrayList(); private final ObservableList outputStrings = FXCollections.observableArrayList(); @@ -486,6 +487,23 @@ public boolean removeLocation(final Location location) { return locations.remove(location); } + public boolean addFailingLocation(final String locationId) { + Location failingLocation = findLocation(locationId); + failingLocation.setFailing(true); + return failingLocations.add(failingLocation); + } + + public void removeFailingLocations() { + for (Location location : failingLocations) { + location.setFailing(false); + failingLocations.remove(location); + } + } + + public ObservableList getFailingLocations() { + return failingLocations; + } + /** * Returns all DisplayableEdges of the component (returning a list potentially containing GroupEdges and Edges) * @return All visual edges of the component diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index 6e97fe4c..927c3fde 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -32,6 +32,9 @@ public class Query implements Serializable { private final Consumer successConsumer = (aBoolean) -> { if (aBoolean) { + for (Component c : Ecdar.getProject().getComponents()) { + c.removeFailingLocations(); + } setQueryState(QueryState.SUCCESSFUL); } else { setQueryState(QueryState.ERROR); @@ -63,10 +66,11 @@ public class Query implements Serializable { private final Consumer stateConsumer = (state) -> { for (Component c : Ecdar.getProject().getComponents()) { - if (query.getValue().equals(c.getName())) { + c.removeFailingLocations(); + if (query.getValue().contains(c.getName())) { for (ObjectProtos.Location location : state.getLocationTuple().getLocationsList()) { - c.findLocation(location.getId()).setFailing(true); - }; + c.addFailingLocation(location.getId()); + } } } }; diff --git a/src/main/java/ecdar/controllers/EditorController.java b/src/main/java/ecdar/controllers/EditorController.java index 605cc6fd..27ab5245 100644 --- a/src/main/java/ecdar/controllers/EditorController.java +++ b/src/main/java/ecdar/controllers/EditorController.java @@ -153,7 +153,7 @@ void scaleEdgeStatusToggle(double size) { * Handles the change of color on selected objects * * @param enabledColor The new color for the selected objects - * @param previousColor The color old color of the selected objects + * @param previousColor The old color of the selected objects */ public void changeColorOnSelectedElements(final EnabledColor enabledColor, final List> previousColor) { diff --git a/src/main/java/ecdar/presentations/LocationPresentation.java b/src/main/java/ecdar/presentations/LocationPresentation.java index 2c471390..59b0f6ea 100644 --- a/src/main/java/ecdar/presentations/LocationPresentation.java +++ b/src/main/java/ecdar/presentations/LocationPresentation.java @@ -15,6 +15,7 @@ import javafx.scene.effect.DropShadow; import javafx.scene.shape.*; import javafx.util.Duration; +import javafx.util.Pair; import java.util.ArrayList; import java.util.List; @@ -170,7 +171,7 @@ private void initializeIdLabel() { if(isFailing) { updateColor.accept(Color.RED, colorIntensity.get()); } else { - //ToDo: Paint to previous color + updateColor.accept(location.getColor(), colorIntensity.get()); } }; @@ -433,7 +434,7 @@ protected void interpolate(final double frac) { if(isFailing) { updateColor.accept(Color.RED, colorIntensity.get()); } else { - //ToDo: Paint to previous color + updateColor.accept(location.getColor(), colorIntensity.get()); } }; From 37457e312dc9f973e0e3607efb1525bffe48e5f4 Mon Sep 17 00:00:00 2001 From: martin Date: Mon, 7 Nov 2022 14:21:50 +0100 Subject: [PATCH 067/120] displays failure message, still needs component name --- src/main/java/ecdar/presentations/QueryPresentation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ecdar/presentations/QueryPresentation.java b/src/main/java/ecdar/presentations/QueryPresentation.java index 793ac2fa..1dde50d2 100644 --- a/src/main/java/ecdar/presentations/QueryPresentation.java +++ b/src/main/java/ecdar/presentations/QueryPresentation.java @@ -194,7 +194,7 @@ private void initializeStateIndicator() { } } else if (queryState.getStatusCode() == 2){ - this.tooltip.setText("This query was not a success!"); + this.tooltip.setText("This query was not a success: " + controller.getQuery().getCurrentErrors()); } else if (queryState.getStatusCode() == 3) { this.tooltip.setText("The query has not been executed yet"); From 550758a0305c9167dd3ad0b53932614905207a79 Mon Sep 17 00:00:00 2001 From: Sigurd Date: Mon, 7 Nov 2022 14:38:54 +0100 Subject: [PATCH 068/120] Fixed java.util.ConcurrentModificationException thrown when removing failing locations --- src/main/java/ecdar/abstractions/Component.java | 2 +- src/main/java/ecdar/backend/QueryHandler.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/ecdar/abstractions/Component.java b/src/main/java/ecdar/abstractions/Component.java index 36201250..b0126207 100644 --- a/src/main/java/ecdar/abstractions/Component.java +++ b/src/main/java/ecdar/abstractions/Component.java @@ -496,8 +496,8 @@ public boolean addFailingLocation(final String locationId) { public void removeFailingLocations() { for (Location location : failingLocations) { location.setFailing(false); - failingLocations.remove(location); } + failingLocations.removeAll(); } public ObservableList getFailingLocations() { diff --git a/src/main/java/ecdar/backend/QueryHandler.java b/src/main/java/ecdar/backend/QueryHandler.java index 671f77ae..8ec9214a 100644 --- a/src/main/java/ecdar/backend/QueryHandler.java +++ b/src/main/java/ecdar/backend/QueryHandler.java @@ -57,6 +57,7 @@ public void onNext(QueryProtos.QueryResponse value) { @Override public void onError(Throwable t) { + System.out.println(t); handleQueryBackendError(t, query); // Release backend connection From 02e642252466acaee986e718a3f731b15b98a53c Mon Sep 17 00:00:00 2001 From: martin Date: Mon, 7 Nov 2022 15:05:12 +0100 Subject: [PATCH 069/120] Modified build.gradle, fixes java.lang.illegalAccessError --- build.gradle | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f2c68089..6364d14a 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,21 @@ javafx { modules = ['javafx.controls', 'javafx.fxml', 'javafx.swing'] } +run { + mainClassName = 'com.jfxbase/com.jfxbase.sample.Main' + applicationDefaultJvmArgs += [ + "--add-opens", "javafx.graphics/javafx.css=ALL-UNNAMED", + "--add-opens", "javafx.base/com.sun.javafx.runtime=ALL-UNNAMED", + "--add-opens", "javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED", + "--add-opens", "javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED", + "--add-opens", "javafx.base/com.sun.javafx.binding=ALL-UNNAMED", + "--add-opens", "javafx.base/com.sun.javafx.event=ALL-UNNAMED", + "--add-opens", "javafx.graphics/com.sun.javafx.stage=ALL-UNNAMED", + "--add-opens", "javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED", + ] +} + + group 'ecdar' if (project.hasProperty('ecdarVersion')) { version = project.ecdarVersion @@ -40,6 +55,8 @@ repositories { mavenCentral() } + + def grpcVersion = '1.41.0' // CURRENT_GRPC_VERSION def protobufVersion = '3.17.2' def protocVersion = protobufVersion @@ -128,4 +145,4 @@ sourceSets { srcDirs 'build/generated/source/proto/main/java' } } -} \ No newline at end of file +} From 9c50f147b3ca72c374db6dce2b8a09b713b70943 Mon Sep 17 00:00:00 2001 From: martin Date: Mon, 7 Nov 2022 15:11:01 +0100 Subject: [PATCH 070/120] Revert "Modified build.gradle, fixes java.lang.illegalAccessError" This reverts commit 02e642252466acaee986e718a3f731b15b98a53c additions will be added on another branch. --- build.gradle | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/build.gradle b/build.gradle index 6364d14a..f2c68089 100644 --- a/build.gradle +++ b/build.gradle @@ -10,21 +10,6 @@ javafx { modules = ['javafx.controls', 'javafx.fxml', 'javafx.swing'] } -run { - mainClassName = 'com.jfxbase/com.jfxbase.sample.Main' - applicationDefaultJvmArgs += [ - "--add-opens", "javafx.graphics/javafx.css=ALL-UNNAMED", - "--add-opens", "javafx.base/com.sun.javafx.runtime=ALL-UNNAMED", - "--add-opens", "javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED", - "--add-opens", "javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED", - "--add-opens", "javafx.base/com.sun.javafx.binding=ALL-UNNAMED", - "--add-opens", "javafx.base/com.sun.javafx.event=ALL-UNNAMED", - "--add-opens", "javafx.graphics/com.sun.javafx.stage=ALL-UNNAMED", - "--add-opens", "javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED", - ] -} - - group 'ecdar' if (project.hasProperty('ecdarVersion')) { version = project.ecdarVersion @@ -55,8 +40,6 @@ repositories { mavenCentral() } - - def grpcVersion = '1.41.0' // CURRENT_GRPC_VERSION def protobufVersion = '3.17.2' def protocVersion = protobufVersion @@ -145,4 +128,4 @@ sourceSets { srcDirs 'build/generated/source/proto/main/java' } } -} +} \ No newline at end of file From 0f6f498515078e7c9d01f803b7d21198d8a4d371 Mon Sep 17 00:00:00 2001 From: Sigurd Date: Mon, 7 Nov 2022 15:17:11 +0100 Subject: [PATCH 071/120] Remove unused imports and fields --- src/main/java/ecdar/abstractions/Query.java | 12 ------------ src/main/java/ecdar/backend/QueryHandler.java | 2 -- .../ecdar/presentations/LocationPresentation.java | 1 - 3 files changed, 15 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index 927c3fde..c9e0f997 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -1,17 +1,14 @@ package ecdar.abstractions; -import EcdarProtoBuf.ComponentProtos; import EcdarProtoBuf.ObjectProtos; import ecdar.Ecdar; import ecdar.backend.*; import ecdar.controllers.EcdarController; -import ecdar.utility.colors.Color; import ecdar.utility.serialize.Serializable; import com.google.gson.JsonObject; import javafx.application.Platform; import javafx.beans.property.*; -import java.io.FilenameFilter; import java.util.function.Consumer; public class Query implements Serializable { @@ -26,7 +23,6 @@ public class Query implements Serializable { private final SimpleBooleanProperty isPeriodic = new SimpleBooleanProperty(false); private final ObjectProperty queryState = new SimpleObjectProperty<>(QueryState.UNKNOWN); private final ObjectProperty type = new SimpleObjectProperty<>(); - private final StringProperty failingLocation = new SimpleStringProperty(""); private BackendInstance backend; @@ -102,14 +98,6 @@ public void setQueryState(final QueryState queryState) { this.queryState.set(queryState); } - public void setFailingLocation(final String location) { - this.failingLocation.set(location); - } - - public String getFailingLocation() { - return this.failingLocation.get(); - } - public ObjectProperty queryStateProperty() { return queryState; } diff --git a/src/main/java/ecdar/backend/QueryHandler.java b/src/main/java/ecdar/backend/QueryHandler.java index 8ec9214a..1cdeac01 100644 --- a/src/main/java/ecdar/backend/QueryHandler.java +++ b/src/main/java/ecdar/backend/QueryHandler.java @@ -57,7 +57,6 @@ public void onNext(QueryProtos.QueryResponse value) { @Override public void onError(Throwable t) { - System.out.println(t); handleQueryBackendError(t, query); // Release backend connection @@ -101,7 +100,6 @@ public void closeAllBackendConnections() throws IOException { private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { // If the query has been cancelled, ignore the result if (query.getQueryState() == QueryState.UNKNOWN) return; - System.out.println(value); switch (value.getResponseCase()) { case QUERY_OK: diff --git a/src/main/java/ecdar/presentations/LocationPresentation.java b/src/main/java/ecdar/presentations/LocationPresentation.java index 59b0f6ea..8d5f5969 100644 --- a/src/main/java/ecdar/presentations/LocationPresentation.java +++ b/src/main/java/ecdar/presentations/LocationPresentation.java @@ -15,7 +15,6 @@ import javafx.scene.effect.DropShadow; import javafx.scene.shape.*; import javafx.util.Duration; -import javafx.util.Pair; import java.util.ArrayList; import java.util.List; From b1b90bc344ebf10710f20b42bb3351407dcf5237 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 8 Nov 2022 09:17:29 +0100 Subject: [PATCH 072/120] QueryPanel Checkmark enabled in View Dropdown --- src/main/java/ecdar/controllers/EcdarController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index e350b296..2dd09fc2 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -667,7 +667,7 @@ private void initializeViewMenu() { menuBarViewFilePanel.getGraphic().opacityProperty().bind(new When(isOpen).then(1).otherwise(0)); }); - menuBarViewQueryPanel.getGraphic().setOpacity(0); + menuBarViewQueryPanel.getGraphic().setOpacity(1); menuBarViewQueryPanel.setAccelerator(new KeyCodeCombination(KeyCode.G, KeyCodeCombination.SHORTCUT_DOWN)); menuBarViewQueryPanel.setOnAction(event -> { final BooleanProperty isOpen = Ecdar.toggleQueryPane(); From f223e714f7c0d96bf84c6bf44084e9527665e2e0 Mon Sep 17 00:00:00 2001 From: Sigurd Date: Tue, 8 Nov 2022 10:32:42 +0100 Subject: [PATCH 073/120] Documentation for public methods --- src/main/java/ecdar/abstractions/Component.java | 13 +++++++++++++ src/main/java/ecdar/abstractions/Location.java | 8 ++++++++ src/main/java/ecdar/abstractions/Query.java | 5 +++++ 3 files changed, 26 insertions(+) diff --git a/src/main/java/ecdar/abstractions/Component.java b/src/main/java/ecdar/abstractions/Component.java index b0126207..1d187a6a 100644 --- a/src/main/java/ecdar/abstractions/Component.java +++ b/src/main/java/ecdar/abstractions/Component.java @@ -487,12 +487,21 @@ public boolean removeLocation(final Location location) { return locations.remove(location); } + /** + * Adds a failing location to the list of failing locations. + * @param locationId the id of the location that is failing. + * @return whether adding the location to the list was a success + */ public boolean addFailingLocation(final String locationId) { Location failingLocation = findLocation(locationId); failingLocation.setFailing(true); return failingLocations.add(failingLocation); } + /** + * Sets all previous failing locations to not failing + * and removes all previous failing locations from list. + */ public void removeFailingLocations() { for (Location location : failingLocations) { location.setFailing(false); @@ -500,6 +509,10 @@ public void removeFailingLocations() { failingLocations.removeAll(); } + /** + * Observable list of all failing locations. + * @return Observable list of all failing locations. + */ public ObservableList getFailingLocations() { return failingLocations; } diff --git a/src/main/java/ecdar/abstractions/Location.java b/src/main/java/ecdar/abstractions/Location.java index 613a65e5..ba500c1b 100644 --- a/src/main/java/ecdar/abstractions/Location.java +++ b/src/main/java/ecdar/abstractions/Location.java @@ -478,10 +478,18 @@ public void setFailing(boolean bool) { this.failing.set(bool); } + /** + * Whether this location is currently failing. + * @return Whether this location is currently failing. + */ public boolean getFailing() { return this.failing.get(); } + /** + * The observable boolean property for 'failing' of this. + * @return The observable boolean property for 'failing' of this. + */ public BooleanProperty failingProperty() { return this.failing; } diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index c9e0f997..03ca02be 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -167,6 +167,11 @@ public Consumer getSuccessConsumer() { public Consumer getFailureConsumer() { return failureConsumer; } + + /** + * Getter for the state consumer. + * @return The State Consumer + */ public Consumer getStateConsumer() { return stateConsumer; } From e30c77ac86cb403c75395beea6a3ab1aadfe1460 Mon Sep 17 00:00:00 2001 From: Sigurd Date: Tue, 8 Nov 2022 10:49:01 +0100 Subject: [PATCH 074/120] Change get for each response --- src/main/java/ecdar/backend/QueryHandler.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/ecdar/backend/QueryHandler.java b/src/main/java/ecdar/backend/QueryHandler.java index 1cdeac01..7a7a5f42 100644 --- a/src/main/java/ecdar/backend/QueryHandler.java +++ b/src/main/java/ecdar/backend/QueryHandler.java @@ -113,7 +113,7 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.setQueryState(QueryState.ERROR); query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getRefinement().getReason())); query.getSuccessConsumer().accept(false); - query.getStateConsumer().accept(value.getQueryOk().getConsistency().getState()); + query.getStateConsumer().accept(value.getQueryOk().getRefinement().getState()); } break; @@ -137,7 +137,7 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.setQueryState(QueryState.ERROR); query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getDeterminism().getReason())); query.getSuccessConsumer().accept(false); - query.getStateConsumer().accept(value.getQueryOk().getConsistency().getState()); + query.getStateConsumer().accept(value.getQueryOk().getDeterminism().getState()); } break; @@ -149,7 +149,7 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.setQueryState(QueryState.ERROR); query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getImplementation().getReason())); query.getSuccessConsumer().accept(false); - query.getStateConsumer().accept(value.getQueryOk().getConsistency().getState()); + query.getStateConsumer().accept(value.getQueryOk().getImplementation().getState()); } break; @@ -161,7 +161,7 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.setQueryState(QueryState.ERROR); query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getReachability().getReason())); query.getSuccessConsumer().accept(false); - query.getStateConsumer().accept(value.getQueryOk().getConsistency().getState()); + query.getStateConsumer().accept(value.getQueryOk().getReachability().getState()); } break; From 15866b542e8a81ea43d17bde0adc90a9ec9cc608 Mon Sep 17 00:00:00 2001 From: Sigurd Date: Tue, 8 Nov 2022 14:41:36 +0100 Subject: [PATCH 075/120] Initial work on painting failing actions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Victor Doré Hanse Co-authored-by: seba6505 --- .../java/ecdar/abstractions/Component.java | 34 ++++++++++++++++-- .../ecdar/abstractions/DisplayableEdge.java | 20 +++++++++++ src/main/java/ecdar/abstractions/Edge.java | 13 +++++++ .../java/ecdar/abstractions/GroupedEdge.java | 9 +++++ src/main/java/ecdar/abstractions/Query.java | 17 +++++++++ src/main/java/ecdar/backend/QueryHandler.java | 1 + .../ecdar/presentations/NailPresentation.java | 35 ++++++++++++++++++- 7 files changed, 126 insertions(+), 3 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Component.java b/src/main/java/ecdar/abstractions/Component.java index 1d187a6a..d1f7bcf5 100644 --- a/src/main/java/ecdar/abstractions/Component.java +++ b/src/main/java/ecdar/abstractions/Component.java @@ -40,6 +40,7 @@ public class Component extends HighLevelModelObject implements Boxed { private final ObservableList locations = FXCollections.observableArrayList(); private final ObservableList failingLocations = FXCollections.observableArrayList(); private final ObservableList edges = FXCollections.observableArrayList(); + private final ObservableList failingEdges = FXCollections.observableArrayList(); private final ObservableList inputStrings = FXCollections.observableArrayList(); private final ObservableList outputStrings = FXCollections.observableArrayList(); private final StringProperty description = new SimpleStringProperty(""); @@ -487,6 +488,35 @@ public boolean removeLocation(final Location location) { return locations.remove(location); } + /** + * Adds a failing Edge to the list of failing Edges. + * @param edge the Edge that is failing. + * @return whether adding the Edge to the list was a success + */ + public boolean addFailingEdge(final Edge edge) { + edge.setFailing(true); + return failingEdges.add(edge); + } + + /** + * Sets all previous failing locations to not failing + * and removes all previous failing locations from list. + */ + public void removeFailingEdges() { + for (DisplayableEdge edge : failingEdges) { + edge.setFailing(false); + } + failingEdges.removeAll(); + } + + /** + * Observable list of all failing locations. + * @return Observable list of all failing locations. + */ + public ObservableList getFailingLocations() { + return failingLocations; + } + /** * Adds a failing location to the list of failing locations. * @param locationId the id of the location that is failing. @@ -513,8 +543,8 @@ public void removeFailingLocations() { * Observable list of all failing locations. * @return Observable list of all failing locations. */ - public ObservableList getFailingLocations() { - return failingLocations; + public ObservableList getFailingEdges() { + return failingEdges; } /** diff --git a/src/main/java/ecdar/abstractions/DisplayableEdge.java b/src/main/java/ecdar/abstractions/DisplayableEdge.java index 7a5d62d2..3a1f9341 100644 --- a/src/main/java/ecdar/abstractions/DisplayableEdge.java +++ b/src/main/java/ecdar/abstractions/DisplayableEdge.java @@ -39,6 +39,8 @@ public abstract class DisplayableEdge implements Nearable { private final BooleanProperty isHighlighted = new SimpleBooleanProperty(false); + protected final BooleanProperty failing = new SimpleBooleanProperty(false); + public Location getSourceLocation() { return sourceLocation.get(); } @@ -302,4 +304,22 @@ public StringProperty idProperty() { public abstract List getProperty(final PropertyType propertyType); public abstract void setProperty(final PropertyType propertyType, final List newProperty); + + /** + * Sets the 'failing' property + * @param bool true if the edge is failing. + */ + public abstract void setFailing(final boolean bool); + + /** + * Getter for the 'failing' boolean + * @return Whether edge is failing in last query response. + */ + public abstract boolean getFailing(); + + /** + * The observable boolean property for 'failing' of this. + * @return The observable boolean property for 'failing' of this. + */ + public abstract BooleanProperty failingProperty(); } diff --git a/src/main/java/ecdar/abstractions/Edge.java b/src/main/java/ecdar/abstractions/Edge.java index 2b1057c9..016b3b33 100644 --- a/src/main/java/ecdar/abstractions/Edge.java +++ b/src/main/java/ecdar/abstractions/Edge.java @@ -77,6 +77,19 @@ public void setGroup(final String string){ group.set(string); } + @Override + public boolean getFailing() { return this.failing.get(); } + + @Override + public void setFailing(boolean bool) { + this.failing.set(bool); + } + + @Override + public BooleanProperty failingProperty() { + return this.failing; + } + /** * Creates a clone of an edge. * Clones objects used for verification. diff --git a/src/main/java/ecdar/abstractions/GroupedEdge.java b/src/main/java/ecdar/abstractions/GroupedEdge.java index 6f216efc..4862e962 100644 --- a/src/main/java/ecdar/abstractions/GroupedEdge.java +++ b/src/main/java/ecdar/abstractions/GroupedEdge.java @@ -168,6 +168,15 @@ public void setProperty(final PropertyType propertyType, final List newP } } + @Override + public void setFailing(boolean bool) { this.failing.set(bool); } + + @Override + public boolean getFailing() { return this.failing.get(); } + + @Override + public BooleanProperty failingProperty() { return this.failing; } + public List getSyncProperties() { List syncProperties = new ArrayList<>(); diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index 03ca02be..ed0831d6 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -71,6 +71,19 @@ public class Query implements Serializable { } }; + private final Consumer actionConsumer = (action) -> { + for (Component c : Ecdar.getProject().getComponents()) { + c.removeFailingEdges(); + if (query.getValue().contains(c.getName())) { + for (Edge edge : c.getEdges()) { + if(action.equals(edge.getSync()) && edge.getSourceLocation().getFailing()) { + c.addFailingEdge(edge); + } + } + } + } + }; + public Query(final String query, final String comment, final QueryState queryState) { this.setQuery(query); this.comment.set(comment); @@ -176,6 +189,10 @@ public Consumer getStateConsumer() { return stateConsumer; } + public Consumer getActionConsumer() { + return actionConsumer; + } + @Override public JsonObject serialize() { final JsonObject result = new JsonObject(); diff --git a/src/main/java/ecdar/backend/QueryHandler.java b/src/main/java/ecdar/backend/QueryHandler.java index 7a7a5f42..58c8fd16 100644 --- a/src/main/java/ecdar/backend/QueryHandler.java +++ b/src/main/java/ecdar/backend/QueryHandler.java @@ -126,6 +126,7 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getConsistency().getReason())); query.getSuccessConsumer().accept(false); query.getStateConsumer().accept(value.getQueryOk().getConsistency().getState()); + query.getActionConsumer().accept(value.getQueryOk().getConsistency().getAction()); } break; diff --git a/src/main/java/ecdar/presentations/NailPresentation.java b/src/main/java/ecdar/presentations/NailPresentation.java index 2ab5823f..c27e8bbe 100644 --- a/src/main/java/ecdar/presentations/NailPresentation.java +++ b/src/main/java/ecdar/presentations/NailPresentation.java @@ -12,10 +12,12 @@ import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.application.Platform; +import javafx.beans.property.ObjectProperty; import javafx.scene.Group; import javafx.scene.control.Label; import javafx.scene.shape.Line; +import java.util.function.BiConsumer; import java.util.function.Consumer; import static javafx.util.Duration.millis; @@ -180,6 +182,24 @@ private void updateSyncLabel(final TagPresentation propertyTag) { } } + private void initializeFailingListener() { + final DisplayableEdge edge = controller.getEdge(); + final ObjectProperty colorIntensity = edge.colorIntensityProperty(); + + // Delegate to style the label based on the color of the location + final BiConsumer updateColor = (newColor, newIntensity) -> { + controller.color(newColor, newIntensity); + }; + final Consumer handleFailingUpdate = (isFailing) -> { + if(isFailing) { + updateColor.accept(Color.RED, colorIntensity.get()); + } else { + updateColor.accept(edge.getColor(), colorIntensity.get()); + } + }; + edge.failingProperty().addListener((obs, oldFailing, newFailing) -> handleFailingUpdate.accept(newFailing)); + } + private void initializeNailCircleColor() { final Runnable updateNailColor = () -> { final Color color = controller.getComponent().getColor(); @@ -194,12 +214,25 @@ private void initializeNailCircleColor() { } }; + final Runnable updateNailColorOnFailing = () -> { + final Color color = controller.getComponent().getColor(); + final Color.Intensity colorIntensity = controller.getComponent().getColorIntensity(); + for(DisplayableEdge edge : controller.getComponent().getFailingEdges()) { + for (Nail nail : edge.getNails()) { + if(nail.getPropertyType().equals(DisplayableEdge.PropertyType.SYNCHRONIZATION)){ + controller.nailCircle.setFill(Color.RED.getColor(colorIntensity)); + controller.nailCircle.setStroke(Color.RED.getColor(colorIntensity.next(2))); + } + } + } + }; + // When the color of the component updates, update the nail indicator as well controller.getComponent().colorProperty().addListener((observable) -> updateNailColor.run()); // When the color intensity of the component updates, update the nail indicator controller.getComponent().colorIntensityProperty().addListener((observable) -> updateNailColor.run()); - + controller.getEdge().failingProperty().addListener((observable) -> updateNailColorOnFailing.run()); // Initialize the color of the nail updateNailColor.run(); } From a39b1af519aa8da0f518af94051a1f4abd641b70 Mon Sep 17 00:00:00 2001 From: Sigurd Date: Wed, 9 Nov 2022 12:09:47 +0100 Subject: [PATCH 076/120] Refactor observer so it is added in the edge presentation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Victor DoréHansen Co-authored-by: seba6505 --- .../ecdar/presentations/EdgePresentation.java | 14 ++++++ .../ecdar/presentations/NailPresentation.java | 48 ++++++------------- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/src/main/java/ecdar/presentations/EdgePresentation.java b/src/main/java/ecdar/presentations/EdgePresentation.java index 993743bd..06e04216 100644 --- a/src/main/java/ecdar/presentations/EdgePresentation.java +++ b/src/main/java/ecdar/presentations/EdgePresentation.java @@ -2,6 +2,7 @@ import ecdar.abstractions.Component; import ecdar.abstractions.DisplayableEdge; +import ecdar.abstractions.Nail; import ecdar.controllers.EdgeController; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; @@ -21,6 +22,19 @@ public EdgePresentation(final DisplayableEdge edge, final Component component) { controller.setComponent(component); this.component.bind(controller.componentProperty()); + initializeFailingEdgeListener(); + } + + private void initializeFailingEdgeListener() { + controller.getEdge().failingProperty().addListener((observable, oldFailing, newFailing) -> onFailingUpdate(controller.getEdge(), newFailing)); + } + + private void onFailingUpdate(DisplayableEdge edge, Boolean isFailing) { + for (Nail nail : edge.getNails()) { + if (nail.getPropertyType().equals(DisplayableEdge.PropertyType.SYNCHRONIZATION)) { + controller.getNailNailPresentationMap().get(nail).onFailingUpdate(); + } + } } public EdgeController getController() { diff --git a/src/main/java/ecdar/presentations/NailPresentation.java b/src/main/java/ecdar/presentations/NailPresentation.java index c27e8bbe..f8b44cfa 100644 --- a/src/main/java/ecdar/presentations/NailPresentation.java +++ b/src/main/java/ecdar/presentations/NailPresentation.java @@ -12,12 +12,11 @@ import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.application.Platform; -import javafx.beans.property.ObjectProperty; +import javafx.beans.value.ObservableValue; import javafx.scene.Group; import javafx.scene.control.Label; import javafx.scene.shape.Line; -import java.util.function.BiConsumer; import java.util.function.Consumer; import static javafx.util.Duration.millis; @@ -182,24 +181,6 @@ private void updateSyncLabel(final TagPresentation propertyTag) { } } - private void initializeFailingListener() { - final DisplayableEdge edge = controller.getEdge(); - final ObjectProperty colorIntensity = edge.colorIntensityProperty(); - - // Delegate to style the label based on the color of the location - final BiConsumer updateColor = (newColor, newIntensity) -> { - controller.color(newColor, newIntensity); - }; - final Consumer handleFailingUpdate = (isFailing) -> { - if(isFailing) { - updateColor.accept(Color.RED, colorIntensity.get()); - } else { - updateColor.accept(edge.getColor(), colorIntensity.get()); - } - }; - edge.failingProperty().addListener((obs, oldFailing, newFailing) -> handleFailingUpdate.accept(newFailing)); - } - private void initializeNailCircleColor() { final Runnable updateNailColor = () -> { final Color color = controller.getComponent().getColor(); @@ -214,29 +195,30 @@ private void initializeNailCircleColor() { } }; - final Runnable updateNailColorOnFailing = () -> { - final Color color = controller.getComponent().getColor(); - final Color.Intensity colorIntensity = controller.getComponent().getColorIntensity(); - for(DisplayableEdge edge : controller.getComponent().getFailingEdges()) { - for (Nail nail : edge.getNails()) { - if(nail.getPropertyType().equals(DisplayableEdge.PropertyType.SYNCHRONIZATION)){ - controller.nailCircle.setFill(Color.RED.getColor(colorIntensity)); - controller.nailCircle.setStroke(Color.RED.getColor(colorIntensity.next(2))); - } - } - } - }; + // When the color of the component updates, update the nail indicator as well controller.getComponent().colorProperty().addListener((observable) -> updateNailColor.run()); // When the color intensity of the component updates, update the nail indicator controller.getComponent().colorIntensityProperty().addListener((observable) -> updateNailColor.run()); - controller.getEdge().failingProperty().addListener((observable) -> updateNailColorOnFailing.run()); // Initialize the color of the nail updateNailColor.run(); } + /** + * Update color when the edge of this nails failing property is updated. + */ + public void onFailingUpdate() { + final Runnable updateNailColorOnFailing = () -> { + final Color color = controller.getComponent().getColor(); + final Color.Intensity colorIntensity = controller.getComponent().getColorIntensity(); + controller.nailCircle.setFill(Color.RED.getColor(colorIntensity)); + controller.nailCircle.setStroke(Color.RED.getColor(colorIntensity.next(2))); + }; + updateNailColorOnFailing.run(); + } + private void initializeShakeAnimation() { final Interpolator interpolator = Interpolator.SPLINE(0.645, 0.045, 0.355, 1); From 5a5e6a4334cda5ab188ea8e0773dc3938d259fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20Dor=C3=A9=20Hansen?= Date: Wed, 9 Nov 2022 13:52:03 +0100 Subject: [PATCH 077/120] Red replaced by Lime in enabled colors --- .../java/ecdar/utility/colors/EnabledColor.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/ecdar/utility/colors/EnabledColor.java b/src/main/java/ecdar/utility/colors/EnabledColor.java index 78cfc10b..8ef4e647 100644 --- a/src/main/java/ecdar/utility/colors/EnabledColor.java +++ b/src/main/java/ecdar/utility/colors/EnabledColor.java @@ -9,13 +9,13 @@ public class EnabledColor { public static final ArrayList enabledColors = new ArrayList() {{ add(new EnabledColor(Color.GREY_BLUE, Color.Intensity.I700, KeyCode.DIGIT0)); add(new EnabledColor(Color.DEEP_ORANGE, Color.Intensity.I700, KeyCode.DIGIT1)); - add(new EnabledColor(Color.RED, Color.Intensity.I500, KeyCode.DIGIT2)); - add(new EnabledColor(Color.PINK, Color.Intensity.I500, KeyCode.DIGIT3)); - add(new EnabledColor(Color.PURPLE, Color.Intensity.I500, KeyCode.DIGIT4)); - add(new EnabledColor(Color.INDIGO, Color.Intensity.I500, KeyCode.DIGIT5)); - add(new EnabledColor(Color.BLUE, Color.Intensity.I600, KeyCode.DIGIT6)); - add(new EnabledColor(Color.CYAN, Color.Intensity.I700, KeyCode.DIGIT7)); - add(new EnabledColor(Color.GREEN, Color.Intensity.I600, KeyCode.DIGIT8)); + add(new EnabledColor(Color.PINK, Color.Intensity.I500, KeyCode.DIGIT2)); + add(new EnabledColor(Color.PURPLE, Color.Intensity.I500, KeyCode.DIGIT3)); + add(new EnabledColor(Color.INDIGO, Color.Intensity.I500, KeyCode.DIGIT4)); + add(new EnabledColor(Color.BLUE, Color.Intensity.I600, KeyCode.DIGIT5)); + add(new EnabledColor(Color.CYAN, Color.Intensity.I700, KeyCode.DIGIT6)); + add(new EnabledColor(Color.GREEN, Color.Intensity.I600, KeyCode.DIGIT7)); + add(new EnabledColor(Color.LIME, Color.Intensity.I500, KeyCode.DIGIT8)); add(new EnabledColor(Color.BROWN, Color.Intensity.I500, KeyCode.DIGIT9)); }}; From dda9acf884a82713e794e1ecb2dfb517d6a1f090 Mon Sep 17 00:00:00 2001 From: Sigurd Date: Thu, 10 Nov 2022 12:21:02 +0100 Subject: [PATCH 078/120] Set color back to component color when action doesnt fail. Other cleanup --- .../java/ecdar/abstractions/Component.java | 7 +-- src/main/java/ecdar/abstractions/Edge.java | 6 ++- .../java/ecdar/abstractions/GroupedEdge.java | 12 +++-- src/main/java/ecdar/abstractions/Query.java | 1 + src/main/java/ecdar/backend/QueryHandler.java | 3 +- .../ecdar/presentations/EdgePresentation.java | 2 +- .../ecdar/presentations/NailPresentation.java | 46 +++++++++++-------- 7 files changed, 47 insertions(+), 30 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Component.java b/src/main/java/ecdar/abstractions/Component.java index d1f7bcf5..013ec70e 100644 --- a/src/main/java/ecdar/abstractions/Component.java +++ b/src/main/java/ecdar/abstractions/Component.java @@ -489,7 +489,8 @@ public boolean removeLocation(final Location location) { } /** - * Adds a failing Edge to the list of failing Edges. + * Add a failing Edge to the list of failing edges + * and set the edge's failing property to true. * @param edge the Edge that is failing. * @return whether adding the Edge to the list was a success */ @@ -499,8 +500,8 @@ public boolean addFailingEdge(final Edge edge) { } /** - * Sets all previous failing locations to not failing - * and removes all previous failing locations from list. + * Sets all previous failing edges to not failing + * and removes all previous failing edges from list. */ public void removeFailingEdges() { for (DisplayableEdge edge : failingEdges) { diff --git a/src/main/java/ecdar/abstractions/Edge.java b/src/main/java/ecdar/abstractions/Edge.java index 016b3b33..9bbcff9c 100644 --- a/src/main/java/ecdar/abstractions/Edge.java +++ b/src/main/java/ecdar/abstractions/Edge.java @@ -69,7 +69,7 @@ public String getSyncWithSymbol() { return sync.get() + (ioStatus.get().equals(EdgeStatus.INPUT) ? "?" : "!"); } - public String getGroup(){ + public String getGroup() { return group.get(); } @@ -78,7 +78,9 @@ public void setGroup(final String string){ } @Override - public boolean getFailing() { return this.failing.get(); } + public boolean getFailing() { + return this.failing.get(); + } @Override public void setFailing(boolean bool) { diff --git a/src/main/java/ecdar/abstractions/GroupedEdge.java b/src/main/java/ecdar/abstractions/GroupedEdge.java index 4862e962..f96ab4b7 100644 --- a/src/main/java/ecdar/abstractions/GroupedEdge.java +++ b/src/main/java/ecdar/abstractions/GroupedEdge.java @@ -169,13 +169,19 @@ public void setProperty(final PropertyType propertyType, final List newP } @Override - public void setFailing(boolean bool) { this.failing.set(bool); } + public void setFailing(boolean bool) { + this.failing.set(bool); + } @Override - public boolean getFailing() { return this.failing.get(); } + public boolean getFailing() { + return this.failing.get(); + } @Override - public BooleanProperty failingProperty() { return this.failing; } + public BooleanProperty failingProperty() { + return this.failing; + } public List getSyncProperties() { List syncProperties = new ArrayList<>(); diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index ed0831d6..c00cfb76 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -30,6 +30,7 @@ public class Query implements Serializable { if (aBoolean) { for (Component c : Ecdar.getProject().getComponents()) { c.removeFailingLocations(); + c.removeFailingEdges(); } setQueryState(QueryState.SUCCESSFUL); } else { diff --git a/src/main/java/ecdar/backend/QueryHandler.java b/src/main/java/ecdar/backend/QueryHandler.java index 58c8fd16..74453cf5 100644 --- a/src/main/java/ecdar/backend/QueryHandler.java +++ b/src/main/java/ecdar/backend/QueryHandler.java @@ -100,7 +100,6 @@ public void closeAllBackendConnections() throws IOException { private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { // If the query has been cancelled, ignore the result if (query.getQueryState() == QueryState.UNKNOWN) return; - switch (value.getResponseCase()) { case QUERY_OK: QueryProtos.QueryResponse.QueryOk queryOk = value.getQueryOk(); @@ -114,6 +113,7 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getRefinement().getReason())); query.getSuccessConsumer().accept(false); query.getStateConsumer().accept(value.getQueryOk().getRefinement().getState()); + query.getActionConsumer().accept(value.getQueryOk().getConsistency().getAction()); } break; @@ -139,6 +139,7 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getDeterminism().getReason())); query.getSuccessConsumer().accept(false); query.getStateConsumer().accept(value.getQueryOk().getDeterminism().getState()); + query.getActionConsumer().accept(value.getQueryOk().getConsistency().getAction()); } break; diff --git a/src/main/java/ecdar/presentations/EdgePresentation.java b/src/main/java/ecdar/presentations/EdgePresentation.java index 06e04216..949f3ce2 100644 --- a/src/main/java/ecdar/presentations/EdgePresentation.java +++ b/src/main/java/ecdar/presentations/EdgePresentation.java @@ -32,7 +32,7 @@ private void initializeFailingEdgeListener() { private void onFailingUpdate(DisplayableEdge edge, Boolean isFailing) { for (Nail nail : edge.getNails()) { if (nail.getPropertyType().equals(DisplayableEdge.PropertyType.SYNCHRONIZATION)) { - controller.getNailNailPresentationMap().get(nail).onFailingUpdate(); + controller.getNailNailPresentationMap().get(nail).onFailingUpdate(isFailing); } } } diff --git a/src/main/java/ecdar/presentations/NailPresentation.java b/src/main/java/ecdar/presentations/NailPresentation.java index f8b44cfa..f945213b 100644 --- a/src/main/java/ecdar/presentations/NailPresentation.java +++ b/src/main/java/ecdar/presentations/NailPresentation.java @@ -182,41 +182,47 @@ private void updateSyncLabel(final TagPresentation propertyTag) { } private void initializeNailCircleColor() { - final Runnable updateNailColor = () -> { - final Color color = controller.getComponent().getColor(); - final Color.Intensity colorIntensity = controller.getComponent().getColorIntensity(); - - if(!controller.getNail().getPropertyType().equals(Edge.PropertyType.NONE)) { - controller.nailCircle.setFill(color.getColor(colorIntensity)); - controller.nailCircle.setStroke(color.getColor(colorIntensity.next(2))); - } else { - controller.nailCircle.setFill(Color.GREY_BLUE.getColor(Color.Intensity.I800)); - controller.nailCircle.setStroke(Color.GREY_BLUE.getColor(Color.Intensity.I900)); - } - }; - - // When the color of the component updates, update the nail indicator as well - controller.getComponent().colorProperty().addListener((observable) -> updateNailColor.run()); + controller.getComponent().colorProperty().addListener((observable) -> updateNailColor()); // When the color intensity of the component updates, update the nail indicator - controller.getComponent().colorIntensityProperty().addListener((observable) -> updateNailColor.run()); + controller.getComponent().colorIntensityProperty().addListener((observable) -> updateNailColor()); // Initialize the color of the nail - updateNailColor.run(); + updateNailColor(); } /** * Update color when the edge of this nails failing property is updated. */ - public void onFailingUpdate() { - final Runnable updateNailColorOnFailing = () -> { + public void onFailingUpdate(boolean isFailing) { + final Runnable updateNailColorOnFailingUpdate = () -> { final Color color = controller.getComponent().getColor(); final Color.Intensity colorIntensity = controller.getComponent().getColorIntensity(); controller.nailCircle.setFill(Color.RED.getColor(colorIntensity)); controller.nailCircle.setStroke(Color.RED.getColor(colorIntensity.next(2))); }; - updateNailColorOnFailing.run(); + if (isFailing) { + updateNailColorOnFailingUpdate.run(); + } else { + updateNailColor(); + } + } + + private void updateNailColor() { + final Runnable updateNailColor = () -> { + final Color color = controller.getComponent().getColor(); + final Color.Intensity colorIntensity = controller.getComponent().getColorIntensity(); + + if(!controller.getNail().getPropertyType().equals(Edge.PropertyType.NONE)) { + controller.nailCircle.setFill(color.getColor(colorIntensity)); + controller.nailCircle.setStroke(color.getColor(colorIntensity.next(2))); + } else { + controller.nailCircle.setFill(Color.GREY_BLUE.getColor(Color.Intensity.I800)); + controller.nailCircle.setStroke(Color.GREY_BLUE.getColor(Color.Intensity.I900)); + } + }; + updateNailColor.run(); } private void initializeShakeAnimation() { From d327874f75748485b3348d68a39c5ed3185a124a Mon Sep 17 00:00:00 2001 From: Sigurd Date: Thu, 10 Nov 2022 12:27:42 +0100 Subject: [PATCH 079/120] Fix comment --- src/main/java/ecdar/abstractions/Component.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Component.java b/src/main/java/ecdar/abstractions/Component.java index 013ec70e..f03faa87 100644 --- a/src/main/java/ecdar/abstractions/Component.java +++ b/src/main/java/ecdar/abstractions/Component.java @@ -541,8 +541,8 @@ public void removeFailingLocations() { } /** - * Observable list of all failing locations. - * @return Observable list of all failing locations. + * Observable list of all failing edges. + * @return Observable list of all failing edges. */ public ObservableList getFailingEdges() { return failingEdges; From f61d61847ce554b9cd0db649aaa0da3b52b2852a Mon Sep 17 00:00:00 2001 From: WassawRoki <56611129+WassawRoki@users.noreply.github.com> Date: Thu, 10 Nov 2022 13:41:30 +0100 Subject: [PATCH 080/120] cloning --- src/main/java/ecdar/abstractions/Component.java | 15 +++++++++++++++ .../SimulationInitializationDialogController.java | 1 + .../ecdar/controllers/SimulatorController.java | 15 ++++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/main/java/ecdar/abstractions/Component.java b/src/main/java/ecdar/abstractions/Component.java index d3da9db1..d3b90f2a 100644 --- a/src/main/java/ecdar/abstractions/Component.java +++ b/src/main/java/ecdar/abstractions/Component.java @@ -97,6 +97,7 @@ public Component(final JsonObject json) { bindReachabilityAnalysis(); } + /** * Creates a clone of another component. * Copies objects used for verification (e.g. locations, edges and the declarations). @@ -116,6 +117,20 @@ public Component cloneForVerification() { return clone; } + public Component cloneForSimulation() { + final Component clone = new Component(); + clone.addVerificationObjects(this); + clone.setIncludeInPeriodicCheck(false); + clone.inputStrings.addAll(getInputStrings()); + clone.outputStrings.addAll(getOutputStrings()); + clone.setName(getName()); + clone.setColor(this.getColor()); + for (Location l: locations){ + clone.locations.add(l); + } + + return clone; + } /** * Adds objects used for verifications to this component. diff --git a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java index 1960d465..76eec5ad 100644 --- a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java +++ b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java @@ -22,6 +22,7 @@ public class SimulationInitializationDialogController implements Initializable { * and saves it in the public static ListOfComponents */ public void GetListOfComponentsToSimulate(){ + ListOfComponents.clear(); String componentsToSimulate = simulationComboBox.getSelectionModel().getSelectedItem(); //filters out all components by ignoring operators. Pattern pattern = Pattern.compile("([\\w]*)", Pattern.CASE_INSENSITIVE); diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index b08976ad..588a906b 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -1,5 +1,6 @@ package ecdar.controllers; +import com.google.gson.JsonObject; import ecdar.Ecdar; import ecdar.abstractions.*; import ecdar.backend.SimulationHandler; @@ -7,12 +8,14 @@ import ecdar.presentations.SimulatorOverviewPresentation; import ecdar.simulation.SimulationState; import ecdar.simulation.Transition; +import ecdar.utility.colors.Color; import javafx.beans.property.DoubleProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.fxml.Initializable; import javafx.scene.layout.StackPane; +import org.apache.commons.lang3.SerializationUtils; import java.net.URL; import java.util.ArrayList; @@ -81,8 +84,16 @@ private void resetSimulation() { overviewPresentation.getController().getComponentObservableList().clear(); overviewPresentation.getController().getComponentObservableList().addAll(findComponentsInCurrentSimulation(SimulationInitializationDialogController.ListOfComponents)); firstTimeInSimulator = false; + + //Method that colors all initial states. + initialstatelighter(findComponentsInCurrentSimulation(SimulationInitializationDialogController.ListOfComponents)); } + private void initialstatelighter(List listofComponents){ + for(Component comp: listofComponents){ + comp.getInitialLocation().setColor(Color.ORANGE); + } + } /** * Finds the components that are used in the current simulation by looking at the component found in * {@link Project#getComponents()} and compare them to the processes declared in the {@link SimulationHandler#getSystem()} @@ -102,7 +113,9 @@ private List findComponentsInCurrentSimulation(List queryComp for(Component comp:components) { for(String componentInQuery : queryComponents) { if((comp.getName().equals(componentInQuery))) { - SelectedComponents.add(comp); + Component temp = new Component(); + temp = comp.cloneForSimulation(); + SelectedComponents.add(temp); } } } From ae1c02c59b50f67c4c04be78de2c54089b462c05 Mon Sep 17 00:00:00 2001 From: WassawRoki <56611129+WassawRoki@users.noreply.github.com> Date: Thu, 10 Nov 2022 14:29:53 +0100 Subject: [PATCH 081/120] bingbong --- .../java/ecdar/abstractions/Component.java | 13 ---------- .../controllers/SimulatorController.java | 24 ++++++++++++------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Component.java b/src/main/java/ecdar/abstractions/Component.java index d3b90f2a..bb127e87 100644 --- a/src/main/java/ecdar/abstractions/Component.java +++ b/src/main/java/ecdar/abstractions/Component.java @@ -117,20 +117,7 @@ public Component cloneForVerification() { return clone; } - public Component cloneForSimulation() { - final Component clone = new Component(); - clone.addVerificationObjects(this); - clone.setIncludeInPeriodicCheck(false); - clone.inputStrings.addAll(getInputStrings()); - clone.outputStrings.addAll(getOutputStrings()); - clone.setName(getName()); - clone.setColor(this.getColor()); - for (Location l: locations){ - clone.locations.add(l); - } - return clone; - } /** * Adds objects used for verifications to this component. diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index 588a906b..f898fcb3 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -79,20 +79,29 @@ public void willShow() { private void resetSimulation() { final SimulationHandler sm = Ecdar.getSimulationHandler(); sm.initializeDefaultSystem(); - + List listOfComponentsForSimulation = findComponentsInCurrentSimulation(SimulationInitializationDialogController.ListOfComponents); overviewPresentation.getController().clearOverview(); overviewPresentation.getController().getComponentObservableList().clear(); - overviewPresentation.getController().getComponentObservableList().addAll(findComponentsInCurrentSimulation(SimulationInitializationDialogController.ListOfComponents)); + overviewPresentation.getController().getComponentObservableList().addAll(listOfComponentsForSimulation); firstTimeInSimulator = false; //Method that colors all initial states. - initialstatelighter(findComponentsInCurrentSimulation(SimulationInitializationDialogController.ListOfComponents)); + initialstatelighter(listOfComponentsForSimulation); } private void initialstatelighter(List listofComponents){ - for(Component comp: listofComponents){ - comp.getInitialLocation().setColor(Color.ORANGE); + for(Component comp: listofComponents) + { + Location initiallocation = comp.getInitialLocation(); + initiallocation.setColor(Color.ORANGE); + List tempedge = comp.getRelatedEdges(initiallocation); + for(DisplayableEdge e: tempedge) + { + if(e.getSourceLocation() == initiallocation) + { + e.setIsHighlighted(true); + } + } } - } /** * Finds the components that are used in the current simulation by looking at the component found in @@ -113,8 +122,7 @@ private List findComponentsInCurrentSimulation(List queryComp for(Component comp:components) { for(String componentInQuery : queryComponents) { if((comp.getName().equals(componentInQuery))) { - Component temp = new Component(); - temp = comp.cloneForSimulation(); + Component temp = new Component(comp.serialize()); SelectedComponents.add(temp); } } From 5c64f424981b80929f4697cb1afe16a316c6ea9e Mon Sep 17 00:00:00 2001 From: jskad Date: Thu, 10 Nov 2022 15:37:51 +0100 Subject: [PATCH 082/120] Consume correct response --- src/main/java/ecdar/backend/QueryHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ecdar/backend/QueryHandler.java b/src/main/java/ecdar/backend/QueryHandler.java index 74453cf5..b98a0e57 100644 --- a/src/main/java/ecdar/backend/QueryHandler.java +++ b/src/main/java/ecdar/backend/QueryHandler.java @@ -113,7 +113,7 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getRefinement().getReason())); query.getSuccessConsumer().accept(false); query.getStateConsumer().accept(value.getQueryOk().getRefinement().getState()); - query.getActionConsumer().accept(value.getQueryOk().getConsistency().getAction()); + query.getActionConsumer().accept(value.getQueryOk().getRefinement().getAction()); } break; @@ -139,7 +139,7 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getDeterminism().getReason())); query.getSuccessConsumer().accept(false); query.getStateConsumer().accept(value.getQueryOk().getDeterminism().getState()); - query.getActionConsumer().accept(value.getQueryOk().getConsistency().getAction()); + query.getActionConsumer().accept(value.getQueryOk().getDeterminism().getAction()); } break; From 64197b5961f9d0fbcd4a231082c2b571d018b7cf Mon Sep 17 00:00:00 2001 From: Sigurd Date: Tue, 15 Nov 2022 12:51:45 +0100 Subject: [PATCH 083/120] Keep red color when selecting nails. Keep red color when swapping components --- .../presentations/LocationPresentation.java | 34 +++++++++++-------- .../ecdar/presentations/NailPresentation.java | 21 +++++------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/main/java/ecdar/presentations/LocationPresentation.java b/src/main/java/ecdar/presentations/LocationPresentation.java index 8d5f5969..288c52c7 100644 --- a/src/main/java/ecdar/presentations/LocationPresentation.java +++ b/src/main/java/ecdar/presentations/LocationPresentation.java @@ -162,8 +162,13 @@ private void initializeIdLabel() { // Delegate to style the label based on the color of the location final BiConsumer updateColor = (newColor, newIntensity) -> { - idLabel.setTextFill(newColor.getTextColor(newIntensity)); - ds.setColor(newColor.getColor(newIntensity)); + if (location.getFailing()) { + idLabel.setTextFill(Color.RED.getTextColor(newIntensity)); + ds.setColor(Color.RED.getColor(newIntensity)); + } else { + idLabel.setTextFill(newColor.getTextColor(newIntensity)); + ds.setColor(newColor.getColor(newIntensity)); + } }; final Consumer handleFailingUpdate = (isFailing) -> { @@ -419,21 +424,22 @@ protected void interpolate(final double frac) { // Delegate to style the label based on the color of the location final BiConsumer updateColor = (newColor, newIntensity) -> { - notCommittedShape.setFill(newColor.getColor(newIntensity)); if (!location.getUrgency().equals(Location.Urgency.PROHIBITED)) { - notCommittedShape.setStroke(newColor.getColor(newIntensity.next(2))); + if (location.getFailing()) { + notCommittedShape.setStroke(Color.RED.getColor(newIntensity)); + } else { + notCommittedShape.setStroke(newColor.getColor(newIntensity.next(2))); + } } - - committedShape.setFill(newColor.getColor(newIntensity)); - committedShape.setStroke(newColor.getColor(newIntensity.next(2))); - }; - - final Consumer handleFailingUpdate = (isFailing) -> { - if(isFailing) { - updateColor.accept(Color.RED, colorIntensity.get()); + if (location.getFailing()) { + notCommittedShape.setFill(Color.RED.getColor(newIntensity)); + committedShape.setFill(Color.RED.getColor(newIntensity)); + committedShape.setStroke(Color.RED.getColor(newIntensity.next(2))); } else { - updateColor.accept(location.getColor(), colorIntensity.get()); + notCommittedShape.setFill(newColor.getColor(newIntensity)); + committedShape.setFill(newColor.getColor(newIntensity)); + committedShape.setStroke(newColor.getColor(newIntensity.next(2))); } }; @@ -444,7 +450,7 @@ protected void interpolate(final double frac) { // Update the color of the circle when the color of the location is updated color.addListener((obs, old, newColor) -> updateColor.accept(newColor, colorIntensity.get())); - failing.addListener((obs, old, newFailing) -> handleFailingUpdate.accept(newFailing)); + failing.addListener((obs, old, newFailing) -> updateColor.accept(color.get(), colorIntensity.get())); } private void initializeTypeGraphics() { diff --git a/src/main/java/ecdar/presentations/NailPresentation.java b/src/main/java/ecdar/presentations/NailPresentation.java index f945213b..f9853567 100644 --- a/src/main/java/ecdar/presentations/NailPresentation.java +++ b/src/main/java/ecdar/presentations/NailPresentation.java @@ -213,8 +213,13 @@ private void updateNailColor() { final Runnable updateNailColor = () -> { final Color color = controller.getComponent().getColor(); final Color.Intensity colorIntensity = controller.getComponent().getColorIntensity(); - - if(!controller.getNail().getPropertyType().equals(Edge.PropertyType.NONE)) { + //If edge is failing and is a SYNC + if (controller.getEdge().getFailing() && controller.getNail().getPropertyType().equals(Edge.PropertyType.SYNCHRONIZATION)) { + controller.nailCircle.setFill(Color.RED.getColor(colorIntensity)); + controller.nailCircle.setStroke(Color.RED.getColor(colorIntensity.next(2))); + } + //If edge is not NONE + else if (!controller.getNail().getPropertyType().equals(Edge.PropertyType.NONE)) { controller.nailCircle.setFill(color.getColor(colorIntensity)); controller.nailCircle.setStroke(color.getColor(colorIntensity.next(2))); } else { @@ -258,17 +263,7 @@ public void select() { @Override public void deselect() { - Color color = Color.GREY_BLUE; - Color.Intensity intensity = Color.Intensity.I800; - - // Set the color - if(!controller.getNail().getPropertyType().equals(Edge.PropertyType.NONE)) { - color = controller.getComponent().getColor(); - intensity = controller.getComponent().getColorIntensity(); - } - - controller.nailCircle.setFill(color.getColor(intensity)); - controller.nailCircle.setStroke(color.getColor(intensity.next(2))); + updateNailColor(); } public NailController getController() { From 6abd4a61e183d24d4161eb410a906683ddc0c4cf Mon Sep 17 00:00:00 2001 From: APaludan Date: Wed, 16 Nov 2022 09:36:51 +0100 Subject: [PATCH 084/120] fix build error --- .../java/ecdar/simulation/SimulationTest.java | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/test/java/ecdar/simulation/SimulationTest.java b/src/test/java/ecdar/simulation/SimulationTest.java index 2acb8136..6d20da38 100644 --- a/src/test/java/ecdar/simulation/SimulationTest.java +++ b/src/test/java/ecdar/simulation/SimulationTest.java @@ -32,24 +32,24 @@ public void testGetInitialStateHighlightsTheInitialLocation() { final List components = generateComponentsWithInitialLocations(); BindableService testService = new EcdarBackendGrpc.EcdarBackendImplBase() { - @Override - public void startSimulation(QueryProtos.SimulationStartRequest request, - StreamObserver responseObserver) { - try { - ObjectProtos.StateTuple state = ObjectProtos.StateTuple.newBuilder().addAllLocations(components.stream() - .map(c -> ObjectProtos.StateTuple.LocationTuple.newBuilder() - .setComponentName(c.getName()) - .setId(c.getInitialLocation().getId()) - .build()) - .collect(Collectors.toList())).build(); + // @Override + // public void startSimulation(QueryProtos.SimulationStartRequest request, + // StreamObserver responseObserver) { + // try { + // ObjectProtos.StateTuple state = ObjectProtos.StateTuple.newBuilder().addAllLocations(components.stream() + // .map(c -> ObjectProtos.StateTuple.LocationTuple.newBuilder() + // .setComponentName(c.getName()) + // .setId(c.getInitialLocation().getId()) + // .build()) + // .collect(Collectors.toList())).build(); - QueryProtos.SimulationStepResponse response = QueryProtos.SimulationStepResponse.newBuilder().setState(state).build(); - responseObserver.onNext(response); - responseObserver.onCompleted(); - } catch (Throwable e) { - responseObserver.onError(Status.INVALID_ARGUMENT.withDescription(e.getMessage()).asException()); - } - } + // QueryProtos.SimulationStepResponse response = QueryProtos.SimulationStepResponse.newBuilder().setState(state).build(); + // responseObserver.onNext(response); + // responseObserver.onCompleted(); + // } catch (Throwable e) { + // responseObserver.onError(Status.INVALID_ARGUMENT.withDescription(e.getMessage()).asException()); + // } + // } @Override public void takeSimulationStep(EcdarProtoBuf.QueryProtos.SimulationStepRequest request, From 43854b72617a1e04ba2026f743911dd393b76b70 Mon Sep 17 00:00:00 2001 From: APaludan Date: Wed, 16 Nov 2022 09:42:52 +0100 Subject: [PATCH 085/120] fix test --- .../java/ecdar/simulation/SimulationTest.java | 98 +++++++++---------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/src/test/java/ecdar/simulation/SimulationTest.java b/src/test/java/ecdar/simulation/SimulationTest.java index 6d20da38..625968a5 100644 --- a/src/test/java/ecdar/simulation/SimulationTest.java +++ b/src/test/java/ecdar/simulation/SimulationTest.java @@ -27,63 +27,63 @@ public class SimulationTest extends TestFXBase { public GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); private final String serverName = InProcessServerBuilder.generateName(); - @Test - public void testGetInitialStateHighlightsTheInitialLocation() { - final List components = generateComponentsWithInitialLocations(); + // @Test + // public void testGetInitialStateHighlightsTheInitialLocation() { + // final List components = generateComponentsWithInitialLocations(); - BindableService testService = new EcdarBackendGrpc.EcdarBackendImplBase() { - // @Override - // public void startSimulation(QueryProtos.SimulationStartRequest request, - // StreamObserver responseObserver) { - // try { - // ObjectProtos.StateTuple state = ObjectProtos.StateTuple.newBuilder().addAllLocations(components.stream() - // .map(c -> ObjectProtos.StateTuple.LocationTuple.newBuilder() - // .setComponentName(c.getName()) - // .setId(c.getInitialLocation().getId()) - // .build()) - // .collect(Collectors.toList())).build(); + // BindableService testService = new EcdarBackendGrpc.EcdarBackendImplBase() { + // @Override + // public void startSimulation(QueryProtos.SimulationStartRequest request, + // StreamObserver responseObserver) { + // try { + // ObjectProtos.State state = ObjectProtos.StateTuple.newBuilder().addAllLocations(components.stream() + // .map(c -> ObjectProtos.StateTuple.LocationTuple.newBuilder() + // .setComponentName(c.getName()) + // .setId(c.getInitialLocation().getId()) + // .build()) + // .collect(Collectors.toList())).build(); - // QueryProtos.SimulationStepResponse response = QueryProtos.SimulationStepResponse.newBuilder().setState(state).build(); - // responseObserver.onNext(response); - // responseObserver.onCompleted(); - // } catch (Throwable e) { - // responseObserver.onError(Status.INVALID_ARGUMENT.withDescription(e.getMessage()).asException()); - // } - // } + // QueryProtos.SimulationStepResponse response = QueryProtos.SimulationStepResponse.newBuilder().setState(state).build(); + // responseObserver.onNext(response); + // responseObserver.onCompleted(); + // } catch (Throwable e) { + // responseObserver.onError(Status.INVALID_ARGUMENT.withDescription(e.getMessage()).asException()); + // } + // } - @Override - public void takeSimulationStep(EcdarProtoBuf.QueryProtos.SimulationStepRequest request, - io.grpc.stub.StreamObserver responseObserver) { - } - }; + // @Override + // public void takeSimulationStep(EcdarProtoBuf.QueryProtos.SimulationStepRequest request, + // io.grpc.stub.StreamObserver responseObserver) { + // } + // }; - final Server server; - final ManagedChannel channel; - final EcdarBackendGrpc.EcdarBackendBlockingStub stub; - try { - server = grpcCleanup.register(InProcessServerBuilder - .forName(serverName).directExecutor().addService(testService).build().start()); - channel = grpcCleanup.register(InProcessChannelBuilder - .forName(serverName).directExecutor().build()); - stub = EcdarBackendGrpc.newBlockingStub(channel); - QueryProtos.SimulationStartRequest request = QueryProtos.SimulationStartRequest.newBuilder().setSystem("(A || B)").build(); + // final Server server; + // final ManagedChannel channel; + // final EcdarBackendGrpc.EcdarBackendBlockingStub stub; + // try { + // server = grpcCleanup.register(InProcessServerBuilder + // .forName(serverName).directExecutor().addService(testService).build().start()); + // channel = grpcCleanup.register(InProcessChannelBuilder + // .forName(serverName).directExecutor().build()); + // stub = EcdarBackendGrpc.newBlockingStub(channel); + // QueryProtos.SimulationStartRequest request = QueryProtos.SimulationStartRequest.newBuilder().setSystem("(A || B)").build(); - var expectedResponse = new ObjectProtos.StateTuple.LocationTuple[components.size()]; + // var expectedResponse = new ObjectProtos.StateTuple.LocationTuple[components.size()]; - for (int i = 0; i < components.size(); i++) { - Component comp = components.get(i); - expectedResponse[i] = ObjectProtos.StateTuple.LocationTuple.newBuilder() - .setComponentName(comp.getName()) - .setId(comp.getInitialLocation().getId()).build(); - } + // for (int i = 0; i < components.size(); i++) { + // Component comp = components.get(i); + // expectedResponse[i] = ObjectProtos.StateTuple.LocationTuple.newBuilder() + // .setComponentName(comp.getName()) + // .setId(comp.getInitialLocation().getId()).build(); + // } - var result = stub.startSimulation(request).getState().getLocationsList().toArray(); + // var result = stub.startSimulation(request).getState().getLocationsList().toArray(); - Assertions.assertArrayEquals(expectedResponse, result); - } catch (IOException e) { - Assertions.fail("Exception encountered: " + e.getMessage()); - } - } + // Assertions.assertArrayEquals(expectedResponse, result); + // } catch (IOException e) { + // Assertions.fail("Exception encountered: " + e.getMessage()); + // } + // } private List generateComponentsWithInitialLocations() { List comps = new ArrayList<>(); From 639d24b951ed03fa65a74e15f1ef20a12bd53480 Mon Sep 17 00:00:00 2001 From: APaludan Date: Thu, 17 Nov 2022 12:43:12 +0100 Subject: [PATCH 086/120] fix test fr --- .../java/ecdar/simulation/SimulationTest.java | 111 ++++++++++-------- 1 file changed, 61 insertions(+), 50 deletions(-) diff --git a/src/test/java/ecdar/simulation/SimulationTest.java b/src/test/java/ecdar/simulation/SimulationTest.java index 625968a5..d1906460 100644 --- a/src/test/java/ecdar/simulation/SimulationTest.java +++ b/src/test/java/ecdar/simulation/SimulationTest.java @@ -3,12 +3,14 @@ import EcdarProtoBuf.EcdarBackendGrpc; import EcdarProtoBuf.ObjectProtos; import EcdarProtoBuf.QueryProtos; +import EcdarProtoBuf.ObjectProtos.DecisionPoint; +import EcdarProtoBuf.ObjectProtos.LocationTuple; +import EcdarProtoBuf.ObjectProtos.SpecificComponent; import ecdar.TestFXBase; import ecdar.abstractions.Component; import ecdar.abstractions.Location; import io.grpc.BindableService; import io.grpc.ManagedChannel; -import io.grpc.Server; import io.grpc.Status; import io.grpc.inprocess.InProcessChannelBuilder; import io.grpc.inprocess.InProcessServerBuilder; @@ -21,69 +23,78 @@ import java.util.stream.Collectors; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.Assertions; +@TestInstance(Lifecycle.PER_CLASS) public class SimulationTest extends TestFXBase { public GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); private final String serverName = InProcessServerBuilder.generateName(); - // @Test - // public void testGetInitialStateHighlightsTheInitialLocation() { - // final List components = generateComponentsWithInitialLocations(); + @Test + public void testGetInitialStateHighlightsTheInitialLocation() { + final List components = generateComponentsWithInitialLocations(); - // BindableService testService = new EcdarBackendGrpc.EcdarBackendImplBase() { - // @Override - // public void startSimulation(QueryProtos.SimulationStartRequest request, - // StreamObserver responseObserver) { - // try { - // ObjectProtos.State state = ObjectProtos.StateTuple.newBuilder().addAllLocations(components.stream() - // .map(c -> ObjectProtos.StateTuple.LocationTuple.newBuilder() - // .setComponentName(c.getName()) - // .setId(c.getInitialLocation().getId()) - // .build()) - // .collect(Collectors.toList())).build(); + BindableService testService = new EcdarBackendGrpc.EcdarBackendImplBase() { + @Override + public void startSimulation(QueryProtos.SimulationStartRequest request, + StreamObserver responseObserver) { + try { + ObjectProtos.LocationTuple locations = LocationTuple.newBuilder().addAllLocations(components.stream() + .map(c -> ObjectProtos.Location.newBuilder() + .setSpecificComponent(SpecificComponent.newBuilder() + .setComponentName(c.getName())) + .setId(c.getInitialLocation().getId()) + .build()) + .collect(Collectors.toList())) + .build(); + + ObjectProtos.State state = ObjectProtos.State.newBuilder().setLocationTuple(locations).build(); + DecisionPoint decisionPoint = DecisionPoint.newBuilder().setSource(state).build(); + QueryProtos.SimulationStepResponse response = QueryProtos.SimulationStepResponse.newBuilder() + .setNewDecisionPoint(decisionPoint) + .build(); + responseObserver.onNext(response); + responseObserver.onCompleted(); + } catch (Throwable e) { + responseObserver.onError(Status.INVALID_ARGUMENT.withDescription(e.getMessage()).asException()); + } + } - // QueryProtos.SimulationStepResponse response = QueryProtos.SimulationStepResponse.newBuilder().setState(state).build(); - // responseObserver.onNext(response); - // responseObserver.onCompleted(); - // } catch (Throwable e) { - // responseObserver.onError(Status.INVALID_ARGUMENT.withDescription(e.getMessage()).asException()); - // } - // } + @Override + public void takeSimulationStep(EcdarProtoBuf.QueryProtos.SimulationStepRequest request, + io.grpc.stub.StreamObserver responseObserver) { + } + }; - // @Override - // public void takeSimulationStep(EcdarProtoBuf.QueryProtos.SimulationStepRequest request, - // io.grpc.stub.StreamObserver responseObserver) { - // } - // }; + final ManagedChannel channel; + final EcdarBackendGrpc.EcdarBackendBlockingStub stub; + try { + grpcCleanup.register(InProcessServerBuilder + .forName(serverName).directExecutor().addService(testService).build().start()); + channel = grpcCleanup.register(InProcessChannelBuilder + .forName(serverName).directExecutor().build()); + stub = EcdarBackendGrpc.newBlockingStub(channel); - // final Server server; - // final ManagedChannel channel; - // final EcdarBackendGrpc.EcdarBackendBlockingStub stub; - // try { - // server = grpcCleanup.register(InProcessServerBuilder - // .forName(serverName).directExecutor().addService(testService).build().start()); - // channel = grpcCleanup.register(InProcessChannelBuilder - // .forName(serverName).directExecutor().build()); - // stub = EcdarBackendGrpc.newBlockingStub(channel); - // QueryProtos.SimulationStartRequest request = QueryProtos.SimulationStartRequest.newBuilder().setSystem("(A || B)").build(); + QueryProtos.SimulationStartRequest request = QueryProtos.SimulationStartRequest.newBuilder().build(); - // var expectedResponse = new ObjectProtos.StateTuple.LocationTuple[components.size()]; + var expectedResponse = new ObjectProtos.Location[components.size()]; - // for (int i = 0; i < components.size(); i++) { - // Component comp = components.get(i); - // expectedResponse[i] = ObjectProtos.StateTuple.LocationTuple.newBuilder() - // .setComponentName(comp.getName()) - // .setId(comp.getInitialLocation().getId()).build(); - // } + for (int i = 0; i < components.size(); i++) { + Component comp = components.get(i); + expectedResponse[i] = ObjectProtos.Location.newBuilder() + .setSpecificComponent(SpecificComponent.newBuilder().setComponentName(comp.getName())) + .setId(comp.getInitialLocation().getId()).build(); + } - // var result = stub.startSimulation(request).getState().getLocationsList().toArray(); + var result = stub.startSimulation(request).getNewDecisionPoint().getSource().getLocationTuple().getLocationsList().toArray(); - // Assertions.assertArrayEquals(expectedResponse, result); - // } catch (IOException e) { - // Assertions.fail("Exception encountered: " + e.getMessage()); - // } - // } + Assertions.assertArrayEquals(expectedResponse, result); + } catch (IOException e) { + Assertions.fail("Exception encountered: " + e.getMessage()); + } + } private List generateComponentsWithInitialLocations() { List comps = new ArrayList<>(); From 0db2ea2ccb912dd6f0d98fa58e2559fafabafc43 Mon Sep 17 00:00:00 2001 From: Dolmer1 Date: Thu, 17 Nov 2022 13:11:02 +0100 Subject: [PATCH 087/120] PR changes --- .gitmodules | 3 ++- .../java/ecdar/backend/SimulationHandler.java | 18 ++++-------------- .../ecdar/controllers/EcdarController.java | 2 +- ...mulationInitializationDialogController.java | 8 ++++---- .../ecdar/controllers/SimulatorController.java | 9 +++------ ...lationInitializationDialogPresentation.fxml | 2 +- 6 files changed, 15 insertions(+), 27 deletions(-) diff --git a/.gitmodules b/.gitmodules index 2e15cc85..53cad6ef 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule "src/main/proto"] path = src/main/proto - url = https://github.com/Ecdar-SW5/Ecdar-ProtoBuf.git + url = ../Ecdar-ProtoBuf.git + diff --git a/src/main/java/ecdar/backend/SimulationHandler.java b/src/main/java/ecdar/backend/SimulationHandler.java index d58d7b3b..adcb8f29 100755 --- a/src/main/java/ecdar/backend/SimulationHandler.java +++ b/src/main/java/ecdar/backend/SimulationHandler.java @@ -28,7 +28,7 @@ */ public class SimulationHandler { public static final String QUERY_PREFIX = "Query: "; - public String composition; + private String composition; private ObjectProperty currentConcreteState = new SimpleObjectProperty<>(); private ObjectProperty initialConcreteState = new SimpleObjectProperty<>(); private ObjectProperty currentTime = new SimpleObjectProperty<>(); @@ -38,13 +38,6 @@ public class SimulationHandler { private SimulationStateSuccessor successor; private int numberOfSteps; - /** - * A string to keep track what is currently being simulated - * For now the string is prefixed with {@link #QUERY_PREFIX} when doing a query simulation - * and kept empty when doing system simulations - */ - private String currentSimulation = ""; - private final ObservableMap simulationVariables = FXCollections.observableHashMap(); private final ObservableMap simulationClocks = FXCollections.observableHashMap(); /** @@ -68,9 +61,6 @@ public SimulationHandler(BackendDriver backendDriver) { /** * Initializes the default system (non-query system) */ - public void initializeDefaultSystem() { - currentSimulation = ""; - } /** * Initializes the values and properties in the {@link SimulationHandler}. @@ -457,9 +447,9 @@ public EcdarSystem getSystem() { return system; } - public String getCurrentSimulation() { - return currentSimulation; - } + public String getComposition() { return composition;} + + public void setComposition(String composition) {this.composition = composition;} public boolean isSimulationRunning() { return false; // ToDo: Implement diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index a8bd7c5a..75ff103d 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -294,7 +294,7 @@ private void initializeDialogs() { simulationInitializationDialog.getController().startButton.setOnMouseClicked(event -> { // ToDo NIELS: Start simulation of selected query - Ecdar.getSimulationHandler().composition = simulationInitializationDialog.getController().simulationComboBox.getSelectionModel().getSelectedItem(); + Ecdar.getSimulationHandler().setComposition(simulationInitializationDialog.getController().simulationComboBox.getSelectionModel().getSelectedItem()); currentMode.setValue(Mode.Simulator); simulationInitializationDialog.close(); }); diff --git a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java index 1960d465..4e42e76b 100644 --- a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java +++ b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java @@ -21,20 +21,20 @@ public class SimulationInitializationDialogController implements Initializable { * Function gets list of components to simulation * and saves it in the public static ListOfComponents */ - public void GetListOfComponentsToSimulate(){ + public void SetListOfComponentsToSimulate(){ String componentsToSimulate = simulationComboBox.getSelectionModel().getSelectedItem(); //filters out all components by ignoring operators. Pattern pattern = Pattern.compile("([\\w]*)", Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(componentsToSimulate); - List listOfComponents = new ArrayList<>(); + List listOfComponentsToSimulate = new ArrayList<>(); //Adds all found components to list. while(matcher.find()){ if(matcher.group().length() != 0) { - listOfComponents.add(matcher.group()); + listOfComponentsToSimulate.add(matcher.group()); } } - ListOfComponents = listOfComponents; + ListOfComponents = listOfComponentsToSimulate; } public void initialize(URL location, ResourceBundle resources) { diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index b08976ad..39789bfc 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -51,7 +51,7 @@ public void willShow() { if (sm.getCurrentState() == null) sm.initialStep(); // ToDo NIELS: Find better solution //Have the user left a trace or is he simulating a query - if (sm.traceLog.size() >= 2 || sm.getCurrentSimulation().contains(SimulationHandler.QUERY_PREFIX)) { + if (sm.traceLog.size() >= 2) { shouldSimulationBeReset = false; } @@ -75,7 +75,6 @@ public void willShow() { */ private void resetSimulation() { final SimulationHandler sm = Ecdar.getSimulationHandler(); - sm.initializeDefaultSystem(); overviewPresentation.getController().clearOverview(); overviewPresentation.getController().getComponentObservableList().clear(); @@ -84,10 +83,8 @@ private void resetSimulation() { } /** - * Finds the components that are used in the current simulation by looking at the component found in - * {@link Project#getComponents()} and compare them to the processes declared in the {@link SimulationHandler#getSystem()} - *

- * TODO This does currently not work if the same component is used multiple times. + * Finds the components that are used in the current simulation by looking at the components found in + * Ecdar.getProject.getComponents() and compares them to the components found in the queryComponents list * * @return all the components used in the current simulation */ diff --git a/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml b/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml index 6df548fa..8fa6635b 100644 --- a/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml +++ b/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml @@ -36,7 +36,7 @@ - + From 36f559fb99d22a2b5607e257e578278ddc74a684 Mon Sep 17 00:00:00 2001 From: APaludan Date: Thu, 17 Nov 2022 13:31:13 +0100 Subject: [PATCH 088/120] Update .gitmodules --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 53cad6ef..808fd3cf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "src/main/proto"] path = src/main/proto - url = ../Ecdar-ProtoBuf.git + url = https://github.com/Ecdar-SW5/Ecdar-ProtoBuf.git From 8f0c405647cde56736f9f9429b37f6c507650e01 Mon Sep 17 00:00:00 2001 From: APaludan Date: Thu, 17 Nov 2022 13:48:06 +0100 Subject: [PATCH 089/120] maybe github checks works now? --- src/test/java/ecdar/TestFXBase.java | 8 +++++--- src/test/java/ecdar/simulation/SimulationTest.java | 1 - 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/test/java/ecdar/TestFXBase.java b/src/test/java/ecdar/TestFXBase.java index e54eecec..843527ee 100644 --- a/src/test/java/ecdar/TestFXBase.java +++ b/src/test/java/ecdar/TestFXBase.java @@ -5,6 +5,7 @@ import javafx.stage.Stage; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.testfx.api.FxRobot; import org.testfx.api.FxToolkit; import org.testfx.framework.junit.ApplicationTest; @@ -22,9 +23,10 @@ public void start(Stage stage) { } @AfterAll - public void afterEachTest() throws TimeoutException { + public static void afterEachTest() throws TimeoutException { FxToolkit.hideStage(); - release(new KeyCode[]{}); - release(new MouseButton[]{}); + FxRobot robot = new FxRobot(); + robot.release(new KeyCode[]{}); + robot.release(new MouseButton[]{}); } } diff --git a/src/test/java/ecdar/simulation/SimulationTest.java b/src/test/java/ecdar/simulation/SimulationTest.java index d1906460..c815e9f3 100644 --- a/src/test/java/ecdar/simulation/SimulationTest.java +++ b/src/test/java/ecdar/simulation/SimulationTest.java @@ -27,7 +27,6 @@ import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.Assertions; -@TestInstance(Lifecycle.PER_CLASS) public class SimulationTest extends TestFXBase { public GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); private final String serverName = InProcessServerBuilder.generateName(); From d1b7b948194242d1c9a57708aaca45b1a656df68 Mon Sep 17 00:00:00 2001 From: Dolmer1 Date: Thu, 17 Nov 2022 13:59:25 +0100 Subject: [PATCH 090/120] deleted variable that is never used --- src/main/java/ecdar/controllers/SimulatorController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index 39789bfc..c791abf1 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -90,8 +90,8 @@ private void resetSimulation() { */ private List findComponentsInCurrentSimulation(List queryComponents) { //Show components from the system - final SimulationHandler sm = Ecdar.getSimulationHandler(); List components = new ArrayList<>(); + components = Ecdar.getProject().getComponents(); //Matches query components against with existing components and adds them to simulation From f2fc61ce73ab51149db145683388df2070e4cc0f Mon Sep 17 00:00:00 2001 From: APaludan Date: Thu, 17 Nov 2022 14:01:23 +0100 Subject: [PATCH 091/120] Update build.yml --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 797f2ae0..2b3a8a10 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,8 +16,9 @@ jobs: submodules: recursive - name: Set up JDK 11 - uses: actions/setup-java@v1 + uses: actions/setup-java@v2 with: + distribution: 'adopt' java-version: 11 java-package: jdk - name: Cache Gradle packages From caa80a917325f7e9896f5e3cf3fe08d949b9b69c Mon Sep 17 00:00:00 2001 From: APaludan Date: Thu, 17 Nov 2022 22:02:57 +0100 Subject: [PATCH 092/120] Update SimulationTest.java --- src/test/java/ecdar/simulation/SimulationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/ecdar/simulation/SimulationTest.java b/src/test/java/ecdar/simulation/SimulationTest.java index c815e9f3..16aba017 100644 --- a/src/test/java/ecdar/simulation/SimulationTest.java +++ b/src/test/java/ecdar/simulation/SimulationTest.java @@ -27,7 +27,7 @@ import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.Assertions; -public class SimulationTest extends TestFXBase { +public class SimulationTest { public GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); private final String serverName = InProcessServerBuilder.generateName(); From 874f4e227b31426874558f6c0e00dee991e3a59d Mon Sep 17 00:00:00 2001 From: APaludan Date: Fri, 18 Nov 2022 10:08:40 +0100 Subject: [PATCH 093/120] remove unused imports --- .../java/ecdar/abstractions/ComponentInstance.java | 1 - src/main/java/ecdar/abstractions/Declarations.java | 3 --- .../java/ecdar/abstractions/EcdarSystemEdge.java | 1 - src/main/java/ecdar/abstractions/Edge.java | 1 - src/main/java/ecdar/abstractions/Location.java | 1 - src/main/java/ecdar/abstractions/Nail.java | 1 - src/main/java/ecdar/abstractions/Query.java | 1 - src/main/java/ecdar/abstractions/Transition.java | 5 ----- src/main/java/ecdar/backend/QueryHandler.java | 1 - src/main/java/ecdar/backend/SimulationHandler.java | 1 - .../java/ecdar/controllers/ComponentController.java | 1 - .../controllers/ComponentOperatorController.java | 1 - .../ecdar/controllers/LeftSimPaneController.java | 1 - .../java/ecdar/controllers/LocationController.java | 1 - .../ecdar/controllers/ProjectPaneController.java | 1 - .../java/ecdar/controllers/QueryPaneController.java | 10 ---------- .../SimulationInitializationDialogController.java | 2 -- .../java/ecdar/controllers/SimulatorController.java | 1 - .../ecdar/controllers/SystemEdgeController.java | 1 - src/main/java/ecdar/mutation/ExportHandler.java | 3 --- .../ecdar/mutation/TestCaseGenerationHandler.java | 2 -- src/main/java/ecdar/presentations/DropDownMenu.java | 3 +-- src/main/java/ecdar/presentations/Link.java | 3 +-- src/main/java/ecdar/presentations/MenuElement.java | 12 +++--------- .../presentations/MultiSyncTagPresentation.java | 1 - .../java/ecdar/presentations/QueryPresentation.java | 13 ------------- .../presentations/RightSimPanePresentation.java | 1 - .../SimulationInitializationDialogPresentation.java | 2 -- .../java/ecdar/presentations/TagPresentation.java | 2 -- src/main/java/ecdar/simulation/SimulationState.java | 2 -- .../ecdar/simulation/SimulationStateSuccessor.java | 2 -- .../java/ecdar/utility/helpers/ItemDragHelper.java | 3 --- .../java/ecdar/utility/helpers/MouseCircular.java | 3 --- src/test/java/ecdar/mutation/StrategyRuleTest.java | 2 -- .../operators/ChangeTargetOperatorTest.java | 1 - .../operators/SinkLocationOperatorTest.java | 3 +-- src/test/java/ecdar/simulation/SimulationTest.java | 3 --- src/test/java/ecdar/ui/SidePaneTest.java | 2 -- 38 files changed, 6 insertions(+), 92 deletions(-) diff --git a/src/main/java/ecdar/abstractions/ComponentInstance.java b/src/main/java/ecdar/abstractions/ComponentInstance.java index 391cae3d..260b44ce 100644 --- a/src/main/java/ecdar/abstractions/ComponentInstance.java +++ b/src/main/java/ecdar/abstractions/ComponentInstance.java @@ -2,7 +2,6 @@ import ecdar.Ecdar; import ecdar.presentations.Grid; -import ecdar.utility.colors.Color; import com.google.gson.JsonObject; import javafx.beans.property.*; import javafx.beans.value.ObservableValue; diff --git a/src/main/java/ecdar/abstractions/Declarations.java b/src/main/java/ecdar/abstractions/Declarations.java index 38f179cb..2014d729 100644 --- a/src/main/java/ecdar/abstractions/Declarations.java +++ b/src/main/java/ecdar/abstractions/Declarations.java @@ -1,15 +1,12 @@ package ecdar.abstractions; import ecdar.utility.colors.Color; -import ecdar.utility.colors.EnabledColor; -import com.google.gson.JsonArray; import com.google.gson.JsonObject; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import org.apache.commons.lang3.tuple.Triple; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/src/main/java/ecdar/abstractions/EcdarSystemEdge.java b/src/main/java/ecdar/abstractions/EcdarSystemEdge.java index d9d30f2c..503e4831 100644 --- a/src/main/java/ecdar/abstractions/EcdarSystemEdge.java +++ b/src/main/java/ecdar/abstractions/EcdarSystemEdge.java @@ -2,7 +2,6 @@ import ecdar.Ecdar; import ecdar.controllers.SystemRootController; -import com.google.gson.JsonElement; import com.google.gson.JsonObject; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; diff --git a/src/main/java/ecdar/abstractions/Edge.java b/src/main/java/ecdar/abstractions/Edge.java index 2b1057c9..995e2ce8 100644 --- a/src/main/java/ecdar/abstractions/Edge.java +++ b/src/main/java/ecdar/abstractions/Edge.java @@ -1,6 +1,5 @@ package ecdar.abstractions; -import EcdarProtoBuf.ComponentProtos; import com.google.gson.JsonPrimitive; import ecdar.Ecdar; import ecdar.controllers.EcdarController; diff --git a/src/main/java/ecdar/abstractions/Location.java b/src/main/java/ecdar/abstractions/Location.java index 183b1fe0..f56ed000 100644 --- a/src/main/java/ecdar/abstractions/Location.java +++ b/src/main/java/ecdar/abstractions/Location.java @@ -1,6 +1,5 @@ package ecdar.abstractions; -import EcdarProtoBuf.ComponentProtos; import ecdar.Ecdar; import ecdar.code_analysis.Nearable; import ecdar.controllers.EcdarController; diff --git a/src/main/java/ecdar/abstractions/Nail.java b/src/main/java/ecdar/abstractions/Nail.java index f7555432..75375d19 100644 --- a/src/main/java/ecdar/abstractions/Nail.java +++ b/src/main/java/ecdar/abstractions/Nail.java @@ -1,6 +1,5 @@ package ecdar.abstractions; -import EcdarProtoBuf.ComponentProtos; import ecdar.utility.helpers.Circular; import ecdar.utility.serialize.Serializable; import com.google.gson.Gson; diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index 639ce469..874e8a25 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -4,7 +4,6 @@ import ecdar.backend.*; import ecdar.controllers.EcdarController; import ecdar.utility.helpers.StringHelper; -import ecdar.utility.helpers.StringValidator; import ecdar.utility.serialize.Serializable; import com.google.gson.JsonObject; import javafx.application.Platform; diff --git a/src/main/java/ecdar/abstractions/Transition.java b/src/main/java/ecdar/abstractions/Transition.java index 9aa71eeb..c79d4f21 100644 --- a/src/main/java/ecdar/abstractions/Transition.java +++ b/src/main/java/ecdar/abstractions/Transition.java @@ -1,12 +1,7 @@ package ecdar.abstractions; import EcdarProtoBuf.ObjectProtos; -import ecdar.Ecdar; - import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; public class Transition { public final ArrayList edges = new ArrayList<>(); diff --git a/src/main/java/ecdar/backend/QueryHandler.java b/src/main/java/ecdar/backend/QueryHandler.java index 5f76ce1e..500a7cc4 100644 --- a/src/main/java/ecdar/backend/QueryHandler.java +++ b/src/main/java/ecdar/backend/QueryHandler.java @@ -1,6 +1,5 @@ package ecdar.backend; -import EcdarProtoBuf.ComponentProtos; import EcdarProtoBuf.QueryProtos; import com.google.gson.JsonObject; import com.google.gson.JsonParser; diff --git a/src/main/java/ecdar/backend/SimulationHandler.java b/src/main/java/ecdar/backend/SimulationHandler.java index adcb8f29..4277bf09 100755 --- a/src/main/java/ecdar/backend/SimulationHandler.java +++ b/src/main/java/ecdar/backend/SimulationHandler.java @@ -1,7 +1,6 @@ package ecdar.backend; import EcdarProtoBuf.ComponentProtos; -import EcdarProtoBuf.ObjectProtos; import EcdarProtoBuf.QueryProtos; import ecdar.Ecdar; import ecdar.abstractions.*; diff --git a/src/main/java/ecdar/controllers/ComponentController.java b/src/main/java/ecdar/controllers/ComponentController.java index 9d67e2aa..24a546d4 100644 --- a/src/main/java/ecdar/controllers/ComponentController.java +++ b/src/main/java/ecdar/controllers/ComponentController.java @@ -13,7 +13,6 @@ import javafx.animation.Interpolator; import javafx.animation.Transition; import javafx.application.Platform; -import javafx.beans.binding.DoubleBinding; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ChangeListener; diff --git a/src/main/java/ecdar/controllers/ComponentOperatorController.java b/src/main/java/ecdar/controllers/ComponentOperatorController.java index f0b7c606..7da44f1c 100644 --- a/src/main/java/ecdar/controllers/ComponentOperatorController.java +++ b/src/main/java/ecdar/controllers/ComponentOperatorController.java @@ -14,7 +14,6 @@ import javafx.scene.control.Label; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; -import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.scene.shape.Circle; import javafx.scene.shape.Polygon; diff --git a/src/main/java/ecdar/controllers/LeftSimPaneController.java b/src/main/java/ecdar/controllers/LeftSimPaneController.java index d8dc30eb..7f68aaaa 100755 --- a/src/main/java/ecdar/controllers/LeftSimPaneController.java +++ b/src/main/java/ecdar/controllers/LeftSimPaneController.java @@ -2,7 +2,6 @@ import ecdar.presentations.TracePaneElementPresentation; import ecdar.presentations.TransitionPaneElementPresentation; -import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.ScrollPane; import javafx.scene.layout.StackPane; diff --git a/src/main/java/ecdar/controllers/LocationController.java b/src/main/java/ecdar/controllers/LocationController.java index 0e36a07e..4d442513 100644 --- a/src/main/java/ecdar/controllers/LocationController.java +++ b/src/main/java/ecdar/controllers/LocationController.java @@ -9,7 +9,6 @@ import ecdar.utility.UndoRedoStack; import ecdar.utility.colors.Color; import ecdar.utility.helpers.ItemDragHelper; -import ecdar.utility.helpers.MouseCircular; import ecdar.utility.helpers.SelectHelper; import ecdar.utility.keyboard.Keybind; import ecdar.utility.keyboard.KeyboardTracker; diff --git a/src/main/java/ecdar/controllers/ProjectPaneController.java b/src/main/java/ecdar/controllers/ProjectPaneController.java index 02c68003..8923cad2 100644 --- a/src/main/java/ecdar/controllers/ProjectPaneController.java +++ b/src/main/java/ecdar/controllers/ProjectPaneController.java @@ -21,7 +21,6 @@ import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.image.ImageView; -import javafx.scene.layout.AnchorPane; import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; diff --git a/src/main/java/ecdar/controllers/QueryPaneController.java b/src/main/java/ecdar/controllers/QueryPaneController.java index 26c003dd..ad2cef79 100644 --- a/src/main/java/ecdar/controllers/QueryPaneController.java +++ b/src/main/java/ecdar/controllers/QueryPaneController.java @@ -3,16 +3,9 @@ import ecdar.Ecdar; import ecdar.abstractions.Query; import ecdar.abstractions.QueryState; -import ecdar.backend.*; -import ecdar.presentations.Grid; import ecdar.presentations.QueryPresentation; import com.jfoenix.controls.JFXRippler; -import ecdar.utility.UndoRedoStack; import ecdar.utility.colors.Color; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.DoubleProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleDoubleProperty; import javafx.collections.ListChangeListener; import javafx.fxml.FXML; import javafx.fxml.Initializable; @@ -21,9 +14,6 @@ import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.layout.*; -import javafx.scene.paint.Paint; -import javafx.scene.shape.Rectangle; - import java.net.URL; import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java index 4e42e76b..bc952bad 100644 --- a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java +++ b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java @@ -2,8 +2,6 @@ import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXComboBox; -import ecdar.Ecdar; -import javafx.fxml.FXML; import javafx.fxml.Initializable; import java.net.URL; diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index c791abf1..d0715558 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -3,7 +3,6 @@ import ecdar.Ecdar; import ecdar.abstractions.*; import ecdar.backend.SimulationHandler; -import ecdar.presentations.SimulationInitializationDialogPresentation; import ecdar.presentations.SimulatorOverviewPresentation; import ecdar.simulation.SimulationState; import ecdar.simulation.Transition; diff --git a/src/main/java/ecdar/controllers/SystemEdgeController.java b/src/main/java/ecdar/controllers/SystemEdgeController.java index f8e46cf5..de846446 100644 --- a/src/main/java/ecdar/controllers/SystemEdgeController.java +++ b/src/main/java/ecdar/controllers/SystemEdgeController.java @@ -16,7 +16,6 @@ import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; -import javafx.scene.layout.Pane; import javafx.scene.shape.Circle; import java.net.URL; diff --git a/src/main/java/ecdar/mutation/ExportHandler.java b/src/main/java/ecdar/mutation/ExportHandler.java index fb5455cb..d75dcdfc 100644 --- a/src/main/java/ecdar/mutation/ExportHandler.java +++ b/src/main/java/ecdar/mutation/ExportHandler.java @@ -3,9 +3,6 @@ import com.google.gson.GsonBuilder; import ecdar.Ecdar; import ecdar.abstractions.Component; -import ecdar.abstractions.Project; -import ecdar.backend.BackendException; -import ecdar.backend.BackendHelper; import ecdar.mutation.models.MutationTestCase; import ecdar.mutation.models.MutationTestPlan; import ecdar.mutation.operators.MutationOperator; diff --git a/src/main/java/ecdar/mutation/TestCaseGenerationHandler.java b/src/main/java/ecdar/mutation/TestCaseGenerationHandler.java index 7d610874..8f408cbd 100644 --- a/src/main/java/ecdar/mutation/TestCaseGenerationHandler.java +++ b/src/main/java/ecdar/mutation/TestCaseGenerationHandler.java @@ -2,12 +2,10 @@ import ecdar.Ecdar; import ecdar.abstractions.Component; -import ecdar.abstractions.Project; import ecdar.backend.BackendException; import ecdar.backend.BackendHelper; import ecdar.mutation.models.MutationTestCase; import ecdar.mutation.models.MutationTestPlan; -import ecdar.mutation.models.NonRefinementStrategy; import javafx.application.Platform; import javafx.scene.paint.Color; import javafx.scene.text.Text; diff --git a/src/main/java/ecdar/presentations/DropDownMenu.java b/src/main/java/ecdar/presentations/DropDownMenu.java index 0fb91c8f..308f0033 100644 --- a/src/main/java/ecdar/presentations/DropDownMenu.java +++ b/src/main/java/ecdar/presentations/DropDownMenu.java @@ -14,7 +14,6 @@ import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Label; -import javafx.scene.control.ScrollPane; import javafx.scene.input.MouseEvent; import javafx.scene.layout.*; import javafx.scene.shape.Circle; @@ -24,7 +23,7 @@ import org.kordamp.ikonli.javafx.FontIcon; import javax.swing.*; -import java.util.Stack; + import java.util.function.BiConsumer; import java.util.function.Consumer; diff --git a/src/main/java/ecdar/presentations/Link.java b/src/main/java/ecdar/presentations/Link.java index d4e30147..1c3cc98e 100644 --- a/src/main/java/ecdar/presentations/Link.java +++ b/src/main/java/ecdar/presentations/Link.java @@ -1,9 +1,8 @@ package ecdar.presentations; import ecdar.Debug; -import ecdar.abstractions.EdgeStatus; -import ecdar.utility.colors.Color; import ecdar.utility.Highlightable; +import ecdar.utility.colors.Color; import ecdar.utility.helpers.SelectHelper; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; diff --git a/src/main/java/ecdar/presentations/MenuElement.java b/src/main/java/ecdar/presentations/MenuElement.java index 8a145e1e..3094fdeb 100644 --- a/src/main/java/ecdar/presentations/MenuElement.java +++ b/src/main/java/ecdar/presentations/MenuElement.java @@ -1,12 +1,8 @@ package ecdar.presentations; -import ecdar.Ecdar; -import ecdar.controllers.EcdarController; import ecdar.utility.colors.Color; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ObservableBooleanValue; -import javafx.css.CssMetaData; -import javafx.css.Styleable; import javafx.geometry.Insets; import javafx.scene.Node; import javafx.scene.control.Label; @@ -14,15 +10,13 @@ import javafx.scene.layout.*; import org.kordamp.ikonli.javafx.FontIcon; - -import java.util.List; import java.util.function.Consumer; import static javafx.scene.paint.Color.TRANSPARENT; import static javafx.scene.paint.Color.WHITE; -/** - * Represents an element of the dropdown menu, excluding spacer and the colour palette element. + +/* Represents an element of the dropdown menu, excluding spacer and the colour palette element. */ public class MenuElement { public static final javafx.scene.paint.Color DISABLED_COLOR = Color.GREY_BLUE.getColor(Color.Intensity.I300); @@ -110,7 +104,7 @@ public MenuElement(final String s, final String iconString, final Consumer mouseEventConsumer){ rippler = new ReleaseRippler(clickListenerFix); - rippler.setRipplerFill(javafx.scene.paint.Color.TRANSPARENT); + rippler.setRipplerFill(TRANSPARENT); rippler.setOnMouseEntered(event -> { if (isDisabled.get()) return; diff --git a/src/main/java/ecdar/presentations/MultiSyncTagPresentation.java b/src/main/java/ecdar/presentations/MultiSyncTagPresentation.java index c5929f93..e3f86927 100644 --- a/src/main/java/ecdar/presentations/MultiSyncTagPresentation.java +++ b/src/main/java/ecdar/presentations/MultiSyncTagPresentation.java @@ -4,7 +4,6 @@ import ecdar.abstractions.DisplayableEdge; import ecdar.abstractions.Edge; import ecdar.abstractions.GroupedEdge; -import ecdar.abstractions.Nail; import ecdar.controllers.EcdarController; import ecdar.controllers.MultiSyncTagController; import ecdar.utility.UndoRedoStack; diff --git a/src/main/java/ecdar/presentations/QueryPresentation.java b/src/main/java/ecdar/presentations/QueryPresentation.java index 5a897137..1d1e8c72 100644 --- a/src/main/java/ecdar/presentations/QueryPresentation.java +++ b/src/main/java/ecdar/presentations/QueryPresentation.java @@ -19,22 +19,9 @@ import javafx.scene.input.KeyCode; import javafx.scene.layout.*; import javafx.geometry.Point2D; -import javafx.geometry.Pos; -import javafx.geometry.Rectangle2D; -import javafx.scene.Cursor; -import javafx.scene.control.Label; -import javafx.scene.control.ScrollPane; -import javafx.scene.control.TitledPane; -import javafx.scene.control.Tooltip; -import javafx.scene.input.KeyCode; -import javafx.scene.layout.*; -import javafx.scene.text.TextAlignment; -import javafx.stage.Screen; -import javafx.stage.StageStyle; import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.material.Material; -import javax.swing.*; import java.util.Map; import java.util.Set; import java.util.function.Consumer; diff --git a/src/main/java/ecdar/presentations/RightSimPanePresentation.java b/src/main/java/ecdar/presentations/RightSimPanePresentation.java index 48c79f1c..2345664d 100755 --- a/src/main/java/ecdar/presentations/RightSimPanePresentation.java +++ b/src/main/java/ecdar/presentations/RightSimPanePresentation.java @@ -2,7 +2,6 @@ import ecdar.controllers.RightSimPaneController; import ecdar.utility.colors.Color; -import ecdar.utility.helpers.DropShadowHelper; import javafx.geometry.Insets; import javafx.scene.layout.*; diff --git a/src/main/java/ecdar/presentations/SimulationInitializationDialogPresentation.java b/src/main/java/ecdar/presentations/SimulationInitializationDialogPresentation.java index 100d3b52..e9c9664a 100644 --- a/src/main/java/ecdar/presentations/SimulationInitializationDialogPresentation.java +++ b/src/main/java/ecdar/presentations/SimulationInitializationDialogPresentation.java @@ -1,8 +1,6 @@ package ecdar.presentations; -import com.jfoenix.controls.JFXComboBox; import com.jfoenix.controls.JFXDialog; -import ecdar.controllers.BackendOptionsDialogController; import ecdar.controllers.SimulationInitializationDialogController; public class SimulationInitializationDialogPresentation extends JFXDialog { diff --git a/src/main/java/ecdar/presentations/TagPresentation.java b/src/main/java/ecdar/presentations/TagPresentation.java index a4d4610f..f5b32273 100644 --- a/src/main/java/ecdar/presentations/TagPresentation.java +++ b/src/main/java/ecdar/presentations/TagPresentation.java @@ -17,10 +17,8 @@ import javafx.geometry.Insets; import javafx.scene.Cursor; import javafx.scene.control.Label; -import javafx.scene.input.KeyCode; import javafx.scene.input.MouseEvent; import javafx.scene.layout.StackPane; -import javafx.scene.robot.Robot; import javafx.scene.shape.LineTo; import javafx.scene.shape.MoveTo; import javafx.scene.shape.Path; diff --git a/src/main/java/ecdar/simulation/SimulationState.java b/src/main/java/ecdar/simulation/SimulationState.java index 935ec5de..0a1c03f8 100644 --- a/src/main/java/ecdar/simulation/SimulationState.java +++ b/src/main/java/ecdar/simulation/SimulationState.java @@ -1,12 +1,10 @@ package ecdar.simulation; import EcdarProtoBuf.ObjectProtos; -import ecdar.abstractions.Location; import javafx.util.Pair; import java.math.BigDecimal; import java.util.ArrayList; -import java.util.stream.Collectors; public class SimulationState { private final ArrayList> locations; diff --git a/src/main/java/ecdar/simulation/SimulationStateSuccessor.java b/src/main/java/ecdar/simulation/SimulationStateSuccessor.java index d05f59b0..bf6eb85d 100644 --- a/src/main/java/ecdar/simulation/SimulationStateSuccessor.java +++ b/src/main/java/ecdar/simulation/SimulationStateSuccessor.java @@ -1,7 +1,5 @@ package ecdar.simulation; -import ecdar.Ecdar; - import java.util.ArrayList; public class SimulationStateSuccessor { diff --git a/src/main/java/ecdar/utility/helpers/ItemDragHelper.java b/src/main/java/ecdar/utility/helpers/ItemDragHelper.java index b12375c5..2b601271 100644 --- a/src/main/java/ecdar/utility/helpers/ItemDragHelper.java +++ b/src/main/java/ecdar/utility/helpers/ItemDragHelper.java @@ -3,7 +3,6 @@ import ecdar.controllers.EcdarController; import ecdar.controllers.EdgeController; import ecdar.presentations.ComponentOperatorPresentation; -import ecdar.presentations.ComponentPresentation; import ecdar.presentations.Grid; import ecdar.utility.UndoRedoStack; import javafx.beans.property.BooleanProperty; @@ -19,8 +18,6 @@ import java.util.List; import java.util.function.Supplier; -import static ecdar.presentations.Grid.GRID_SIZE; - public class ItemDragHelper { public static class DragBounds { diff --git a/src/main/java/ecdar/utility/helpers/MouseCircular.java b/src/main/java/ecdar/utility/helpers/MouseCircular.java index 6814b108..3672f52b 100644 --- a/src/main/java/ecdar/utility/helpers/MouseCircular.java +++ b/src/main/java/ecdar/utility/helpers/MouseCircular.java @@ -1,13 +1,10 @@ package ecdar.utility.helpers; import ecdar.controllers.EcdarController; -import ecdar.presentations.Grid; import ecdar.utility.mouse.MouseTracker; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; -import static ecdar.presentations.Grid.GRID_SIZE; - public class MouseCircular implements Circular { private final DoubleProperty x = new SimpleDoubleProperty(0d); private final DoubleProperty y = new SimpleDoubleProperty(0d); diff --git a/src/test/java/ecdar/mutation/StrategyRuleTest.java b/src/test/java/ecdar/mutation/StrategyRuleTest.java index 2fad1c8c..5ed7144a 100644 --- a/src/test/java/ecdar/mutation/StrategyRuleTest.java +++ b/src/test/java/ecdar/mutation/StrategyRuleTest.java @@ -5,8 +5,6 @@ import org.junit.jupiter.api.Assertions; import java.util.HashMap; import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public class StrategyRuleTest { diff --git a/src/test/java/ecdar/mutation/operators/ChangeTargetOperatorTest.java b/src/test/java/ecdar/mutation/operators/ChangeTargetOperatorTest.java index 102ef715..fe3a869e 100644 --- a/src/test/java/ecdar/mutation/operators/ChangeTargetOperatorTest.java +++ b/src/test/java/ecdar/mutation/operators/ChangeTargetOperatorTest.java @@ -5,7 +5,6 @@ import ecdar.abstractions.Edge; import ecdar.abstractions.EdgeStatus; import ecdar.abstractions.Location; -import ecdar.mutation.operators.ChangeTargetOperator; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; diff --git a/src/test/java/ecdar/mutation/operators/SinkLocationOperatorTest.java b/src/test/java/ecdar/mutation/operators/SinkLocationOperatorTest.java index b4d9a492..8970f1f4 100644 --- a/src/test/java/ecdar/mutation/operators/SinkLocationOperatorTest.java +++ b/src/test/java/ecdar/mutation/operators/SinkLocationOperatorTest.java @@ -4,9 +4,8 @@ import ecdar.abstractions.Edge; import ecdar.abstractions.EdgeStatus; import ecdar.abstractions.Location; -import ecdar.mutation.operators.SinkLocationOperator; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class SinkLocationOperatorTest { diff --git a/src/test/java/ecdar/simulation/SimulationTest.java b/src/test/java/ecdar/simulation/SimulationTest.java index 16aba017..c0584f3a 100644 --- a/src/test/java/ecdar/simulation/SimulationTest.java +++ b/src/test/java/ecdar/simulation/SimulationTest.java @@ -6,7 +6,6 @@ import EcdarProtoBuf.ObjectProtos.DecisionPoint; import EcdarProtoBuf.ObjectProtos.LocationTuple; import EcdarProtoBuf.ObjectProtos.SpecificComponent; -import ecdar.TestFXBase; import ecdar.abstractions.Component; import ecdar.abstractions.Location; import io.grpc.BindableService; @@ -23,8 +22,6 @@ import java.util.stream.Collectors; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.Assertions; public class SimulationTest { diff --git a/src/test/java/ecdar/ui/SidePaneTest.java b/src/test/java/ecdar/ui/SidePaneTest.java index 73214791..b38a65de 100644 --- a/src/test/java/ecdar/ui/SidePaneTest.java +++ b/src/test/java/ecdar/ui/SidePaneTest.java @@ -11,8 +11,6 @@ import org.testfx.util.WaitForAsyncUtils; import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Predicate; From 51fd6ecdb9b8bd0a99de13bd8850bf0c403562ac Mon Sep 17 00:00:00 2001 From: APaludan Date: Fri, 18 Nov 2022 10:42:19 +0100 Subject: [PATCH 094/120] edit comment --- src/main/java/ecdar/controllers/SimulatorController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index d0715558..e5c47843 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -49,7 +49,7 @@ public void willShow() { if (sm.getCurrentState() == null) sm.initialStep(); // ToDo NIELS: Find better solution - //Have the user left a trace or is he simulating a query + //Have the user left a trace if (sm.traceLog.size() >= 2) { shouldSimulationBeReset = false; } From 080597d136e525481d0150ef22ba361cdfff1c7c Mon Sep 17 00:00:00 2001 From: Sigurd Date: Fri, 18 Nov 2022 11:43:25 +0100 Subject: [PATCH 095/120] small changes after merge --- .../java/ecdar/abstractions/Component.java | 7 +------ src/main/java/ecdar/backend/QueryHandler.java | 2 +- .../presentations/LocationPresentation.java | 18 +----------------- 3 files changed, 3 insertions(+), 24 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Component.java b/src/main/java/ecdar/abstractions/Component.java index a5cdb5f8..37789bfe 100644 --- a/src/main/java/ecdar/abstractions/Component.java +++ b/src/main/java/ecdar/abstractions/Component.java @@ -547,12 +547,7 @@ public void removeFailingLocations() { public ObservableList getFailingEdges() { return failingEdges; } - * Observable list of all failing locations. - * @return Observable list of all failing locations. - */ - public ObservableList getFailingLocations() { - return failingLocations; - } + /** * Returns all DisplayableEdges of the component (returning a list potentially containing GroupEdges and Edges) diff --git a/src/main/java/ecdar/backend/QueryHandler.java b/src/main/java/ecdar/backend/QueryHandler.java index 4a495eac..8e018da4 100644 --- a/src/main/java/ecdar/backend/QueryHandler.java +++ b/src/main/java/ecdar/backend/QueryHandler.java @@ -138,7 +138,7 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getDeterminism().getReason())); query.getSuccessConsumer().accept(false); query.getStateConsumer().accept(value.getQueryOk().getDeterminism().getState()); - query.getActionConsumer().accept(value.getQueryOk().getDeterminism().getAction(); + query.getActionConsumer().accept(value.getQueryOk().getDeterminism().getAction()); } break; diff --git a/src/main/java/ecdar/presentations/LocationPresentation.java b/src/main/java/ecdar/presentations/LocationPresentation.java index e8e433ae..0b25e090 100644 --- a/src/main/java/ecdar/presentations/LocationPresentation.java +++ b/src/main/java/ecdar/presentations/LocationPresentation.java @@ -171,22 +171,6 @@ private void initializeIdLabel() { } }; - final Consumer handleFailingUpdate = (isFailing) -> { - if(isFailing) { - updateColor.accept(Color.RED, colorIntensity.get()); - } else { - updateColor.accept(location.getColor(), colorIntensity.get()); - } - }; - - final Consumer handleFailingUpdate = (isFailing) -> { - if(isFailing) { - updateColor.accept(Color.RED, colorIntensity.get()); - } else { - updateColor.accept(location.getColor(), colorIntensity.get()); - } - }; - updateColorDelegates.add(updateColor); // Set the initial color @@ -194,7 +178,7 @@ private void initializeIdLabel() { // Update the color of the circle when the color of the location is updated color.addListener((obs, old, newColor) -> updateColor.accept(newColor, colorIntensity.get())); - failing.addListener((obs, old, newFailing) -> handleFailingUpdate.accept(newFailing)); + failing.addListener((obs, old, newFailing) -> updateColor.accept(color.get(), colorIntensity.get() )); } private void initializeTags() { From f63033a0dcd1843d6f7c06c7da2c0f9adebc7840 Mon Sep 17 00:00:00 2001 From: Sigurd Date: Fri, 18 Nov 2022 12:12:31 +0100 Subject: [PATCH 096/120] Update submodule --- src/main/proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/proto b/src/main/proto index c181269f..22f7b52a 160000 --- a/src/main/proto +++ b/src/main/proto @@ -1 +1 @@ -Subproject commit c181269f734c85ec51ae7e09de10cb685e54c595 +Subproject commit 22f7b52a65a7dcf56563eb7b5d353ce4c47dd231 From 5ed4439164d207af7fe0c33ca772c4c612f6dde6 Mon Sep 17 00:00:00 2001 From: Sigurd Date: Mon, 21 Nov 2022 13:23:56 +0100 Subject: [PATCH 097/120] Implemented specific component in consumer --- src/main/java/ecdar/abstractions/Query.java | 41 +++++++++---------- src/main/java/ecdar/backend/QueryHandler.java | 23 +++++++---- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index 86599dbe..76cc5930 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -10,6 +10,7 @@ import javafx.application.Platform; import javafx.beans.property.*; +import java.util.function.BiConsumer; import java.util.function.Consumer; public class Query implements Serializable { @@ -62,25 +63,24 @@ public class Query implements Serializable { } }; - private final Consumer stateConsumer = (state) -> { + private final BiConsumer stateActionConsumer = (state, action) -> { for (Component c : Ecdar.getProject().getComponents()) { c.removeFailingLocations(); - if (query.getValue().contains(c.getName())) { - for (ObjectProtos.Location location : state.getLocationTuple().getLocationsList()) { - c.addFailingLocation(location.getId()); - } - } - } - }; - - private final Consumer actionConsumer = (action) -> { - for (Component c : Ecdar.getProject().getComponents()) { c.removeFailingEdges(); - if (query.getValue().contains(c.getName())) { - for (Edge edge : c.getEdges()) { - if(action.equals(edge.getSync()) && edge.getSourceLocation().getFailing()) { - c.addFailingEdge(edge); - } + } + for (ObjectProtos.Location location : state.getLocationTuple().getLocationsList()) { + Component c = Ecdar.getProject().findComponent(location.getSpecificComponent().getComponentName()); + if (c == null) { + throw new NullPointerException("Could not find the specific component: " + location.getSpecificComponent().getComponentName()); + } + Location l = c.findLocation(location.getId()); + if (l == null) { + throw new NullPointerException("Could not find location: " + location.getId()); + } + c.addFailingLocation(l.getId()); + for (Edge edge : c.getEdges()) { + if(action.equals(edge.getSync()) && edge.getSourceLocation() == l) { + c.addFailingEdge(edge); } } } @@ -176,16 +176,13 @@ public Consumer getFailureConsumer() { } /** - * Getter for the state consumer. + * Getter for the state action consumer. * @return The State Consumer */ - public Consumer getStateConsumer() { - return stateConsumer; + public BiConsumer getStateActionConsumer() { + return stateActionConsumer; } - public Consumer getActionConsumer() { - return actionConsumer; - } @Override public JsonObject serialize() { diff --git a/src/main/java/ecdar/backend/QueryHandler.java b/src/main/java/ecdar/backend/QueryHandler.java index 8e018da4..c068c2f7 100644 --- a/src/main/java/ecdar/backend/QueryHandler.java +++ b/src/main/java/ecdar/backend/QueryHandler.java @@ -97,6 +97,7 @@ public void closeAllBackendConnections() throws IOException { } private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { + System.out.println(value); // If the query has been cancelled, ignore the result if (query.getQueryState() == QueryState.UNKNOWN) return; switch (value.getResponseCase()) { @@ -111,8 +112,8 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.setQueryState(QueryState.ERROR); query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getRefinement().getReason())); query.getSuccessConsumer().accept(false); - query.getStateConsumer().accept(value.getQueryOk().getRefinement().getState()); - query.getActionConsumer().accept(value.getQueryOk().getRefinement().getAction()); + query.getStateActionConsumer().accept(value.getQueryOk().getRefinement().getState(), + value.getQueryOk().getRefinement().getAction()); } break; @@ -124,8 +125,9 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.setQueryState(QueryState.ERROR); query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getConsistency().getReason())); query.getSuccessConsumer().accept(false); - query.getStateConsumer().accept(value.getQueryOk().getConsistency().getState()); - query.getActionConsumer().accept(value.getQueryOk().getConsistency().getAction()); + query.getStateActionConsumer().accept(value.getQueryOk().getConsistency().getState(), + value.getQueryOk().getConsistency().getAction()); + } break; @@ -137,8 +139,9 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.setQueryState(QueryState.ERROR); query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getDeterminism().getReason())); query.getSuccessConsumer().accept(false); - query.getStateConsumer().accept(value.getQueryOk().getDeterminism().getState()); - query.getActionConsumer().accept(value.getQueryOk().getDeterminism().getAction()); + query.getStateActionConsumer().accept(value.getQueryOk().getDeterminism().getState(), + value.getQueryOk().getDeterminism().getAction()); + } break; @@ -150,7 +153,9 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.setQueryState(QueryState.ERROR); query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getImplementation().getReason())); query.getSuccessConsumer().accept(false); - query.getStateConsumer().accept(value.getQueryOk().getImplementation().getState()); + //ToDo: These errors are not implemented in the Reveaal backend. + query.getStateActionConsumer().accept(value.getQueryOk().getImplementation().getState(), + ""); } break; @@ -162,7 +167,9 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { query.setQueryState(QueryState.ERROR); query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getReachability().getReason())); query.getSuccessConsumer().accept(false); - query.getStateConsumer().accept(value.getQueryOk().getReachability().getState()); + //ToDo: These errors are not implemented in the Reveaal backend. + query.getStateActionConsumer().accept(value.getQueryOk().getReachability().getState(), + ""); } break; From 27a8b93a9ff3d2305b3bb1e3f73c9d2ee38bb7f9 Mon Sep 17 00:00:00 2001 From: WassawRoki <56611129+WassawRoki@users.noreply.github.com> Date: Mon, 21 Nov 2022 13:51:59 +0100 Subject: [PATCH 098/120] fixed --- src/main/java/ecdar/controllers/SimulatorController.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index 591d794b..50851fc4 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -76,8 +76,6 @@ public void willShow() { * {@link SimulatorOverviewController#processContainer} and adding the processes of the new simulation. */ private void resetSimulation() { - final SimulationHandler sm = Ecdar.getSimulationHandler(); - sm.initializeDefaultSystem(); List listOfComponentsForSimulation = findComponentsInCurrentSimulation(SimulationInitializationDialogController.ListOfComponents); overviewPresentation.getController().clearOverview(); overviewPresentation.getController().getComponentObservableList().clear(); @@ -93,13 +91,13 @@ private void initialstatelighter(List listofComponents){ Location initiallocation = comp.getInitialLocation(); initiallocation.setColor(Color.ORANGE); List tempedge = comp.getRelatedEdges(initiallocation); - for(DisplayableEdge e: tempedge) + /* for(DisplayableEdge e: tempedge) { if(e.getSourceLocation() == initiallocation) { e.setIsHighlighted(true); } - } + }*/ } } /** From dd6179d857fbc2ab49e6034d251ede0b4de0c1a9 Mon Sep 17 00:00:00 2001 From: "Julie H. Bengtsson" <82820935+jhbengtsson@users.noreply.github.com> Date: Mon, 21 Nov 2022 13:55:28 +0100 Subject: [PATCH 099/120] Update Component.java --- src/main/java/ecdar/abstractions/Component.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Component.java b/src/main/java/ecdar/abstractions/Component.java index 76f16fc2..37789bfe 100644 --- a/src/main/java/ecdar/abstractions/Component.java +++ b/src/main/java/ecdar/abstractions/Component.java @@ -99,7 +99,6 @@ public Component(final JsonObject json) { bindReachabilityAnalysis(); } - /** * Creates a clone of another component. * Copies objects used for verification (e.g. locations, edges and the declarations). @@ -120,7 +119,6 @@ public Component cloneForVerification() { return clone; } - /** * Adds objects used for verifications to this component. * @param original the component to add from From bccc37dc91f4a1684bbd9dbbeaff03474134bf34 Mon Sep 17 00:00:00 2001 From: "Julie H. Bengtsson" <82820935+jhbengtsson@users.noreply.github.com> Date: Mon, 21 Nov 2022 13:55:51 +0100 Subject: [PATCH 100/120] Update SimulatorController.java --- src/main/java/ecdar/controllers/SimulatorController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index 50851fc4..da2acc9c 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -85,6 +85,7 @@ private void resetSimulation() { //Method that colors all initial states. initialstatelighter(listOfComponentsForSimulation); } + private void initialstatelighter(List listofComponents){ for(Component comp: listofComponents) { From e3b6a39a3bad453de1fd64376223eef436bff287 Mon Sep 17 00:00:00 2001 From: Sigurd Date: Mon, 21 Nov 2022 14:13:54 +0100 Subject: [PATCH 101/120] Remove unused imports --- src/main/java/ecdar/controllers/SimulatorController.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index da2acc9c..36539552 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -1,6 +1,5 @@ package ecdar.controllers; -import com.google.gson.JsonObject; import ecdar.Ecdar; import ecdar.abstractions.*; import ecdar.backend.SimulationHandler; @@ -14,7 +13,6 @@ import javafx.beans.property.SimpleObjectProperty; import javafx.fxml.Initializable; import javafx.scene.layout.StackPane; -import org.apache.commons.lang3.SerializationUtils; import java.net.URL; import java.util.ArrayList; From f04ec3a98ad6cb14011292943ab0fc1d6e32ba39 Mon Sep 17 00:00:00 2001 From: WassawRoki <56611129+WassawRoki@users.noreply.github.com> Date: Mon, 21 Nov 2022 14:28:19 +0100 Subject: [PATCH 102/120] It works --- .../controllers/SimLocationController.java | 43 ++++++++++++++++++- .../SimLocationPresentation.java | 12 ++++-- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/main/java/ecdar/controllers/SimLocationController.java b/src/main/java/ecdar/controllers/SimLocationController.java index c8c39522..c8a0d467 100755 --- a/src/main/java/ecdar/controllers/SimLocationController.java +++ b/src/main/java/ecdar/controllers/SimLocationController.java @@ -1,7 +1,9 @@ package ecdar.controllers; -import ecdar.abstractions.Component; -import ecdar.abstractions.Location; +import com.jfoenix.controls.JFXPopup; +import ecdar.abstractions.*; +import ecdar.backend.BackendHelper; +import ecdar.presentations.DropDownMenu; import ecdar.presentations.SimLocationPresentation; import ecdar.presentations.SimTagPresentation; import ecdar.utility.colors.Color; @@ -14,6 +16,9 @@ import javafx.scene.shape.Circle; import javafx.scene.shape.Line; import javafx.scene.shape.Path; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import java.util.function.Consumer; import java.net.URL; import java.util.ResourceBundle; @@ -36,6 +41,7 @@ public class SimLocationController implements Initializable { public Label idLabel; public Line nameTagLine; public Line invariantTagLine; + private DropDownMenu dropDownMenu; @Override public void initialize(final URL location, final ResourceBundle resources) { @@ -49,8 +55,41 @@ public void initialize(final URL location, final ResourceBundle resources) { // Scale x and y 1:1 (based on the x-scale) scaleContent.scaleYProperty().bind(scaleContent.scaleXProperty()); + initializeMouseControls(); } + private void initializeMouseControls() { + final Consumer mouseClicked = (event) -> { + if (root.isPlaced()) { + if (event.getButton().equals(MouseButton.SECONDARY)) { + initializeDropDownMenu(); + dropDownMenu.show(JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, 0, 0); + } + } + + }; + locationProperty().addListener((obs, oldLocation, newLocation) -> { + if(newLocation == null) return; + root.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseClicked::accept); + }); + } + public void initializeDropDownMenu(){ + dropDownMenu = new DropDownMenu(root); + + dropDownMenu.addClickableListElement("Is " + getLocation().getId() + " reachable?", event -> { + dropDownMenu.hide(); + // Generate the query from the backend + final String reachabilityQuery = BackendHelper.getLocationReachableQuery(getLocation(), getComponent()); + + // Add proper comment + final String reachabilityComment = "Is " + getLocation().getMostDescriptiveIdentifier() + " reachable?"; + + // Add new query for this location + final Query query = new Query(reachabilityQuery, reachabilityComment, QueryState.UNKNOWN); + query.setType(QueryType.REACHABILITY); + dropDownMenu.hide(); + }); + } public Location getLocation() { return location.get(); } diff --git a/src/main/java/ecdar/presentations/SimLocationPresentation.java b/src/main/java/ecdar/presentations/SimLocationPresentation.java index e250433a..c4198ba4 100755 --- a/src/main/java/ecdar/presentations/SimLocationPresentation.java +++ b/src/main/java/ecdar/presentations/SimLocationPresentation.java @@ -9,9 +9,7 @@ import ecdar.utility.helpers.SelectHelper; import javafx.animation.*; import javafx.beans.binding.DoubleBinding; -import javafx.beans.property.DoubleProperty; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.property.*; import javafx.scene.Group; import javafx.scene.control.Label; import javafx.scene.effect.DropShadow; @@ -46,6 +44,7 @@ public class SimLocationPresentation extends Group implements Highlightable { private final List> updateColorDelegates = new ArrayList<>(); private final DoubleProperty animation = new SimpleDoubleProperty(0); private final DoubleBinding reverseAnimation = new SimpleDoubleProperty(1).subtract(animation); + private BooleanProperty isPlaced = new SimpleBooleanProperty(true); /** * Constructs a Simulator Location ready to be placed on the view @@ -272,6 +271,13 @@ protected void interpolate(final double frac) { color.addListener((obs, old, newColor) -> updateColor.accept(newColor, colorIntensity.get())); } + public Boolean isPlaced(){ + return isPlaced.get(); + } + + public void setPlaced(boolean placed){ + isPlaced.set(placed); + } private void initializeTypeGraphics() { final Location location = controller.getLocation(); From ea36ec83109df2bf78ff4d7c6c296fecadd9b6d4 Mon Sep 17 00:00:00 2001 From: Sigurd00 Date: Mon, 21 Nov 2022 14:39:46 +0100 Subject: [PATCH 103/120] Change build.gradle file. Surely it works now (#66) --- build.gradle | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/build.gradle b/build.gradle index 6051ef81..c7664e51 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,20 @@ javafx { modules = ['javafx.controls', 'javafx.fxml', 'javafx.swing'] } +run { + mainClassName = 'com.jfxbase/com.jfxbase.sample.Main' + applicationDefaultJvmArgs += [ + "--add-opens", "javafx.graphics/javafx.css=ALL-UNNAMED", + "--add-opens", "javafx.base/com.sun.javafx.runtime=ALL-UNNAMED", + "--add-opens", "javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED", + "--add-opens", "javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED", + "--add-opens", "javafx.base/com.sun.javafx.binding=ALL-UNNAMED", + "--add-opens", "javafx.base/com.sun.javafx.event=ALL-UNNAMED", + "--add-opens", "javafx.graphics/com.sun.javafx.stage=ALL-UNNAMED", + "--add-opens", "javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED", + ] +} + group 'ecdar' if (project.hasProperty('ecdarVersion')) { version = project.ecdarVersion From 2ce8dca2b4c34181cef1287ab8110b4a4c5b27dc Mon Sep 17 00:00:00 2001 From: Emilie Steinmann Date: Wed, 23 Nov 2022 12:11:22 +0100 Subject: [PATCH 104/120] =?UTF-8?q?reachability=20check=20p=C3=A5=20=C3=A9?= =?UTF-8?q?n=20location=20i=20sim=20med=20flere=20components.=20mangler=20?= =?UTF-8?q?clocks=20og=20anden=20startstate=20end=20initial.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ecdar/backend/BackendHelper.java | 40 +++++++++++++++++-- .../ecdar/controllers/LocationController.java | 18 --------- .../controllers/SimLocationController.java | 6 ++- 3 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/main/java/ecdar/backend/BackendHelper.java b/src/main/java/ecdar/backend/BackendHelper.java index b93756b5..57c72a99 100644 --- a/src/main/java/ecdar/backend/BackendHelper.java +++ b/src/main/java/ecdar/backend/BackendHelper.java @@ -19,6 +19,8 @@ import java.util.List; import java.util.Optional; +import static ecdar.controllers.SimulationInitializationDialogController.ListOfComponents; + public final class BackendHelper { final static String TEMP_DIRECTORY = "temporary"; private static BackendInstance defaultBackend = null; @@ -67,12 +69,42 @@ public static void stopQueries() { /** * Generates a reachability query based on the given location and component * - * @param location The location which should be checked for reachability - * @param component The component where the location belong to / are placed + * @param endLocation The location which should be checked for reachability * @return A reachability query string */ - public static String getLocationReachableQuery(final Location location, final Component component) { - return component.getName() + "." + location.getId(); + public static String getLocationReachableQuery(final Location endLocation, final Component component) { + var stringBuilder = new StringBuilder(); + + for (var componentName:ListOfComponents) { + stringBuilder.append(componentName); + stringBuilder.append(" || "); + } + stringBuilder.delete(stringBuilder.length()-3, stringBuilder.length()); + + // append start location here TODO + + + // append end state + var indexOfSelectedComponent = ListOfComponents.indexOf(component.getName()); + stringBuilder.append("-> ["); + // add underscore to indicate, that we don't care about the end locations in the other components + var numberOfComponents = ListOfComponents.size(); + for (int i = 0; i < numberOfComponents; i++){ + if (i == indexOfSelectedComponent){ + stringBuilder.append(endLocation.getId() + ", "); + } + else{ + stringBuilder.append("_, "); + } + } + stringBuilder.delete(stringBuilder.length()-2, stringBuilder.length()); + stringBuilder.append("]("); + + // append clock here TODO + stringBuilder.append(")"); + + // return example: m1 || M2 -> [L1, L4](y<3); [L2, L7](y<2) + return stringBuilder.toString(); } /** diff --git a/src/main/java/ecdar/controllers/LocationController.java b/src/main/java/ecdar/controllers/LocationController.java index 4d442513..2b887f9e 100644 --- a/src/main/java/ecdar/controllers/LocationController.java +++ b/src/main/java/ecdar/controllers/LocationController.java @@ -193,24 +193,6 @@ public void initializeDropDownMenu() { dropDownMenu.addSpacerElement(); - dropDownMenu.addClickableListElement("Is " + getLocation().getId() + " reachable?", event -> { - dropDownMenu.hide(); - // Generate the query from the backend - final String reachabilityQuery = BackendHelper.getLocationReachableQuery(getLocation(), getComponent()); - - // Add proper comment - final String reachabilityComment = "Is " + getLocation().getMostDescriptiveIdentifier() + " reachable?"; - - // Add new query for this location - final Query query = new Query(reachabilityQuery, reachabilityComment, QueryState.UNKNOWN); - query.setType(QueryType.REACHABILITY); - Ecdar.getProject().getQueries().add(query); - Ecdar.getQueryExecutor().executeQuery(query); - dropDownMenu.hide(); - }); - - dropDownMenu.addSpacerElement(); - dropDownMenu.addColorPicker(getLocation(), (color, intensity) -> { getLocation().setColorIntensity(intensity); getLocation().setColor(color); diff --git a/src/main/java/ecdar/controllers/SimLocationController.java b/src/main/java/ecdar/controllers/SimLocationController.java index c8a0d467..f3613cb4 100755 --- a/src/main/java/ecdar/controllers/SimLocationController.java +++ b/src/main/java/ecdar/controllers/SimLocationController.java @@ -1,6 +1,7 @@ package ecdar.controllers; import com.jfoenix.controls.JFXPopup; +import ecdar.Ecdar; import ecdar.abstractions.*; import ecdar.backend.BackendHelper; import ecdar.presentations.DropDownMenu; @@ -77,7 +78,6 @@ public void initializeDropDownMenu(){ dropDownMenu = new DropDownMenu(root); dropDownMenu.addClickableListElement("Is " + getLocation().getId() + " reachable?", event -> { - dropDownMenu.hide(); // Generate the query from the backend final String reachabilityQuery = BackendHelper.getLocationReachableQuery(getLocation(), getComponent()); @@ -87,6 +87,10 @@ public void initializeDropDownMenu(){ // Add new query for this location final Query query = new Query(reachabilityQuery, reachabilityComment, QueryState.UNKNOWN); query.setType(QueryType.REACHABILITY); + + // execute query + Ecdar.getQueryExecutor().executeQuery(query); + dropDownMenu.hide(); }); } From 507c57ee7539384cae61399a7904d6b3c639560f Mon Sep 17 00:00:00 2001 From: jhbengtsson Date: Thu, 24 Nov 2022 10:15:05 +0100 Subject: [PATCH 105/120] showtoast based in reachability response status --- src/main/java/ecdar/backend/QueryHandler.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/ecdar/backend/QueryHandler.java b/src/main/java/ecdar/backend/QueryHandler.java index c068c2f7..b4546bbe 100644 --- a/src/main/java/ecdar/backend/QueryHandler.java +++ b/src/main/java/ecdar/backend/QueryHandler.java @@ -162,9 +162,11 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { case REACHABILITY: if (queryOk.getReachability().getSuccess()) { query.setQueryState(QueryState.SUCCESSFUL); + Ecdar.showToast("Reachability check was successful."); query.getSuccessConsumer().accept(true); } else { query.setQueryState(QueryState.ERROR); + Ecdar.showToast("Reachability check was unsuccessful!"); query.getFailureConsumer().accept(new BackendException.QueryErrorException(queryOk.getReachability().getReason())); query.getSuccessConsumer().accept(false); //ToDo: These errors are not implemented in the Reveaal backend. From 369074b29d8ddf93a67da604c2b65702f5d237d3 Mon Sep 17 00:00:00 2001 From: Emilie Steinmann Date: Thu, 24 Nov 2022 12:37:41 +0100 Subject: [PATCH 106/120] test and refactoring of getLocationReachableQuery, and some minor changes --- .../java/ecdar/backend/BackendHelper.java | 69 +++++++--- .../ecdar/simulation/ReachabilityTest.java | 119 ++++++++++++++++++ 2 files changed, 172 insertions(+), 16 deletions(-) create mode 100644 src/test/java/ecdar/simulation/ReachabilityTest.java diff --git a/src/main/java/ecdar/backend/BackendHelper.java b/src/main/java/ecdar/backend/BackendHelper.java index 57c72a99..e3a986ef 100644 --- a/src/main/java/ecdar/backend/BackendHelper.java +++ b/src/main/java/ecdar/backend/BackendHelper.java @@ -75,35 +75,72 @@ public static void stopQueries() { public static String getLocationReachableQuery(final Location endLocation, final Component component) { var stringBuilder = new StringBuilder(); - for (var componentName:ListOfComponents) { - stringBuilder.append(componentName); - stringBuilder.append(" || "); - } - stringBuilder.delete(stringBuilder.length()-3, stringBuilder.length()); + // append simulation query (currently only supports parallel composition) + stringBuilder.append(getSimulationQueryString()); // append start location here TODO - // append end state - var indexOfSelectedComponent = ListOfComponents.indexOf(component.getName()); - stringBuilder.append("-> ["); + stringBuilder.append(getEndStateString(component.getName(), endLocation.getId())); + + // append clocks + stringBuilder.append("("); + // append clock here TODO + stringBuilder.append(")"); + + // return example: m1||M2->[L1,L4](y<3);[L2, L7](y<2) + return stringBuilder.toString(); + } + + private static String getSimulationQueryString() { + var stringBuilder = new StringBuilder(); + + var appendComponentWithSeparator = false; + for (var componentName:ListOfComponents) { + if (appendComponentWithSeparator){ + stringBuilder.append("||" + componentName); + } + else { + stringBuilder.append(componentName); + } + if (!appendComponentWithSeparator) { + appendComponentWithSeparator = true; + } + } + return stringBuilder.toString(); + } + + private static String getEndStateString(String componentName, String endLocationId) { + var stringBuilder = new StringBuilder(); + + var indexOfSelectedComponent = ListOfComponents.indexOf(componentName); + stringBuilder.append(" -> ["); // add underscore to indicate, that we don't care about the end locations in the other components var numberOfComponents = ListOfComponents.size(); + var appendLocationWithSeparator = false; for (int i = 0; i < numberOfComponents; i++){ if (i == indexOfSelectedComponent){ - stringBuilder.append(endLocation.getId() + ", "); + if (appendLocationWithSeparator){ + stringBuilder.append("," + endLocationId); + } + else{ + stringBuilder.append(endLocationId); + } } else{ - stringBuilder.append("_, "); + if (appendLocationWithSeparator){ + stringBuilder.append(",_"); + } + else{ + stringBuilder.append("_"); + } + } + if (!appendLocationWithSeparator) { + appendLocationWithSeparator = true; } } - stringBuilder.delete(stringBuilder.length()-2, stringBuilder.length()); - stringBuilder.append("]("); - - // append clock here TODO - stringBuilder.append(")"); + stringBuilder.append("]"); - // return example: m1 || M2 -> [L1, L4](y<3); [L2, L7](y<2) return stringBuilder.toString(); } diff --git a/src/test/java/ecdar/simulation/ReachabilityTest.java b/src/test/java/ecdar/simulation/ReachabilityTest.java new file mode 100644 index 00000000..480ecb49 --- /dev/null +++ b/src/test/java/ecdar/simulation/ReachabilityTest.java @@ -0,0 +1,119 @@ +package ecdar.simulation; + +import ecdar.Ecdar; +import ecdar.abstractions.Component; +import ecdar.abstractions.Location; +import ecdar.backend.BackendHelper; +import ecdar.controllers.SimulationInitializationDialogController; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.regex.Pattern; + +import static org.junit.jupiter.api.Assertions.*; + +public class ReachabilityTest { + + @BeforeAll + static void setup() { + Ecdar.setUpForTest(); + } + + @Test + void reachabilityQuerySyntaxTestSuccess() { + var regex = "([a-zA-Z]\\w*)([|][|][a-zA-Z]\\w*)*\\s+\\->\\s+\\[(\\w*)(,(\\w)*)*\\]\\([a-zA-Z0-9_<>=]*\\)(;\\[(\\w*)(,(\\w)*)\\]\\([a-zA-Z0-9_<>=]*\\))*"; + + var location = new Location(); + location.setId("L1"); + var component = new Component(); + component.setName("C1"); + + SimulationInitializationDialogController.ListOfComponents.clear(); + SimulationInitializationDialogController.ListOfComponents.add("C1"); + SimulationInitializationDialogController.ListOfComponents.add("C2"); + SimulationInitializationDialogController.ListOfComponents.add("C3"); + + var result = BackendHelper.getLocationReachableQuery(location, component); + assertTrue(result.matches(regex)); + } + + @Test + void reachabilityQueryLocationPosition1TestSuccess() { + var location = new Location(); + location.setId("L1"); + var component = new Component(); + component.setName("C1"); + + SimulationInitializationDialogController.ListOfComponents.clear(); + SimulationInitializationDialogController.ListOfComponents.add("C1"); + SimulationInitializationDialogController.ListOfComponents.add("C2"); + SimulationInitializationDialogController.ListOfComponents.add("C3"); + + var result = BackendHelper.getLocationReachableQuery(location, component); + var indexOfComponent = result.indexOf('[') + 1; + var output = result.charAt(indexOfComponent); + assertEquals(output, location.getId().charAt(0)); + } + + @Test + void reachabilityQueryLocationPosition2TestSuccess() { + var location = new Location(); + location.setId("L1"); + var component = new Component(); + component.setName("C1"); + + SimulationInitializationDialogController.ListOfComponents.clear(); + SimulationInitializationDialogController.ListOfComponents.add("C2"); + SimulationInitializationDialogController.ListOfComponents.add("C1"); + SimulationInitializationDialogController.ListOfComponents.add("C3"); + + var result = BackendHelper.getLocationReachableQuery(location, component); + var indexOfComponent = result.indexOf(',') + 1; + var output = result.charAt(indexOfComponent); + assertEquals(output, location.getId().charAt(0)); + } + + @Test + void reachabilityQueryLocationPosition3TestSuccess() { + var location = new Location(); + location.setId("L1"); + var component = new Component(); + component.setName("C1"); + + SimulationInitializationDialogController.ListOfComponents.clear(); + SimulationInitializationDialogController.ListOfComponents.add("C2"); + SimulationInitializationDialogController.ListOfComponents.add("C3"); + SimulationInitializationDialogController.ListOfComponents.add("C1"); + + var query = BackendHelper.getLocationReachableQuery(location, component); + var indexOfComponent = query.indexOf(']') - 2; + var output = query.charAt(indexOfComponent); + assertEquals(output, location.getId().charAt(0)); + } + + @Test + void reachabilityQueryNumberOfLocationsTestSuccess() { + var location = new Location(); + location.setId("L1"); + var component = new Component(); + component.setName("C1"); + + SimulationInitializationDialogController.ListOfComponents.clear(); + SimulationInitializationDialogController.ListOfComponents.add("C2"); + SimulationInitializationDialogController.ListOfComponents.add("C1"); + SimulationInitializationDialogController.ListOfComponents.add("C3"); + SimulationInitializationDialogController.ListOfComponents.add("C4"); + + var query = BackendHelper.getLocationReachableQuery(location, component); + int underscoreCount = 0; + for (int i = 0; i < query.length(); i++) { + if (query.charAt(i) == '_') { + underscoreCount++; + } + } + + assertEquals(SimulationInitializationDialogController.ListOfComponents.size(), underscoreCount + 1); + } +} From d1146094c794b7ffb625551d7e72f218545ef19b Mon Sep 17 00:00:00 2001 From: jhbengtsson Date: Thu, 24 Nov 2022 13:09:52 +0100 Subject: [PATCH 107/120] showtoast messages changed --- src/main/java/ecdar/backend/QueryHandler.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/ecdar/backend/QueryHandler.java b/src/main/java/ecdar/backend/QueryHandler.java index b4546bbe..e5ae7dad 100644 --- a/src/main/java/ecdar/backend/QueryHandler.java +++ b/src/main/java/ecdar/backend/QueryHandler.java @@ -7,6 +7,7 @@ import ecdar.abstractions.Component; import ecdar.abstractions.Query; import ecdar.abstractions.QueryState; +import ecdar.abstractions.QueryType; import ecdar.controllers.EcdarController; import ecdar.utility.UndoRedoStack; import ecdar.utility.helpers.StringValidator; @@ -162,7 +163,12 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { case REACHABILITY: if (queryOk.getReachability().getSuccess()) { query.setQueryState(QueryState.SUCCESSFUL); - Ecdar.showToast("Reachability check was successful."); + if(value.toString().contains("true")){ + Ecdar.showToast("Reachability check was successful and the location can be reached."); + } + else if(value.toString().contains("false")){ + Ecdar.showToast("Reachability check was successful but the location cannot be reached."); + } query.getSuccessConsumer().accept(true); } else { query.setQueryState(QueryState.ERROR); @@ -212,6 +218,11 @@ private void handleQueryBackendError(Throwable t, Query query) { // If the query has been cancelled, ignore the error if (query.getQueryState() == QueryState.UNKNOWN) return; + // due to lack of information from backend if the reachability check shows that a location can NOT be reached, this is the most accurate information we can provide + if(query.getType() == QueryType.REACHABILITY){ + Ecdar.showToast("The reachability query failed. This might be due to the fact that the location is not reachable."); + } + // Each error starts with a capitalized description of the error equal to the gRPC error type encountered String errorType = t.getMessage().split(":\\s+", 2)[0]; From 2f5ddc0315acb9a7e498c3ffb6adf8eeae86afa0 Mon Sep 17 00:00:00 2001 From: Dolmer1 Date: Thu, 24 Nov 2022 13:39:36 +0100 Subject: [PATCH 108/120] renamed variable --- src/test/java/ecdar/simulation/ReachabilityTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/java/ecdar/simulation/ReachabilityTest.java b/src/test/java/ecdar/simulation/ReachabilityTest.java index 480ecb49..527cc2b8 100644 --- a/src/test/java/ecdar/simulation/ReachabilityTest.java +++ b/src/test/java/ecdar/simulation/ReachabilityTest.java @@ -52,8 +52,8 @@ void reachabilityQueryLocationPosition1TestSuccess() { SimulationInitializationDialogController.ListOfComponents.add("C3"); var result = BackendHelper.getLocationReachableQuery(location, component); - var indexOfComponent = result.indexOf('[') + 1; - var output = result.charAt(indexOfComponent); + var indexOfLocation = result.indexOf('[') + 1; + var output = result.charAt(indexOfLocation); assertEquals(output, location.getId().charAt(0)); } @@ -70,8 +70,8 @@ void reachabilityQueryLocationPosition2TestSuccess() { SimulationInitializationDialogController.ListOfComponents.add("C3"); var result = BackendHelper.getLocationReachableQuery(location, component); - var indexOfComponent = result.indexOf(',') + 1; - var output = result.charAt(indexOfComponent); + var indexOfLocation = result.indexOf(',') + 1; + var output = result.charAt(indexOfLocation); assertEquals(output, location.getId().charAt(0)); } @@ -88,8 +88,8 @@ void reachabilityQueryLocationPosition3TestSuccess() { SimulationInitializationDialogController.ListOfComponents.add("C1"); var query = BackendHelper.getLocationReachableQuery(location, component); - var indexOfComponent = query.indexOf(']') - 2; - var output = query.charAt(indexOfComponent); + var indexOfLocation = query.indexOf(']') - 2; + var output = query.charAt(indexOfLocation); assertEquals(output, location.getId().charAt(0)); } From 02b447e4f94def0a68066689446bc656b5b5f156 Mon Sep 17 00:00:00 2001 From: Emilie Steinmann Date: Fri, 25 Nov 2022 09:24:46 +0100 Subject: [PATCH 109/120] Improve readability --- src/main/java/ecdar/backend/BackendHelper.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/ecdar/backend/BackendHelper.java b/src/main/java/ecdar/backend/BackendHelper.java index e3a986ef..bcc932eb 100644 --- a/src/main/java/ecdar/backend/BackendHelper.java +++ b/src/main/java/ecdar/backend/BackendHelper.java @@ -113,13 +113,12 @@ private static String getSimulationQueryString() { private static String getEndStateString(String componentName, String endLocationId) { var stringBuilder = new StringBuilder(); - var indexOfSelectedComponent = ListOfComponents.indexOf(componentName); stringBuilder.append(" -> ["); - // add underscore to indicate, that we don't care about the end locations in the other components - var numberOfComponents = ListOfComponents.size(); var appendLocationWithSeparator = false; - for (int i = 0; i < numberOfComponents; i++){ - if (i == indexOfSelectedComponent){ + + for (var component:ListOfComponents) + { + if (component.equals(componentName)){ if (appendLocationWithSeparator){ stringBuilder.append("," + endLocationId); } @@ -127,7 +126,7 @@ private static String getEndStateString(String componentName, String endLocation stringBuilder.append(endLocationId); } } - else{ + else{ // add underscore to indicate, that we don't care about the end locations in the other components if (appendLocationWithSeparator){ stringBuilder.append(",_"); } From 7a169bfc8799bb20e6e30d56952b781b65ca10e1 Mon Sep 17 00:00:00 2001 From: jhbengtsson Date: Fri, 25 Nov 2022 10:54:34 +0100 Subject: [PATCH 110/120] =?UTF-8?q?=C3=A6ndret=20s=C3=A5=20vi=20ikke=20kon?= =?UTF-8?q?verterer=20til=20string=20men=20bruger=20metoden=20til=20at=20s?= =?UTF-8?q?e=20success?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/ecdar/backend/QueryHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ecdar/backend/QueryHandler.java b/src/main/java/ecdar/backend/QueryHandler.java index e5ae7dad..a2f6162e 100644 --- a/src/main/java/ecdar/backend/QueryHandler.java +++ b/src/main/java/ecdar/backend/QueryHandler.java @@ -163,10 +163,10 @@ private void handleQueryResponse(QueryProtos.QueryResponse value, Query query) { case REACHABILITY: if (queryOk.getReachability().getSuccess()) { query.setQueryState(QueryState.SUCCESSFUL); - if(value.toString().contains("true")){ + if(value.getQueryOk().getReachability().getSuccess()){ Ecdar.showToast("Reachability check was successful and the location can be reached."); } - else if(value.toString().contains("false")){ + else if(!value.getQueryOk().getReachability().getSuccess()){ Ecdar.showToast("Reachability check was successful but the location cannot be reached."); } query.getSuccessConsumer().accept(true); From 2294096a153417a9ba9c55aa7ff9fa83a866be2e Mon Sep 17 00:00:00 2001 From: jhbengtsson Date: Fri, 25 Nov 2022 11:02:48 +0100 Subject: [PATCH 111/120] docs --- src/main/java/ecdar/controllers/SimLocationController.java | 5 +++++ .../java/ecdar/presentations/SimLocationPresentation.java | 1 + 2 files changed, 6 insertions(+) diff --git a/src/main/java/ecdar/controllers/SimLocationController.java b/src/main/java/ecdar/controllers/SimLocationController.java index f3613cb4..f7f1e7d5 100755 --- a/src/main/java/ecdar/controllers/SimLocationController.java +++ b/src/main/java/ecdar/controllers/SimLocationController.java @@ -74,6 +74,11 @@ private void initializeMouseControls() { root.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseClicked::accept); }); } + + /** + * Creates the dropdown when right clicking a location. + * When reachability is chosen a request will be send to the backend to see if the location can be reached + */ public void initializeDropDownMenu(){ dropDownMenu = new DropDownMenu(root); diff --git a/src/main/java/ecdar/presentations/SimLocationPresentation.java b/src/main/java/ecdar/presentations/SimLocationPresentation.java index c4198ba4..335cdc2c 100755 --- a/src/main/java/ecdar/presentations/SimLocationPresentation.java +++ b/src/main/java/ecdar/presentations/SimLocationPresentation.java @@ -278,6 +278,7 @@ public Boolean isPlaced(){ public void setPlaced(boolean placed){ isPlaced.set(placed); } + private void initializeTypeGraphics() { final Location location = controller.getLocation(); From a12a403fd208cc8467d41a9adeeb8cc5d2fd7437 Mon Sep 17 00:00:00 2001 From: jhbengtsson Date: Fri, 25 Nov 2022 11:15:53 +0100 Subject: [PATCH 112/120] docs --- .../java/ecdar/presentations/SimLocationPresentation.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/ecdar/presentations/SimLocationPresentation.java b/src/main/java/ecdar/presentations/SimLocationPresentation.java index 335cdc2c..082a9bb3 100755 --- a/src/main/java/ecdar/presentations/SimLocationPresentation.java +++ b/src/main/java/ecdar/presentations/SimLocationPresentation.java @@ -271,10 +271,16 @@ protected void interpolate(final double frac) { color.addListener((obs, old, newColor) -> updateColor.accept(newColor, colorIntensity.get())); } + /** + * Returns true if the mouse hovers over a location + * */ public Boolean isPlaced(){ return isPlaced.get(); } + /** + * Set placed to true or false + * */ public void setPlaced(boolean placed){ isPlaced.set(placed); } From e57fbf7bbabf98c4c96e05514bf5e18ff047a435 Mon Sep 17 00:00:00 2001 From: EmilieSonne <82802471+EmilieSonne@users.noreply.github.com> Date: Wed, 30 Nov 2022 13:51:34 +0100 Subject: [PATCH 113/120] Reachability: send request with startSimulation query as input (#104) * Save simulation query and use it for reachability check * remove unnecessary code * update tests to be independent of simulation start query format --- .../java/ecdar/backend/BackendHelper.java | 24 +++---------------- .../ecdar/controllers/EcdarController.java | 2 +- .../controllers/SimLocationController.java | 2 +- ...ulationInitializationDialogController.java | 13 ++++++---- .../controllers/SimulatorController.java | 12 ++++++++-- ...ationInitializationDialogPresentation.fxml | 2 +- .../ecdar/simulation/ReachabilityTest.java | 12 +++++----- 7 files changed, 30 insertions(+), 37 deletions(-) diff --git a/src/main/java/ecdar/backend/BackendHelper.java b/src/main/java/ecdar/backend/BackendHelper.java index bcc932eb..0397b6ee 100644 --- a/src/main/java/ecdar/backend/BackendHelper.java +++ b/src/main/java/ecdar/backend/BackendHelper.java @@ -72,11 +72,11 @@ public static void stopQueries() { * @param endLocation The location which should be checked for reachability * @return A reachability query string */ - public static String getLocationReachableQuery(final Location endLocation, final Component component) { + public static String getLocationReachableQuery(final Location endLocation, final Component component, final String query) { var stringBuilder = new StringBuilder(); - // append simulation query (currently only supports parallel composition) - stringBuilder.append(getSimulationQueryString()); + // append simulation query + stringBuilder.append(query); // append start location here TODO @@ -92,24 +92,6 @@ public static String getLocationReachableQuery(final Location endLocation, final return stringBuilder.toString(); } - private static String getSimulationQueryString() { - var stringBuilder = new StringBuilder(); - - var appendComponentWithSeparator = false; - for (var componentName:ListOfComponents) { - if (appendComponentWithSeparator){ - stringBuilder.append("||" + componentName); - } - else { - stringBuilder.append(componentName); - } - if (!appendComponentWithSeparator) { - appendComponentWithSeparator = true; - } - } - return stringBuilder.toString(); - } - private static String getEndStateString(String componentName, String endLocationId) { var stringBuilder = new StringBuilder(); diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index 75ff103d..a7b7c558 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -455,7 +455,7 @@ private void startBackgroundQueriesThread() { component.getLocations().forEach(location -> location.setReachability(Location.Reachability.EXCLUDED)); } else { component.getLocations().forEach(location -> { - final String locationReachableQuery = BackendHelper.getLocationReachableQuery(location, component); + final String locationReachableQuery = BackendHelper.getLocationReachableQuery(location, component, SimulatorController.getSimulationQuery()); Query reachabilityQuery = new Query(locationReachableQuery, "", QueryState.UNKNOWN); reachabilityQuery.setType(QueryType.REACHABILITY); diff --git a/src/main/java/ecdar/controllers/SimLocationController.java b/src/main/java/ecdar/controllers/SimLocationController.java index f7f1e7d5..d5112fc4 100755 --- a/src/main/java/ecdar/controllers/SimLocationController.java +++ b/src/main/java/ecdar/controllers/SimLocationController.java @@ -84,7 +84,7 @@ public void initializeDropDownMenu(){ dropDownMenu.addClickableListElement("Is " + getLocation().getId() + " reachable?", event -> { // Generate the query from the backend - final String reachabilityQuery = BackendHelper.getLocationReachableQuery(getLocation(), getComponent()); + final String reachabilityQuery = BackendHelper.getLocationReachableQuery(getLocation(), getComponent(), SimulatorController.getSimulationQuery()); // Add proper comment final String reachabilityComment = "Is " + getLocation().getMostDescriptiveIdentifier() + " reachable?"; diff --git a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java index 52442964..a34787a6 100644 --- a/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java +++ b/src/main/java/ecdar/controllers/SimulationInitializationDialogController.java @@ -19,12 +19,15 @@ public class SimulationInitializationDialogController implements Initializable { * Function gets list of components to simulation * and saves it in the public static ListOfComponents */ - public void SetListOfComponentsToSimulate(){ + public void setSimulationData(){ + // set simulation query + SimulatorController.setSimulationQuery(simulationComboBox.getSelectionModel().getSelectedItem()); + + // set list of components involved in simulation ListOfComponents.clear(); - String componentsToSimulate = simulationComboBox.getSelectionModel().getSelectedItem(); - //filters out all components by ignoring operators. + // pattern filters out all components by ignoring operators. Pattern pattern = Pattern.compile("([\\w]*)", Pattern.CASE_INSENSITIVE); - Matcher matcher = pattern.matcher(componentsToSimulate); + Matcher matcher = pattern.matcher(SimulatorController.getSimulationQuery()); List listOfComponentsToSimulate = new ArrayList<>(); //Adds all found components to list. while(matcher.find()){ @@ -32,9 +35,9 @@ public void SetListOfComponentsToSimulate(){ listOfComponentsToSimulate.add(matcher.group()); } } - ListOfComponents = listOfComponentsToSimulate; } + public void initialize(URL location, ResourceBundle resources) { } diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index 36539552..63d82474 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -21,6 +21,7 @@ import java.util.ResourceBundle; public class SimulatorController implements Initializable { + private static String simulationQuery; public StackPane root; public SimulatorOverviewPresentation overviewPresentation; public StackPane toolbar; @@ -83,7 +84,7 @@ private void resetSimulation() { //Method that colors all initial states. initialstatelighter(listOfComponentsForSimulation); } - + private void initialstatelighter(List listofComponents){ for(Component comp: listofComponents) { @@ -108,7 +109,7 @@ private void initialstatelighter(List listofComponents){ private List findComponentsInCurrentSimulation(List queryComponents) { //Show components from the system List components = new ArrayList<>(); - + components = Ecdar.getProject().getComponents(); //Matches query components against with existing components and adds them to simulation @@ -166,4 +167,11 @@ public static ObjectProperty getSelectedStateProperty() { public static void setSelectedState(SimulationState selectedState) { SimulatorController.selectedState.set(selectedState); } + public static void setSimulationQuery(String query) { + simulationQuery = query; + } + + public static String getSimulationQuery(){ + return simulationQuery; + } } diff --git a/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml b/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml index 8fa6635b..eb97bdd1 100644 --- a/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml +++ b/src/main/resources/ecdar/presentations/SimulationInitializationDialogPresentation.fxml @@ -36,7 +36,7 @@ - + diff --git a/src/test/java/ecdar/simulation/ReachabilityTest.java b/src/test/java/ecdar/simulation/ReachabilityTest.java index 527cc2b8..6e645470 100644 --- a/src/test/java/ecdar/simulation/ReachabilityTest.java +++ b/src/test/java/ecdar/simulation/ReachabilityTest.java @@ -23,7 +23,7 @@ static void setup() { @Test void reachabilityQuerySyntaxTestSuccess() { - var regex = "([a-zA-Z]\\w*)([|][|][a-zA-Z]\\w*)*\\s+\\->\\s+\\[(\\w*)(,(\\w)*)*\\]\\([a-zA-Z0-9_<>=]*\\)(;\\[(\\w*)(,(\\w)*)\\]\\([a-zA-Z0-9_<>=]*\\))*"; + var regex = "query\\s+\\->\\s+\\[(\\w*)(,(\\w)*)*\\]\\([a-zA-Z0-9_<>=]*\\)(;\\[(\\w*)(,(\\w)*)\\]\\([a-zA-Z0-9_<>=]*\\))*"; var location = new Location(); location.setId("L1"); @@ -35,7 +35,7 @@ void reachabilityQuerySyntaxTestSuccess() { SimulationInitializationDialogController.ListOfComponents.add("C2"); SimulationInitializationDialogController.ListOfComponents.add("C3"); - var result = BackendHelper.getLocationReachableQuery(location, component); + var result = BackendHelper.getLocationReachableQuery(location, component, "query"); assertTrue(result.matches(regex)); } @@ -51,7 +51,7 @@ void reachabilityQueryLocationPosition1TestSuccess() { SimulationInitializationDialogController.ListOfComponents.add("C2"); SimulationInitializationDialogController.ListOfComponents.add("C3"); - var result = BackendHelper.getLocationReachableQuery(location, component); + var result = BackendHelper.getLocationReachableQuery(location, component, "query"); var indexOfLocation = result.indexOf('[') + 1; var output = result.charAt(indexOfLocation); assertEquals(output, location.getId().charAt(0)); @@ -69,7 +69,7 @@ void reachabilityQueryLocationPosition2TestSuccess() { SimulationInitializationDialogController.ListOfComponents.add("C1"); SimulationInitializationDialogController.ListOfComponents.add("C3"); - var result = BackendHelper.getLocationReachableQuery(location, component); + var result = BackendHelper.getLocationReachableQuery(location, component, "query"); var indexOfLocation = result.indexOf(',') + 1; var output = result.charAt(indexOfLocation); assertEquals(output, location.getId().charAt(0)); @@ -87,7 +87,7 @@ void reachabilityQueryLocationPosition3TestSuccess() { SimulationInitializationDialogController.ListOfComponents.add("C3"); SimulationInitializationDialogController.ListOfComponents.add("C1"); - var query = BackendHelper.getLocationReachableQuery(location, component); + var query = BackendHelper.getLocationReachableQuery(location, component, "query"); var indexOfLocation = query.indexOf(']') - 2; var output = query.charAt(indexOfLocation); assertEquals(output, location.getId().charAt(0)); @@ -106,7 +106,7 @@ void reachabilityQueryNumberOfLocationsTestSuccess() { SimulationInitializationDialogController.ListOfComponents.add("C3"); SimulationInitializationDialogController.ListOfComponents.add("C4"); - var query = BackendHelper.getLocationReachableQuery(location, component); + var query = BackendHelper.getLocationReachableQuery(location, component, "query"); int underscoreCount = 0; for (int i = 0; i < query.length(); i++) { if (query.charAt(i) == '_') { From bb2521a336a40e36a001f8143ca43521a6914a02 Mon Sep 17 00:00:00 2001 From: EmilieSonne <82802471+EmilieSonne@users.noreply.github.com> Date: Wed, 30 Nov 2022 14:52:50 +0100 Subject: [PATCH 114/120] Simulation logic (#81) * sim work + unique edge ids + temporary fix for scaling issues on startup * clean up * fix errors when switching back to editor * mostly refactoring - move highlight listeners to overviewcontroller - save state (to be used for the next steprequest) in SimulationState * reset when user selects another composition * Update SimulationHandler.java * Remove test data and show message to user when no available transitions * move simhandler * clean up + comments * add functionality to reset button * Update Edge.java * Update SimulatorController.java * Remove unused imports * comment + rename method * fixed edge id error when cloning for sim + comments * footer disabled * bingbong * Update EcdarPresentation.fxml * Yeeeet! * Update SimulatorController.java * rename refreshTransitions * Update SimulationState.java * jep * fix for when SpecificComponent is null in sim response * Update TransitionPaneElementPresentation.fxml * only change to hand cursor on available transitions Co-authored-by: APaludan Co-authored-by: WassawRoki <56611129+WassawRoki@users.noreply.github.com> --- .../ecdar/abstractions/DisplayableEdge.java | 9 +- src/main/java/ecdar/abstractions/Edge.java | 13 +- .../java/ecdar/abstractions/GroupedEdge.java | 9 +- .../java/ecdar/backend/SimulationHandler.java | 814 ++++++++---------- .../ecdar/controllers/EcdarController.java | 20 +- .../controllers/MessageTabPaneController.java | 272 ------ .../controllers/SimulatorController.java | 71 +- .../SimulatorOverviewController.java | 63 +- .../TracePaneElementController.java | 16 +- .../TransitionPaneElementController.java | 40 +- .../presentations/LocationPresentation.java | 13 +- .../MessageTabPanePresentation.java | 115 --- .../ecdar/presentations/NailPresentation.java | 9 +- .../presentations/SimEdgePresentation.java | 18 + .../ecdar/simulation/SimulationState.java | 65 +- .../simulation/SimulationStateSuccessor.java | 15 - .../presentations/EcdarPresentation.fxml | 2 - .../MessageTabPanePresentation.fxml | 54 -- .../presentations/SimEdgePresentation.fxml | 3 +- .../TransitionPaneElementPresentation.fxml | 2 +- 20 files changed, 500 insertions(+), 1123 deletions(-) mode change 100755 => 100644 src/main/java/ecdar/backend/SimulationHandler.java delete mode 100644 src/main/java/ecdar/controllers/MessageTabPaneController.java delete mode 100644 src/main/java/ecdar/presentations/MessageTabPanePresentation.java delete mode 100644 src/main/java/ecdar/simulation/SimulationStateSuccessor.java delete mode 100644 src/main/resources/ecdar/presentations/MessageTabPanePresentation.fxml diff --git a/src/main/java/ecdar/abstractions/DisplayableEdge.java b/src/main/java/ecdar/abstractions/DisplayableEdge.java index bb99d5cf..be812729 100644 --- a/src/main/java/ecdar/abstractions/DisplayableEdge.java +++ b/src/main/java/ecdar/abstractions/DisplayableEdge.java @@ -1,6 +1,5 @@ package ecdar.abstractions; -import ecdar.Ecdar; import ecdar.code_analysis.Nearable; import ecdar.presentations.Grid; import ecdar.utility.colors.Color; @@ -12,6 +11,7 @@ import javafx.collections.ObservableList; import java.util.List; +import java.util.UUID; public abstract class DisplayableEdge implements Nearable { private final StringProperty id = new SimpleStringProperty(""); @@ -282,12 +282,7 @@ public String getId() { * Generate and sets a unique id for this location */ protected void setId() { - for(int counter = 0; ; counter++) { - if(!Ecdar.getProject().getEdgeIds().contains(String.valueOf(counter))){ - id.set(Edge.EDGE + counter); - return; - } - } + id.set(UUID.randomUUID().toString()); } /** diff --git a/src/main/java/ecdar/abstractions/Edge.java b/src/main/java/ecdar/abstractions/Edge.java index a0ed8b38..22c5cdbe 100644 --- a/src/main/java/ecdar/abstractions/Edge.java +++ b/src/main/java/ecdar/abstractions/Edge.java @@ -207,10 +207,17 @@ public void deserialize(final JsonObject json, final Component component) { } ioStatus = new SimpleObjectProperty<>(EdgeStatus.valueOf(json.getAsJsonPrimitive(STATUS).getAsString())); - final JsonPrimitive IDJson = json.getAsJsonPrimitive(ID); - if (IDJson != null) setId(IDJson.getAsString()); - else setId(); + /* The new type of edge ids is a UUID, which has a length of 36 characters. + * The if statement checks if the id is a UUID and if not, it creates a new UUID. + * This is necessary because the old type of edge ids were not unique and has to be replaced in old projects. + * It should be possible to simplify this in the future when all projects are updated. + */ + int UUIDLength = 36; + if (IDJson != null && IDJson.getAsString().length() == UUIDLength) + setId(IDJson.getAsString()); + else + setId(); final JsonPrimitive groupJson = json.getAsJsonPrimitive(GROUP); diff --git a/src/main/java/ecdar/abstractions/GroupedEdge.java b/src/main/java/ecdar/abstractions/GroupedEdge.java index f96ab4b7..02503d9a 100644 --- a/src/main/java/ecdar/abstractions/GroupedEdge.java +++ b/src/main/java/ecdar/abstractions/GroupedEdge.java @@ -1,6 +1,5 @@ package ecdar.abstractions; -import ecdar.Ecdar; import javafx.beans.property.*; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; @@ -8,6 +7,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.UUID; public class GroupedEdge extends DisplayableEdge { private final ObservableList edges = FXCollections.observableList(new ArrayList<>()); @@ -90,12 +90,7 @@ public String getId() { * Generate and sets a unique id for this location */ protected void setId() { - for(int counter = 0; ; counter++) { - if(!Ecdar.getProject().getEdgeIds().contains(String.valueOf(counter))){ - id.set(Edge.EDGE_GROUP + counter); - return; - } - } + id.set(UUID.randomUUID().toString()); } /** diff --git a/src/main/java/ecdar/backend/SimulationHandler.java b/src/main/java/ecdar/backend/SimulationHandler.java old mode 100755 new mode 100644 index 4277bf09..b15e96b0 --- a/src/main/java/ecdar/backend/SimulationHandler.java +++ b/src/main/java/ecdar/backend/SimulationHandler.java @@ -1,467 +1,349 @@ -package ecdar.backend; - -import EcdarProtoBuf.ComponentProtos; -import EcdarProtoBuf.QueryProtos; -import ecdar.Ecdar; -import ecdar.abstractions.*; -import ecdar.simulation.SimulationState; -import ecdar.simulation.SimulationStateSuccessor; -import io.grpc.stub.StreamObserver; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.collections.ObservableMap; - -import java.io.IOException; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import EcdarProtoBuf.QueryProtos.SimulationStepResponse; - -/** - * Handles state changes, updates of values / clocks, and keeps track of all the transitions that - * have been taken throughout a simulation. - */ -public class SimulationHandler { - public static final String QUERY_PREFIX = "Query: "; - private String composition; - private ObjectProperty currentConcreteState = new SimpleObjectProperty<>(); - private ObjectProperty initialConcreteState = new SimpleObjectProperty<>(); - private ObjectProperty currentTime = new SimpleObjectProperty<>(); - private BigDecimal delay; - private ArrayList edgesSelected; - private EcdarSystem system; - private SimulationStateSuccessor successor; - private int numberOfSteps; - - private final ObservableMap simulationVariables = FXCollections.observableHashMap(); - private final ObservableMap simulationClocks = FXCollections.observableHashMap(); - /** - * For some reason the successor.getTransitions() only sometimes returns some of the transitions - * that are available, when running the initial step. - * That is why we need to keep track of the initial transitions. - */ - private final ObservableList initialTransitions = FXCollections.observableArrayList(); - public ObservableList traceLog = FXCollections.observableArrayList(); - public ObservableList availableTransitions = FXCollections.observableArrayList(); - private final BackendDriver backendDriver; - private final ArrayList connections = new ArrayList<>(); - - /** - * Empty constructor that should be used if the system or project has not be initialized yet - */ - public SimulationHandler(BackendDriver backendDriver) { - this.backendDriver = backendDriver; - } - - /** - * Initializes the default system (non-query system) - */ - - /** - * Initializes the values and properties in the {@link SimulationHandler}. - * Can also be used as a reset of the simulation. - * THIS METHOD DOES NOT RESET THE ENGINE, - */ - private void initializeSimulation() { - // Initialization - this.delay = new BigDecimal(0); - this.edgesSelected = new ArrayList<>(); - this.numberOfSteps = 0; - this.availableTransitions.clear(); - this.simulationVariables.clear(); - this.simulationClocks.clear(); - this.traceLog.clear(); - this.currentConcreteState.set(getInitialConcreteState()); - this.initialConcreteState.set(getInitialConcreteState()); - this.currentTime = new SimpleObjectProperty<>(BigDecimal.ZERO); - - //Preparation for the simulation - this.system = getSystem(); - //this.currentConcreteState.get().setTime(currentTime.getValue()); - this.initialTransitions.clear(); - this.successor = null; - } - - /** - * Reloads the whole simulation sets the initial transitions, states, etc - */ - public void initialStep() { - initializeSimulation(); - - final SimulationState currentState = currentConcreteState.get(); - successor = getStateSuccessor(); - - GrpcRequest request = new GrpcRequest(backendConnection -> { - StreamObserver responseObserver = new StreamObserver<>() { - @Override - public void onNext(QueryProtos.SimulationStepResponse value) { - System.out.println(value); - } - - @Override - public void onError(Throwable t) { - System.out.println(t.getMessage()); - Ecdar.showToast("Could not start simulation"); - - // Release backend connection - backendDriver.addBackendConnection(backendConnection); - connections.remove(backendConnection); - } - - @Override - public void onCompleted() { - // Release backend connection - backendDriver.addBackendConnection(backendConnection); - connections.remove(backendConnection); - } - }; - - var comInfo = ComponentProtos.ComponentsInfo.newBuilder(); - for (Component c : Ecdar.getProject().getComponents()) { - comInfo.addComponents(ComponentProtos.Component.newBuilder().setJson(c.serialize().toString()).build()); - } - comInfo.setComponentsHash(comInfo.getComponentsList().hashCode()); - var simStartRequest = QueryProtos.SimulationStartRequest.newBuilder(); - var simInfo = QueryProtos.SimulationInfo.newBuilder() - .setComponentComposition(composition) - .setComponentsInfo(comInfo); - simStartRequest.setSimulationInfo(simInfo); - backendConnection.getStub().withDeadlineAfter(this.backendDriver.getResponseDeadline(), TimeUnit.MILLISECONDS) - .startSimulation(simStartRequest.build(), responseObserver); - }, BackendHelper.getDefaultBackendInstance()); - - backendDriver.addRequestToExecutionQueue(request); - - //Save the previous states, and get the new - currentConcreteState.set(successor.getState()); - this.traceLog.add(currentState); - numberOfSteps++; - - //Updates the transitions available - availableTransitions.addAll(FXCollections.observableArrayList(successor.getTransitions())); - initialTransitions.addAll(availableTransitions); - updateAllValues(); - - } - - /** - * Resets the simulation to the initial location - * where the SimulationState is the {@link SimulationHandler#initialConcreteState}, when there are - * elements in the {@link SimulationHandler#traceLog}. Otherwise, it calls {@link SimulationHandler#initialStep} - */ - public void resetToInitialLocation() { - //If the simulation has not begun - if (traceLog.size() == 0) - initialStep(); - else - selectTransitionFromLog(initialConcreteState.get()); - } - - /** - * Resets the simulation to the state after executing the given transition.
- * This method also resets the state, variables, and clocks to the values they had after the given transition. - * This also updates {@link SimulationHandler#availableTransitions} such that - * it displays the available transitions after taking the given transition. - * - * @param transition the transition which the simulation should go back to - */ - public void selectTransitionFromLog(final SimulationState transition) { - final int indexInTrace = traceLog.indexOf(transition); - final SimulationState selectedState; - if (indexInTrace == -1) { - System.out.println("Cannot find transition: " + transition); - Ecdar.showToast("Cannot find transition: " + transition); - return; - } else if (indexInTrace == numberOfSteps - 1) { - return; //you have selected the current system - } else { - selectedState = traceLog.get(indexInTrace); - } - final int sizeOfTraceLog = traceLog.size(); - final int maxRetries = 3; - int numberOfRetries = 0; - edgesSelected = new ArrayList<>(); - //In case that we fail we have to save the time we had before - final BigDecimal tempTime = currentTime.get(); - - currentTime.setValue(new BigDecimal(selectedState.getTime().doubleValue())); - successor.getState().setTime(currentTime.getValue()); - - while (numberOfRetries < maxRetries) { - successor = getStateSuccessor(); - break; - } - currentConcreteState.set(selectedState); - setSimVarAndClocks(); - traceLog.remove(indexInTrace + 1, sizeOfTraceLog); - availableTransitions.clear(); - - // If the user selected the initial/first state in the trace log, we do not trust the engine, - // as it only gives us a subset of the available transitions, in some cases. - if (indexInTrace == 0) availableTransitions.addAll(initialTransitions); - else availableTransitions.addAll(successor.getTransitions()); - - numberOfSteps = indexInTrace + 1; - } - - /** - * Take a step in the simulation. - * - * @param selectedTransitionIndex the index of the availableTransition that you want to take. - * @param delay the time which should pass after the transition. - */ - public void nextStep(final int selectedTransitionIndex, final BigDecimal delay) { - if (selectedTransitionIndex > availableTransitions.size()) { - Ecdar.showToast("The selected transition index: " + selectedTransitionIndex + " is bigger than it should: " + availableTransitions); - return; - } - - final ecdar.simulation.Transition selectedTransition = availableTransitions.get(selectedTransitionIndex); - edgesSelected = new ArrayList<>(); - - //Preparing for the step - for (int i = 0; i < selectedTransition.getEdges().size(); i++) { - edgesSelected.set(i, selectedTransition.getEdges().get(i)); - } - - final int maxRetries = 3; - int numberOfRetries = 0; - - // getConcreteSuccessor may throw a "ProtocolException: Word expected" but in some cases calling the same - // method again does not throw this exception, and actually gives us the expected result. - // This loop calls the method a number of times (maxRetries) - while (numberOfRetries < maxRetries) { - successor = getStateSuccessor(); - // Break from the loop if the method call was a success - break; - } - - //Save the previous states, and get the new - currentConcreteState.set(successor.getState()); - this.traceLog.add(currentConcreteState.get()); - - // increments the number of steps taken during this simulation - numberOfSteps++; - - //Updates the transitions available - availableTransitions.clear(); - availableTransitions.setAll(successor.getTransitions()); - this.delay = delay; - updateAllValues(); - } - - private SimulationStateSuccessor getStateSuccessor() { - // ToDo: Implement - return new SimulationStateSuccessor(); - } - - /** - * An overload of {@link SimulationHandler#nextStep(int, BigDecimal)} where the delay is 0. - * - * @param selectedTransition the index of the availableTransition that you want to take. - */ - public void nextStep(final int selectedTransition) { - nextStep(selectedTransition, BigDecimal.ZERO); - } - - public void nextStep(final ecdar.simulation.Transition transition, final BigDecimal delay) { - int index = availableTransitions.indexOf(transition); - if (index != -1) { - nextStep(index, delay); - } - } - - /** - * Updates all values and clocks that are used doing the current simulation. - * It also stores the variables in the {@link SimulationHandler#simulationVariables} - * and the clocks in {@link SimulationHandler#simulationClocks}. - */ - private void updateAllValues() { - currentTime.set(currentTime.get().add(delay)); - //successor.getState().setTime(currentTime.get()); - setSimVarAndClocks(); - } - - /** - * Sets the value of simulation variables and clocks, based on {@link SimulationHandler#currentConcreteState} - */ - private void setSimVarAndClocks() { - // The variables and clocks are all found in the getVariables array - // the array is always of the following order: variables, clocks. - // The noOfVars variable thus also functions as an offset for the clocks in the getVariables array -// final int noOfClocks = engine.getSystem().getNoOfClocks(); -// final int noOfVars = engine.getSystem().getNoOfVariables(); - -// for (int i = 0; i < noOfVars; i++){ -// simulationVariables.put(engine.getSystem().getVariableName(i), -// currentConcreteState.get().getVariables()[i].getValue(BigDecimal.ZERO)); -// } - - // As the clocks values starts after the variables values in currentConcreteState.get().getVariables() - // Then i needs to start where the variables ends. - // j is needed to map the correct name with the value -// for (int i = noOfVars, j = 0; i < noOfClocks + noOfVars ; i++, j++) { -// simulationClocks.put(engine.getSystem().getClockName(j), -// currentConcreteState.get().getVariables()[i].getValue(BigDecimal.ZERO)); -// } - } - - /** - * Getter for the current concrete state - * - * @return the current {@link SimulationState} - */ - public SimulationState getCurrentState() { - return currentConcreteState.get(); - } - - /** - * The way to get the time in the current state of a simulation - * - * @return the time in the current state - */ - public BigDecimal getCurrentTime() { - return currentTime.get(); - } - - public ObjectProperty currentTimeProperty() { - return currentTime; - } - - /** - * The way to get the delay of the latest step in the simulation - * - * @return the delay of the latest step in the in the simulation - */ - public BigDecimal getDelay() { - return delay; - } - - /** - * The number of total steps taken in the current simulation - * - * @return the number of steps - */ - public int getNumberOfSteps() { - return numberOfSteps; - } - - /** - * All the transitions taken in this simulation - * - * @return an {@link ObservableList} of all the transitions taken in this simulation so far - */ - public ObservableList getTraceLog() { - return traceLog; - } - - /** - * All the available transitions in this state - * - * @return an {@link ObservableList} of all the currently available transitions in this state - */ - public ObservableList getAvailableTransitions() { - return availableTransitions; - } - - /** - * All the variables connected to the current simulation. - * This does not return any clocks, if you need please use {@link SimulationHandler#getSimulationClocks()} instead - * - * @return a {@link Map} where the name (String) is the key, and a {@link BigDecimal} is the value - */ - public ObservableMap getSimulationVariables() { - return simulationVariables; - } - - /** - * All the clocks connected to the current simulation. - * - * @return a {@link Map} where the name (String) is the key, and a {@link BigDecimal} is the clock value - * @see SimulationHandler#getSimulationVariables() - */ - public ObservableMap getSimulationClocks() { - return simulationClocks; - } - - /** - * The initial state of the current simulation - * - * @return the initial {@link SimulationState} of this simulation - */ - public SimulationState getInitialConcreteState() { - // ToDo: Implement - return initialConcreteState.get(); - } - - public ObjectProperty initialConcreteStateProperty() { - return initialConcreteState; - } - - /** - * Prints all available transitions to {@link System#out}. - * This is very useful for debugging. - * If a string representation is needed please use {@link SimulationHandler#getAvailableTransitionsAsStrings()} - * instead. - */ - public void printAvailableTransitions() { - System.out.println("---------------------------------"); - - System.out.println(numberOfSteps + " Successor state " + currentConcreteState.toString() + " Entry time " + currentTime); - System.out.print("Available transitions: "); - availableTransitions.forEach( - Transition -> System.out.println(Transition.getLabel() + " ")); - - if (!availableTransitions.isEmpty()) { - for (int i = 0; i < availableTransitions.get(0).getEdges().size(); i++) { - // ToDo: Implement -// System.out.println("Edges: " + -// availableTransitions.get(0).getEdges().get(i).getEdge().getSource().getPropertyValue("name") + -// "." + availableTransitions.get(0).getEdges().get(i).getName() + " --> " + -// availableTransitions.get(0).getEdges().get(i).getEdge().getTarget().getPropertyValue("name")); - } - } - - System.out.println("---------------------------------"); - } - - /** - * To get all available transitions as strings - * - * @return an ArrayList of all the enabled transitions - */ - public ArrayList getAvailableTransitionsAsStrings() { - final ArrayList transitions = new ArrayList<>(); - for (final ecdar.simulation.Transition Transition : availableTransitions) { - transitions.add(Transition.getLabel()); - } - return transitions; - } - - public EcdarSystem getSystem() { - return system; - } - - public String getComposition() { return composition;} - - public void setComposition(String composition) {this.composition = composition;} - - public boolean isSimulationRunning() { - return false; // ToDo: Implement - } - - /** - * Close all open backend connection and kill all locally running processes - * - * @throws IOException if any of the sockets do not respond - */ - public void closeAllBackendConnections() throws IOException { - for (BackendConnection con : connections) { - con.close(); - } - } +package ecdar.backend; + +import EcdarProtoBuf.ComponentProtos; +import EcdarProtoBuf.ObjectProtos; +import EcdarProtoBuf.QueryProtos; +import EcdarProtoBuf.ObjectProtos.Decision; +import ecdar.Ecdar; +import ecdar.abstractions.*; +import ecdar.simulation.SimulationState; +import io.grpc.stub.StreamObserver; +import javafx.application.Platform; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.ObservableMap; +import javafx.util.Pair; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import EcdarProtoBuf.QueryProtos.SimulationInfo; +import EcdarProtoBuf.QueryProtos.SimulationStepRequest; +import EcdarProtoBuf.QueryProtos.SimulationStepResponse; + +/** + * Handles state changes, updates of values / clocks, and keeps track of all the transitions that + * have been taken throughout a simulation. + */ +public class SimulationHandler { + public static final String QUERY_PREFIX = "Query: "; + private String composition; + public ObjectProperty currentState = new SimpleObjectProperty<>(); + public ObjectProperty initialState = new SimpleObjectProperty<>(); + public ObjectProperty selectedEdge = new SimpleObjectProperty<>(); + private EcdarSystem system; + private int numberOfSteps; + + private final ObservableMap simulationVariables = FXCollections.observableHashMap(); + private final ObservableMap simulationClocks = FXCollections.observableHashMap(); + public ObservableList traceLog = FXCollections.observableArrayList(); + private final BackendDriver backendDriver; + private final ArrayList connections = new ArrayList<>(); + + /** + * Empty constructor that should be used if the system or project has not be initialized yet + */ + public SimulationHandler(BackendDriver backendDriver) { + this.backendDriver = backendDriver; + } + + + /** + * Initializes the values and properties in the {@link SimulationHandler}. + * Can also be used as a reset of the simulation. + * THIS METHOD DOES NOT RESET THE ENGINE, + */ + private void initializeSimulation() { + // Initialization + this.numberOfSteps = 0; + this.simulationVariables.clear(); + this.simulationClocks.clear(); + this.currentState.set(null); + this.selectedEdge.set(null); + this.traceLog.clear(); + + this.system = getSystem(); + } + + + /** + * Reloads the whole simulation sets the initial transitions, states, etc + */ + public void initialStep() { + initializeSimulation(); + + GrpcRequest request = new GrpcRequest(backendConnection -> { + StreamObserver responseObserver = new StreamObserver<>() { + @Override + public void onNext(QueryProtos.SimulationStepResponse value) { + currentState.set(new SimulationState(value.getNewDecisionPoint())); + Platform.runLater(() -> traceLog.add(currentState.get())); + } + + @Override + public void onError(Throwable t) { + Ecdar.showToast("Could not start simulation:\n" + t.getMessage()); + + // Release backend connection + backendDriver.addBackendConnection(backendConnection); + connections.remove(backendConnection); + } + + @Override + public void onCompleted() { + // Release backend connection + backendDriver.addBackendConnection(backendConnection); + connections.remove(backendConnection); + } + }; + + var comInfo = ComponentProtos.ComponentsInfo.newBuilder(); + for (Component c : Ecdar.getProject().getComponents()) { + comInfo.addComponents(ComponentProtos.Component.newBuilder().setJson(c.serialize().toString()).build()); + } + comInfo.setComponentsHash(comInfo.getComponentsList().hashCode()); + var simStartRequest = QueryProtos.SimulationStartRequest.newBuilder(); + var simInfo = QueryProtos.SimulationInfo.newBuilder() + .setComponentComposition(composition) + .setComponentsInfo(comInfo); + simStartRequest.setSimulationInfo(simInfo); + backendConnection.getStub().withDeadlineAfter(this.backendDriver.getResponseDeadline(), TimeUnit.MILLISECONDS) + .startSimulation(simStartRequest.build(), responseObserver); + }, BackendHelper.getDefaultBackendInstance()); + + backendDriver.addRequestToExecutionQueue(request); + + //Save the previous states, and get the new + this.traceLog.add(currentState.get()); + numberOfSteps++; + + //Updates the transitions available + updateAllValues(); + + } + + /** + * Resets the simulation to the initial location + */ + public void resetToInitialLocation() { + initialStep(); + } + + /** + * Take a step in the simulation. + */ + public void nextStep() { + GrpcRequest request = new GrpcRequest(backendConnection -> { + StreamObserver responseObserver = new StreamObserver<>() { + @Override + public void onNext(QueryProtos.SimulationStepResponse value) { + currentState.set(new SimulationState(value.getNewDecisionPoint())); + Platform.runLater(() -> traceLog.add(currentState.get())); + } + + @Override + public void onError(Throwable t) { + Ecdar.showToast("Could not take next step in simulation\nError: " + t.getMessage()); + + // Release backend connection + backendDriver.addBackendConnection(backendConnection); + connections.remove(backendConnection); + } + + @Override + public void onCompleted() { + // Release backend connection + backendDriver.addBackendConnection(backendConnection); + connections.remove(backendConnection); + } + }; + + var comInfo = ComponentProtos.ComponentsInfo.newBuilder(); + for (Component c : Ecdar.getProject().getComponents()) { + comInfo.addComponents(ComponentProtos.Component.newBuilder().setJson(c.serialize().toString()).build()); + } + comInfo.setComponentsHash(comInfo.getComponentsList().hashCode()); + var simStepRequest = SimulationStepRequest.newBuilder(); + var simInfo = SimulationInfo.newBuilder() + .setComponentComposition(composition) + .setComponentsInfo(comInfo); + simStepRequest.setSimulationInfo(simInfo); + var source = currentState.get().getState(); + var specComp = ObjectProtos.SpecificComponent.newBuilder().setComponentName(getComponentName(selectedEdge.get())).setComponentIndex(getComponentIndex(selectedEdge.get())); + var edge = EcdarProtoBuf.ObjectProtos.Edge.newBuilder().setId(selectedEdge.get().getId()).setSpecificComponent(specComp); + var decision = Decision.newBuilder().setEdge(edge).setSource(source); + simStepRequest.setChosenDecision(decision); + + backendConnection.getStub().withDeadlineAfter(this.backendDriver.getResponseDeadline(), TimeUnit.MILLISECONDS) + .takeSimulationStep(simStepRequest.build(), responseObserver); + }, BackendHelper.getDefaultBackendInstance()); + + backendDriver.addRequestToExecutionQueue(request); + + + // increments the number of steps taken during this simulation + numberOfSteps++; + + + updateAllValues(); + } + + private String getComponentName(Edge edge) { + var components = Ecdar.getProject().getComponents(); + for (var component : components) { + for (var e : component.getEdges()) { + if (e.getId().equals(edge.getId())) { + return component.getName(); + } + } + } + throw new RuntimeException("Could not find component name for edge with id " + edge.getId()); + } + + private int getComponentIndex (Edge edge) { + for (int i = 0; i < Ecdar.getProject().getComponents().size(); i++) { + if (Ecdar.getProject().getComponents().get(i).getEdges().stream().anyMatch(p -> p.getId() == edge.getId())) { + return i; + } + }; + throw new IllegalArgumentException("Edge does not belong to any component"); + } + + + /** + * Updates all values and clocks that are used doing the current simulation. + * It also stores the variables in the {@link SimulationHandler#simulationVariables} + * and the clocks in {@link SimulationHandler#simulationClocks}. + */ + private void updateAllValues() { + setSimVarAndClocks(); + } + + /** + * Sets the value of simulation variables and clocks, based on {@link SimulationHandler#currentConcreteState} + */ + private void setSimVarAndClocks() { + // The variables and clocks are all found in the getVariables array + // the array is always of the following order: variables, clocks. + // The noOfVars variable thus also functions as an offset for the clocks in the getVariables array +// final int noOfClocks = engine.getSystem().getNoOfClocks(); +// final int noOfVars = engine.getSystem().getNoOfVariables(); + +// for (int i = 0; i < noOfVars; i++){ +// simulationVariables.put(engine.getSystem().getVariableName(i), +// currentConcreteState.get().getVariables()[i].getValue(BigDecimal.ZERO)); +// } + + // As the clocks values starts after the variables values in currentConcreteState.get().getVariables() + // Then i needs to start where the variables ends. + // j is needed to map the correct name with the value +// for (int i = noOfVars, j = 0; i < noOfClocks + noOfVars ; i++, j++) { +// simulationClocks.put(engine.getSystem().getClockName(j), +// currentConcreteState.get().getVariables()[i].getValue(BigDecimal.ZERO)); +// } + } + + + + /** + * The number of total steps taken in the current simulation + * + * @return the number of steps + */ + public int getNumberOfSteps() { + return numberOfSteps; + } + + /** + * All the transitions taken in this simulation + * + * @return an {@link ObservableList} of all the transitions taken in this simulation so far + */ + public ObservableList getTraceLog() { + return traceLog; + } + + /** + * All the available transitions in this state + * @return + * + * @return an {@link ObservableList} of all the currently available transitions in this state + */ + public ArrayList> getAvailableTransitions() { + return currentState.get().getEdges(); + } + + /** + * All the variables connected to the current simulation. + * This does not return any clocks, if you need please use {@link SimulationHandler#getSimulationClocks()} instead + * + * @return a {@link Map} where the name (String) is the key, and a {@link BigDecimal} is the value + */ + public ObservableMap getSimulationVariables() { + return simulationVariables; + } + + /** + * All the clocks connected to the current simulation. + * + * @return a {@link Map} where the name (String) is the key, and a {@link BigDecimal} is the clock value + * @see SimulationHandler#getSimulationVariables() + */ + public ObservableMap getSimulationClocks() { + return simulationClocks; + } + + /** + * The initial state of the current simulation + * + * @return the initial {@link SimulationState} of this simulation + */ + public SimulationState getInitialState() { + // ToDo: Implement + return initialState.get(); + } + + public ObjectProperty initialStateProperty() { + return initialState; + } + + + public EcdarSystem getSystem() { + return system; + } + + public String getComposition() { return composition;} + + public void setComposition(String composition) {this.composition = composition;} + + public boolean isSimulationRunning() { + return false; // ToDo: Implement + } + + /** + * Close all open backend connection and kill all locally running processes + * + * @throws IOException if any of the sockets do not respond + */ + public void closeAllBackendConnections() throws IOException { + for (BackendConnection con : connections) { + con.close(); + } + } + + + /** + * Sets the current state of the simulation to the given state from the trace log + */ + public void selectStateFromLog(SimulationState state) { + while (traceLog.get(traceLog.size() - 1) != state) { + traceLog.remove(traceLog.size() - 1); + } + currentState.set(state); + } } \ No newline at end of file diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index a7b7c558..89ebb802 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -60,7 +60,6 @@ public class EcdarController implements Initializable { public StackPane leftPane; public StackPane rightPane; public Rectangle bottomFillerElement; - public MessageTabPanePresentation messageTabPane; public StackPane modellingHelpDialogContainer; public JFXDialog modellingHelpDialog; public StackPane modalBar; @@ -180,9 +179,6 @@ public void initialize(final URL location, final ResourceBundle resources) { initializeMenuBar(); startBackgroundQueriesThread(); // Will terminate immediately if background queries are turned off - bottomFillerElement.heightProperty().bind(messageTabPane.maxHeightProperty()); - messageTabPane.getController().setRunnableForOpeningAndClosingMessageTabPane(this::changeInsetsOfProjectAndQueryPanes); - // Update file coloring when active model changes editorPresentation.getController().getActiveCanvasPresentation().getController().activeComponentProperty().addListener(observable -> projectPane.getController().updateColorsOnFilePresentations()); editorPresentation.getController().activeCanvasPresentationProperty().addListener(observable -> projectPane.getController().updateColorsOnFilePresentations()); @@ -726,7 +722,6 @@ private void updateScaling(double newScale) { scaleIcons(root, newCalculatedScale); editorPresentation.getController().scaleEdgeStatusToggle(newCalculatedScale); - messageTabPane.getController().updateScale(newScale); // Update listeners of UI scale scalingProperty.set(newScale); @@ -1005,6 +1000,7 @@ private void enterEditorMode() { leftPane.getChildren().add(projectPane); rightPane.getChildren().clear(); rightPane.getChildren().add(queryPane); + scaling.selectToggle(scaleM); // temporary fix for scaling issue // Enable or disable the menu items that can be used when in the simulator // updateMenuItems(); @@ -1190,20 +1186,6 @@ private static int getAutoCropRightX(final BufferedImage image) { throw new IllegalArgumentException("Image is all white"); } - /** - * This method is used to push the contents of the project and query panes when the tab pane is opened - */ - private void changeInsetsOfProjectAndQueryPanes() { - if (messageTabPane.getController().isOpen()) { - projectPane.showBottomInset(false); - queryPane.showBottomInset(false); - CanvasPresentation.showBottomInset(false); - } else { - projectPane.showBottomInset(true); - queryPane.showBottomInset(true); - CanvasPresentation.showBottomInset(true); - } - } private void nudgeSelected(final NudgeDirection direction) { final List selectedElements = SelectHelper.getSelectedElements(); diff --git a/src/main/java/ecdar/controllers/MessageTabPaneController.java b/src/main/java/ecdar/controllers/MessageTabPaneController.java deleted file mode 100644 index 022f582c..00000000 --- a/src/main/java/ecdar/controllers/MessageTabPaneController.java +++ /dev/null @@ -1,272 +0,0 @@ -package ecdar.controllers; - -import com.jfoenix.controls.JFXRippler; -import com.jfoenix.controls.JFXTabPane; -import ecdar.Ecdar; -import ecdar.abstractions.Component; -import ecdar.code_analysis.CodeAnalysis; -import ecdar.presentations.MessageCollectionPresentation; -import ecdar.presentations.MessagePresentation; -import javafx.animation.Interpolator; -import javafx.animation.Transition; -import javafx.application.Platform; -import javafx.beans.property.Property; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.collections.ListChangeListener; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.ScrollPane; -import javafx.scene.control.Tab; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.StackPane; -import javafx.scene.layout.VBox; -import javafx.scene.shape.Rectangle; -import javafx.util.Duration; -import org.kordamp.ikonli.javafx.FontIcon; - -import java.net.URL; -import java.util.HashMap; -import java.util.Map; -import java.util.ResourceBundle; -import java.util.function.Consumer; - -public class MessageTabPaneController implements Initializable { - public StackPane root; - public JFXTabPane tabPane; - public Tab errorsTab; - public Tab warningsTab; - public Rectangle tabPaneResizeElement; - public StackPane tabPaneContainer; - public JFXRippler collapseMessages; - public FontIcon collapseMessagesIcon; - public ScrollPane errorsScrollPane; - public VBox errorsList; - public ScrollPane warningsScrollPane; - public VBox warningsList; - public Tab backendErrorsTab; - public ScrollPane backendErrorsScrollPane; - public VBox backendErrorsList; - - private double tabPanePreviousY = 0; - private double expandHeight = 300; - private double collapseHeight = 35; - private final Property isOpen = new SimpleBooleanProperty(false); - private Runnable openCloseExternalAction; - public boolean shouldISkipOpeningTheMessagesContainer = true; - - private final Transition expandMessagesContainer = new Transition() { - { - setInterpolator(Interpolator.SPLINE(0.645, 0.045, 0.355, 1)); - setCycleDuration(Duration.millis(200)); - } - - @Override - protected void interpolate(final double frac) { - tabPaneContainer.setMaxHeight(collapseHeight + frac * (expandHeight - collapseHeight)); - } - }; - private Transition collapseMessagesContainer; - - @Override - public void initialize(final URL location, final ResourceBundle resources) { - Platform.runLater(() -> { - collapseMessagesContainer = new Transition() { - { - setInterpolator(Interpolator.SPLINE(0.645, 0.045, 0.355, 1)); - setCycleDuration(Duration.millis(200)); - } - - @Override - protected void interpolate(final double frac) { - tabPaneContainer.setMaxHeight(((tabPaneContainer.getMaxHeight() - collapseHeight) * (1 - frac)) + collapseHeight); - } - }; - - initializeTabPane(); - initializeMessages(); - - collapseMessagesContainer.play(); - }); - } - - private void initializeTabPane() { - tabPane.getSelectionModel().selectedIndexProperty().addListener((obs, oldSelected, newSelected) -> { - if (newSelected.intValue() < 0 || isOpen.getValue()) return; - - if (shouldISkipOpeningTheMessagesContainer) { - tabPane.getSelectionModel().clearSelection(); - shouldISkipOpeningTheMessagesContainer = false; - } else { - expandMessagesIfNotExpanded(); - } - }); - - tabPane.getSelectionModel().clearSelection(); - isOpen.addListener((observable, oldValue, newValue) -> { - if (newValue) { - collapseMessagesIcon.setIconLiteral("gmi-close"); - } else { - collapseMessagesIcon.setIconLiteral("gmi-expand-less"); - } - - openCloseExternalAction.run(); - }); - - isOpen.setValue(false); - } - - private void initializeMessages() { - final Map componentMessageCollectionPresentationMapForErrors = new HashMap<>(); - final Map componentMessageCollectionPresentationMapForWarnings = new HashMap<>(); - - final Consumer addComponent = (component) -> { - final MessageCollectionPresentation messageCollectionPresentationErrors = new MessageCollectionPresentation(component, CodeAnalysis.getErrors(component)); - componentMessageCollectionPresentationMapForErrors.put(component, messageCollectionPresentationErrors); - errorsList.getChildren().add(messageCollectionPresentationErrors); - - final Runnable addIfErrors = () -> { - if (CodeAnalysis.getErrors(component).size() == 0) { - errorsList.getChildren().remove(messageCollectionPresentationErrors); - } else if (!errorsList.getChildren().contains(messageCollectionPresentationErrors)) { - errorsList.getChildren().add(messageCollectionPresentationErrors); - } - }; - - addIfErrors.run(); - CodeAnalysis.getErrors(component).addListener((ListChangeListener) c -> { - while (c.next()) { - addIfErrors.run(); - } - }); - - final MessageCollectionPresentation messageCollectionPresentationWarnings = new MessageCollectionPresentation(component, CodeAnalysis.getWarnings(component)); - componentMessageCollectionPresentationMapForWarnings.put(component, messageCollectionPresentationWarnings); - warningsList.getChildren().add(messageCollectionPresentationWarnings); - - final Runnable addIfWarnings = () -> { - if (CodeAnalysis.getWarnings(component).size() == 0) { - warningsList.getChildren().remove(messageCollectionPresentationWarnings); - } else if (!warningsList.getChildren().contains(messageCollectionPresentationWarnings)) { - warningsList.getChildren().add(messageCollectionPresentationWarnings); - } - }; - - addIfWarnings.run(); - CodeAnalysis.getWarnings(component).addListener((ListChangeListener) c -> { - while (c.next()) { - addIfWarnings.run(); - } - }); - }; - - // Add error that is project wide but not a backend error - addComponent.accept(null); - - Ecdar.getProject().getComponents().forEach(addComponent); - Ecdar.getProject().getComponents().addListener((ListChangeListener) c -> { - while (c.next()) { - c.getAddedSubList().forEach(addComponent::accept); - - c.getRemoved().forEach(component -> { - errorsList.getChildren().remove(componentMessageCollectionPresentationMapForErrors.get(component)); - componentMessageCollectionPresentationMapForErrors.remove(component); - - warningsList.getChildren().remove(componentMessageCollectionPresentationMapForWarnings.get(component)); - componentMessageCollectionPresentationMapForWarnings.remove(component); - }); - } - }); - - final Map messageMessagePresentationHashMap = new HashMap<>(); - - CodeAnalysis.getBackendErrors().addListener((ListChangeListener) c -> { - while (c.next()) { - c.getAddedSubList().forEach(addedMessage -> { - final MessagePresentation messagePresentation = new MessagePresentation(addedMessage); - backendErrorsList.getChildren().add(messagePresentation); - messageMessagePresentationHashMap.put(addedMessage, messagePresentation); - }); - - c.getRemoved().forEach(removedMessage -> { - backendErrorsList.getChildren().remove(messageMessagePresentationHashMap.get(removedMessage)); - messageMessagePresentationHashMap.remove(removedMessage); - }); - } - }); - } - - public void expandMessagesIfNotExpanded() { - if (!isOpen.getValue()) { - expandMessagesContainer.play(); - isOpen.setValue(true); - } - } - - public void collapseMessagesIfNotCollapsed() { - if (isOpen.getValue()) { - expandHeight = tabPaneContainer.getHeight(); - collapseMessagesContainer.play(); - isOpen.setValue(false); - } - } - - public void setRunnableForOpeningAndClosingMessageTabPane(Runnable runnable) { - openCloseExternalAction = runnable; - } - - public boolean isOpen() { - return isOpen.getValue(); - } - - /** - * Update the scale of root and all children - * - * @param scale the new scale of the tab pane - */ - public void updateScale(double scale) { - final double heightScaled = 35 * scale; - - collapseHeight = heightScaled; - tabPane.setTabMinHeight(heightScaled); - tabPane.setTabMaxHeight(heightScaled); - ((StackPane) collapseMessagesIcon.getParent()).setMinHeight(heightScaled); - ((StackPane) collapseMessagesIcon.getParent()).setMaxHeight(heightScaled); - ((StackPane) collapseMessagesIcon.getParent()).setMinWidth(heightScaled); - ((StackPane) collapseMessagesIcon.getParent()).setMaxWidth(heightScaled); - - if (!isOpen()) { - collapseMessagesContainer.play(); - } - } - - @FXML - public void collapseMessagesClicked() { - if (isOpen()) { - expandHeight = tabPaneContainer.getHeight(); - collapseMessagesContainer.play(); - } else { - expandMessagesContainer.play(); - } - - isOpen.setValue(!isOpen.getValue()); - } - - @FXML - public void tabPaneResizeElementPressed(final MouseEvent event) { - this.tabPanePreviousY = event.getScreenY(); - } - - @FXML - public void tabPaneResizeElementDragged(final MouseEvent event) { - expandMessagesIfNotExpanded(); - - final double mouseY = event.getScreenY(); - double newHeight = tabPaneContainer.getMaxHeight() - (mouseY - tabPanePreviousY); - newHeight = Math.max(collapseHeight, newHeight); - - tabPaneContainer.setMaxHeight(newHeight); - - openCloseExternalAction.run(); - this.tabPanePreviousY = mouseY; - } -} diff --git a/src/main/java/ecdar/controllers/SimulatorController.java b/src/main/java/ecdar/controllers/SimulatorController.java index 63d82474..cacdc467 100644 --- a/src/main/java/ecdar/controllers/SimulatorController.java +++ b/src/main/java/ecdar/controllers/SimulatorController.java @@ -5,7 +5,6 @@ import ecdar.backend.SimulationHandler; import ecdar.presentations.SimulatorOverviewPresentation; import ecdar.simulation.SimulationState; -import ecdar.simulation.Transition; import ecdar.utility.colors.Color; import javafx.beans.property.DoubleProperty; import javafx.beans.property.ObjectProperty; @@ -16,7 +15,6 @@ import java.net.URL; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.ResourceBundle; @@ -29,7 +27,6 @@ public class SimulatorController implements Initializable { private boolean firstTimeInSimulator; private final static DoubleProperty width = new SimpleDoubleProperty(), height = new SimpleDoubleProperty(); - private static ObjectProperty selectedTransition = new SimpleObjectProperty<>(); private static ObjectProperty selectedState = new SimpleObjectProperty<>(); @Override @@ -49,25 +46,32 @@ public void willShow() { final SimulationHandler sm = Ecdar.getSimulationHandler(); boolean shouldSimulationBeReset = true; - if (sm.getCurrentState() == null) sm.initialStep(); // ToDo NIELS: Find better solution - //Have the user left a trace + + // If the user left a trace, continue from that trace if (sm.traceLog.size() >= 2) { shouldSimulationBeReset = false; } - if (!firstTimeInSimulator && !new HashSet<>(overviewPresentation.getController().getComponentObservableList()) - .containsAll(findComponentsInCurrentSimulation(SimulationInitializationDialogController.ListOfComponents))) { + // If the composition is not the same as previous simulation, reset the simulation + if (!(overviewPresentation.getController().getComponentObservableList().hashCode() == + findComponentsInCurrentSimulation(SimulationInitializationDialogController.ListOfComponents).hashCode())) { shouldSimulationBeReset = true; } - - if (shouldSimulationBeReset || firstTimeInSimulator) { - + + if (shouldSimulationBeReset || firstTimeInSimulator || sm.currentState.get() == null) { resetSimulation(); - sm.resetToInitialLocation(); + sm.initialStep(); } + overviewPresentation.getController().addProcessesToGroup(); - overviewPresentation.getController().highlightProcessState(sm.getCurrentState()); + + // If the simulation continues, highligt the current state and available edges + if (sm.currentState.get() != null && !shouldSimulationBeReset) { + overviewPresentation.getController().highlightProcessState(sm.currentState.get()); + overviewPresentation.getController().highlightAvailableEdges(sm.currentState.get()); + } + } /** @@ -80,26 +84,8 @@ private void resetSimulation() { overviewPresentation.getController().getComponentObservableList().clear(); overviewPresentation.getController().getComponentObservableList().addAll(listOfComponentsForSimulation); firstTimeInSimulator = false; - - //Method that colors all initial states. - initialstatelighter(listOfComponentsForSimulation); - } - - private void initialstatelighter(List listofComponents){ - for(Component comp: listofComponents) - { - Location initiallocation = comp.getInitialLocation(); - initiallocation.setColor(Color.ORANGE); - List tempedge = comp.getRelatedEdges(initiallocation); - /* for(DisplayableEdge e: tempedge) - { - if(e.getSourceLocation() == initiallocation) - { - e.setIsHighlighted(true); - } - }*/ - } } + /** * Finds the components that are used in the current simulation by looking at the components found in * Ecdar.getProject.getComponents() and compares them to the components found in the queryComponents list @@ -108,13 +94,11 @@ private void initialstatelighter(List listofComponents){ */ private List findComponentsInCurrentSimulation(List queryComponents) { //Show components from the system - List components = new ArrayList<>(); - - components = Ecdar.getProject().getComponents(); + List components = Ecdar.getProject().getComponents(); //Matches query components against with existing components and adds them to simulation List SelectedComponents = new ArrayList<>(); - for(Component comp:components) { + for(Component comp : components) { for(String componentInQuery : queryComponents) { if((comp.getName().equals(componentInQuery))) { Component temp = new Component(comp.serialize()); @@ -137,10 +121,6 @@ public void resetCurrentSimulation() { public void willHide() { overviewPresentation.getController().removeProcessesFromGroup(); - overviewPresentation.getController().getComponentObservableList().forEach(component -> { - // Previously reset coordinates of component box - }); - overviewPresentation.getController().unhighlightProcesses(); } public static DoubleProperty getWidthProperty() { @@ -151,19 +131,6 @@ public static DoubleProperty getHeightProperty() { return height; } - - public static ObjectProperty getSelectedTransitionProperty() { - return selectedTransition; - } - - public static void setSelectedTransition(Transition selectedTransition) { - SimulatorController.selectedTransition.set(selectedTransition); - } - - public static ObjectProperty getSelectedStateProperty() { - return selectedState; - } - public static void setSelectedState(SimulationState selectedState) { SimulatorController.selectedState.set(selectedState); } diff --git a/src/main/java/ecdar/controllers/SimulatorOverviewController.java b/src/main/java/ecdar/controllers/SimulatorOverviewController.java index 90386d82..b4815785 100644 --- a/src/main/java/ecdar/controllers/SimulatorOverviewController.java +++ b/src/main/java/ecdar/controllers/SimulatorOverviewController.java @@ -72,8 +72,8 @@ public void initialize(final URL location, final ResourceBundle resources) { initializeWindowResizing(); initializeZoom(); - initializeHighlighting(); initializeSimulationVariables(); + initializeHighlighting(); // Add the processes and group to the view addProcessesToGroup(); scrollPane.setContent(groupContainer); @@ -105,7 +105,7 @@ private void initializeProcessContainer() { } } // Highlight the current state when the processes change - highlightProcessState(Ecdar.getSimulationHandler().getCurrentState()); // ToDo NIELS: Throws NullPointerException inside method due to currentState + highlightProcessState(Ecdar.getSimulationHandler().currentState.get()); // ToDo NIELS: Throws NullPointerException inside method due to currentState processContainer.getChildren().addAll(processes.values()); processPresentations.putAll(processes); }); @@ -297,32 +297,21 @@ private void handleWidthOnScale(final Number oldValue, final Number newValue) { } } - /** + /** * Initializer method to setup listeners that handle highlighting when selected/current state/transition changes */ private void initializeHighlighting() { - SimulatorController.getSelectedTransitionProperty().addListener((observable, oldTransition, newTransition) -> { + Ecdar.getSimulationHandler().selectedEdge.addListener((observable, oldEdge, newEdge) -> { unhighlightProcesses(); - - // If the new transition is not null, we want to highlight the locations and edges in the new value - // otherwise we highlight the current state - if (newTransition != null) { - highlightProcessTransition(newTransition); - } else { - highlightProcessState(Ecdar.getSimulationHandler().getCurrentState()); - } }); - SimulatorController.getSelectedStateProperty().addListener((observable, oldState, newState) -> { - unhighlightProcesses(); - - // If the new state is not null, we want to highlight the locations in the new value - // otherwise we highlight the current state - if (newState != null) { - highlightProcessState(newState); - } else { - highlightProcessState(Ecdar.getSimulationHandler().getCurrentState()); + Ecdar.getSimulationHandler().currentState.addListener((observable, oldState, newState) -> { + if (newState == null) { + return; } + unhighlightProcesses(); + highlightProcessState(newState); + highlightAvailableEdges(newState); }); } @@ -350,6 +339,7 @@ public void highlightProcessTransition(final Transition transition) { processesToHide.forEach(ProcessPresentation::showInactive); } + /** * Unhighlights all processes */ @@ -370,17 +360,34 @@ public void highlightProcessState(final SimulationState state) { for (int i = 0; i < state.getLocations().size(); i++) { final Pair loc = state.getLocations().get(i); - for (final ProcessPresentation presentation : processPresentations.values()) { - final String processName = presentation.getController().getComponent().getName(); - - if (processName.equals(loc.getKey())) { - presentation.getController().highlightLocation(loc.getValue()); - } - } + processPresentations.values().stream() + .filter(p -> p.getController().getComponent().getName().equals(loc.getKey())) + .forEach(p -> p.getController().highlightLocation(loc.getValue())); } } public ObservableList getComponentObservableList() { return componentArrayList; } + + public void highlightAvailableEdges(SimulationState state) { + // unhighlight all edges + for (Pair edge : state.getEdges()) { + processPresentations.values().stream() + .forEach(p -> p.getController().getComponent().getEdges().stream() + .forEach(e -> e.setIsHighlighted(false))); + } + + // highlight available edges in the given state + for (Pair edge : state.getEdges()) { + processPresentations.values().stream() + .forEach(p -> p.getController().getComponent().getEdges().stream() + .forEach(e -> { + if (e.getId().equals(edge.getValue())) { + e.setIsHighlighted(true); + } + })); + } + } + } diff --git a/src/main/java/ecdar/controllers/TracePaneElementController.java b/src/main/java/ecdar/controllers/TracePaneElementController.java index 5fe116bd..6898dfad 100755 --- a/src/main/java/ecdar/controllers/TracePaneElementController.java +++ b/src/main/java/ecdar/controllers/TracePaneElementController.java @@ -3,8 +3,8 @@ import com.jfoenix.controls.JFXRippler; import ecdar.Ecdar; import ecdar.abstractions.Location; -import ecdar.simulation.SimulationState; import ecdar.backend.SimulationHandler; +import ecdar.simulation.SimulationState; import ecdar.presentations.TransitionPresentation; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleIntegerProperty; @@ -46,7 +46,7 @@ public void initialize(URL location, ResourceBundle resources) { Ecdar.getSimulationHandler().getTraceLog().addListener((ListChangeListener) c -> { while (c.next()) { for (final SimulationState state : c.getAddedSubList()) { - insertTraceState(state, true); + if (state != null) insertTraceState(state, true); } for (final SimulationState state : c.getRemoved()) { @@ -116,7 +116,7 @@ private void insertTraceState(final SimulationState state, final boolean shouldA event.consume(); final SimulationHandler simHandler = Ecdar.getSimulationHandler(); if (simHandler == null) return; - Ecdar.getSimulationHandler().selectTransitionFromLog(state); + Ecdar.getSimulationHandler().selectStateFromLog(state); }); EventHandler mouseEntered = transitionPresentation.getOnMouseEntered(); @@ -131,12 +131,11 @@ private void insertTraceState(final SimulationState state, final boolean shouldA mouseExited.handle(event); }); - String title = traceString(state); transitionPresentation.getController().setTitle(title); - // Only insert the presentation into the view if the trace is expanded - if (isTraceExpanded.get()) { + // Only insert the presentation into the view if the trace is expanded & state is not null + if (isTraceExpanded.get() && state != null) { traceList.getChildren().add(transitionPresentation); if (shouldAnimate) { transitionPresentation.playFadeAnimation(); @@ -151,16 +150,13 @@ private void insertTraceState(final SimulationState state, final boolean shouldA * @return A string representing the state */ private String traceString(SimulationState state) { - if (state == null) { - return "Initial state"; - } StringBuilder title = new StringBuilder("("); int length = state.getLocations().size(); for (int i = 0; i < length; i++) { Location loc = Ecdar.getProject() .findComponent(state.getLocations().get(i).getKey()) .findLocation(state.getLocations().get(i).getValue()); - String locationName = loc.getNickname(); + String locationName = loc.getId(); if (i == length - 1) { title.append(locationName); } else { diff --git a/src/main/java/ecdar/controllers/TransitionPaneElementController.java b/src/main/java/ecdar/controllers/TransitionPaneElementController.java index 0233e335..30b355ff 100755 --- a/src/main/java/ecdar/controllers/TransitionPaneElementController.java +++ b/src/main/java/ecdar/controllers/TransitionPaneElementController.java @@ -5,11 +5,9 @@ import ecdar.Ecdar; import ecdar.abstractions.Edge; import ecdar.simulation.Transition; -import ecdar.backend.SimulationHandler; import ecdar.presentations.TransitionPresentation; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; -import javafx.collections.ListChangeListener; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.Initializable; @@ -45,17 +43,6 @@ public class TransitionPaneElementController implements Initializable { @Override public void initialize(URL location, ResourceBundle resources) { - Ecdar.getSimulationHandler().availableTransitions.addListener((ListChangeListener) c -> { - while (c.next()) { - for (Transition trans : c.getAddedSubList()) insertTransition(trans); - - for (final Transition trans: c.getRemoved()) { - transitionList.getChildren().remove(transitionPresentationMap.get(trans)); - transitionPresentationMap.remove(trans); - } - } - }); - initializeTransitionExpand(); initializeDelayChooser(); } @@ -137,27 +124,16 @@ private void insertTransition(Transition transition) { // Add the event to existing mouseEntered events // e.g. TransitionPresentation already has mouseEntered functionality and we want to keep it EventHandler mouseEntered = transitionPresentation.getOnMouseEntered(); - transitionPresentation.setOnMouseEntered(event -> { - SimulatorController.setSelectedTransition(transitionPresentation.getController().getTransition()); - mouseEntered.handle(event); - }); + // transitionPresentation.setOnMouseEntered(event -> { + // SimulatorController.setSelectedTransition(transitionPresentation.getController().getTransition()); + // mouseEntered.handle(event); + // }); EventHandler mouseExited = transitionPresentation.getOnMouseExited(); transitionPresentation.setOnMouseExited(event -> { - SimulatorController.setSelectedTransition(null); mouseExited.handle(event); }); - transitionPresentation.setOnMouseClicked(event -> { - event.consume(); - - // Performs the next step of the simulation when clicking on a transition - SimulationHandler simHandler = Ecdar.getSimulationHandler(); - if (simHandler != null) { - simHandler.nextStep(transitionPresentation.getController().getTransition(), this.delay.get()); - } - }); - transitionPresentationMap.put(transition, transitionPresentation); // Only insert the presentation into the view if the transitions are expanded @@ -195,13 +171,11 @@ private void expandTransitions() { } /** - * Gets the initial step from the SimulationHandler. - * Used by the refresh button. + * Restart simulation to the initial step. */ @FXML - private void refreshTransitions() { - SimulatorController.setSelectedTransition(null); -// MainController.openReloadSimulationDialog(); // ToDo: Implement + private void restartSimulation() { + Ecdar.getSimulationHandler().resetToInitialLocation(); } /** diff --git a/src/main/java/ecdar/presentations/LocationPresentation.java b/src/main/java/ecdar/presentations/LocationPresentation.java index 0b25e090..57def2ab 100644 --- a/src/main/java/ecdar/presentations/LocationPresentation.java +++ b/src/main/java/ecdar/presentations/LocationPresentation.java @@ -206,10 +206,15 @@ private void initializeTags() { } // Bind the model to the layout - loc.nicknameXProperty().bindBidirectional(controller.nicknameTag.translateXProperty()); - loc.nicknameYProperty().bindBidirectional(controller.nicknameTag.translateYProperty()); - loc.invariantXProperty().bindBidirectional(controller.invariantTag.translateXProperty()); - loc.invariantYProperty().bindBidirectional(controller.invariantTag.translateYProperty()); + // Check is needed because the property cannot be bound twice + // which happens when switching from the simulator to the editor + if (!loc.nicknameXProperty().isBound() && !loc.nicknameYProperty().isBound() && + !loc.invariantXProperty().isBound() && !loc.invariantYProperty().isBound()) { + loc.nicknameXProperty().bindBidirectional(controller.nicknameTag.translateXProperty()); + loc.nicknameYProperty().bindBidirectional(controller.nicknameTag.translateYProperty()); + loc.invariantXProperty().bindBidirectional(controller.invariantTag.translateXProperty()); + loc.invariantYProperty().bindBidirectional(controller.invariantTag.translateYProperty()); + } final Consumer updateTags = location -> { // Update the color diff --git a/src/main/java/ecdar/presentations/MessageTabPanePresentation.java b/src/main/java/ecdar/presentations/MessageTabPanePresentation.java deleted file mode 100644 index 88c8dda8..00000000 --- a/src/main/java/ecdar/presentations/MessageTabPanePresentation.java +++ /dev/null @@ -1,115 +0,0 @@ -package ecdar.presentations; - -import ecdar.code_analysis.CodeAnalysis; -import ecdar.controllers.MessageTabPaneController; -import javafx.beans.InvalidationListener; -import javafx.beans.Observable; -import javafx.scene.Cursor; -import javafx.scene.layout.StackPane; - -public class MessageTabPanePresentation extends StackPane { - private final MessageTabPaneController controller; - - public MessageTabPanePresentation() { - controller = new EcdarFXMLLoader().loadAndGetController("MessageTabPanePresentation.fxml", this); - initializeMessageContainer(); - } - - public MessageTabPaneController getController() { - return controller; - } - - private void initializeMessageContainer() { - // The element of which you drag to resize should be equal to the width of the window (main stage) - controller.tabPaneResizeElement.sceneProperty().addListener((observableScene, oldScene, newScene) -> { - if (oldScene == null && newScene != null) { - // scene is set for the first time. Now its the time to listen stage changes. - newScene.windowProperty().addListener((observableWindow, oldWindow, newWindow) -> { - if (oldWindow == null && newWindow != null) { - newWindow.widthProperty().addListener((observableWidth, oldWidth, newWidth) -> { - controller.tabPaneResizeElement.setWidth(newWidth.doubleValue() - 30); - }); - } - }); - } - }); - - // Resize cursor - controller.tabPaneResizeElement.setCursor(Cursor.N_RESIZE); - - // Remove the background of the scroll panes - controller.errorsScrollPane.setStyle("-fx-background-color: transparent;"); - controller.warningsScrollPane.setStyle("-fx-background-color: transparent;"); - - final Runnable collapseIfNoErrorsOrWarnings = () -> { - new Thread(() -> { - // Wait for a second to check if new warnings or errors occur - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - // Check if any warnings or errors occurred - if (CodeAnalysis.getBackendErrors().size() + CodeAnalysis.getErrors().size() + CodeAnalysis.getWarnings().size() == 0) { - controller.collapseMessagesIfNotCollapsed(); - } - }).start(); - }; - - // Update the tab-text and expand/collapse the view - CodeAnalysis.getBackendErrors().addListener(new InvalidationListener() { - @Override - public void invalidated(final Observable observable) { - final int errors = CodeAnalysis.getBackendErrors().size(); - if (errors == 0) { - controller.backendErrorsTab.setText("Backend Errors"); - } else { - controller.backendErrorsTab.setText("Backend Errors (" + errors + ")"); - controller.expandMessagesIfNotExpanded(); - controller.tabPane.getSelectionModel().select(controller.backendErrorsTab); - } - - collapseIfNoErrorsOrWarnings.run(); - } - }); - - // Update the tab-text and expand/collapse the view - CodeAnalysis.getErrors().addListener(new InvalidationListener() { - @Override - public void invalidated(final Observable observable) { - final int errors = CodeAnalysis.getErrors().size(); - if (errors == 0) { - controller.errorsTab.setText("Errors"); - } else { - controller.errorsTab.setText("Errors (" + errors + ")"); - controller.expandMessagesIfNotExpanded(); - controller.tabPane.getSelectionModel().select(controller.errorsTab); - } - - collapseIfNoErrorsOrWarnings.run(); - } - }); - - - // Update the tab-text and expand/collapse the view - CodeAnalysis.getWarnings().addListener(new InvalidationListener() { - @Override - public void invalidated(final Observable observable) { - final int warnings = CodeAnalysis.getWarnings().size(); - if (warnings == 0) { - controller.warningsTab.setText("Warnings"); - } else { - controller.warningsTab.setText("Warnings (" + warnings + ")"); - //We must select the warnings tab but we don't want the messages areas to open - controller.shouldISkipOpeningTheMessagesContainer = true; - controller.tabPane.getSelectionModel().select(controller.warningsTab); - } - - collapseIfNoErrorsOrWarnings.run(); - } - }); - - controller.collapseMessagesIcon.getStyleClass().add("icon-size-medium"); - } -} \ No newline at end of file diff --git a/src/main/java/ecdar/presentations/NailPresentation.java b/src/main/java/ecdar/presentations/NailPresentation.java index 74167fba..41913c2a 100644 --- a/src/main/java/ecdar/presentations/NailPresentation.java +++ b/src/main/java/ecdar/presentations/NailPresentation.java @@ -97,9 +97,14 @@ private void initializePropertyTag() { propertyTag.setTranslateX(controller.getNail().getPropertyX()); propertyTag.setTranslateY(controller.getNail().getPropertyY()); } - controller.getNail().propertyXProperty().bindBidirectional(propertyTag.translateXProperty()); - controller.getNail().propertyYProperty().bindBidirectional(propertyTag.translateYProperty()); + // Check is needed because the property cannot be bound twice + // which happens when switching from the simulator to the editor + if (!controller.getNail().propertyXProperty().isBound() && !controller.getNail().propertyYProperty().isBound()) { + controller.getNail().propertyXProperty().bindBidirectional(propertyTag.translateXProperty()); + controller.getNail().propertyYProperty().bindBidirectional(propertyTag.translateYProperty()); + } + Label propertyLabel = controller.propertyLabel; if(propertyType.equals(Edge.PropertyType.SELECTION)) { diff --git a/src/main/java/ecdar/presentations/SimEdgePresentation.java b/src/main/java/ecdar/presentations/SimEdgePresentation.java index 8d74623b..540a7db0 100755 --- a/src/main/java/ecdar/presentations/SimEdgePresentation.java +++ b/src/main/java/ecdar/presentations/SimEdgePresentation.java @@ -1,11 +1,13 @@ package ecdar.presentations; +import ecdar.Ecdar; import ecdar.abstractions.Component; import ecdar.abstractions.Edge; import ecdar.controllers.SimEdgeController; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.scene.Group; +import javafx.util.Pair; /** * The presentation class for the edges shown in the {@link SimulatorOverviewPresentation} @@ -24,9 +26,25 @@ public SimEdgePresentation(final Edge edge, final Component component) { controller.setComponent(component); this.component.bind(controller.componentProperty()); + + // when hovering mouse the curser should change to hand + this.setOnMouseEntered(event -> { + if (Ecdar.getSimulationHandler().currentState.get().getEdges().contains(new Pair<>(component.getName(), edge.getId()))) + this.getScene().setCursor(javafx.scene.Cursor.HAND); + }); + this.setOnMouseExited(event -> this.getScene().setCursor(javafx.scene.Cursor.DEFAULT)); + + // when clicking the edge the edge should be selected and the simulation should take next step (if the edge is enabled) + this.setOnMouseClicked(event -> { + if (Ecdar.getSimulationHandler().currentState.get().getEdges().contains(new Pair<>(component.getName(), edge.getId()))) { + Ecdar.getSimulationHandler().selectedEdge.set(edge); + Ecdar.getSimulationHandler().nextStep(); + } + }); } public SimEdgeController getController() { return controller; } + } diff --git a/src/main/java/ecdar/simulation/SimulationState.java b/src/main/java/ecdar/simulation/SimulationState.java index 0a1c03f8..a6b4f5e3 100644 --- a/src/main/java/ecdar/simulation/SimulationState.java +++ b/src/main/java/ecdar/simulation/SimulationState.java @@ -1,51 +1,54 @@ package ecdar.simulation; import EcdarProtoBuf.ObjectProtos; +import EcdarProtoBuf.ObjectProtos.State; +import ecdar.Ecdar; import javafx.util.Pair; -import java.math.BigDecimal; import java.util.ArrayList; public class SimulationState { + // locations and edges are saved as key-value pair where key is component name and value = id private final ArrayList> locations; + private final ArrayList> edges; + private final State state; - public SimulationState(ObjectProtos.State protoBufState) { + public SimulationState(ObjectProtos.DecisionPoint decisionPoint) { locations = new ArrayList<>(); - for (ObjectProtos.Location location : protoBufState.getLocationTuple().getLocationsList()) { - locations.add(new Pair<>(location.getId(), location.getSpecificComponent().getComponentName())); + for (ObjectProtos.Location location : decisionPoint.getSource().getLocationTuple().getLocationsList()) { + locations.add(new Pair<>(location.getSpecificComponent().getComponentName(), location.getId())); } - } - public void setTime(BigDecimal value) { - // ToDo: Implement + edges = new ArrayList<>(); + if (decisionPoint.getEdgesList().isEmpty()) { + Ecdar.showToast("No available transitions."); + } + for (ObjectProtos.Edge edge : decisionPoint.getEdgesList()) { + edges.add(new Pair<>(getComponentName(edge.getId()), edge.getId())); + } + state = decisionPoint.getSource(); } - public Number getTime() { - // ToDo: Implement - return new Number() { - @Override - public int intValue() { - return 0; - } - - @Override - public long longValue() { - return 0; - } - - @Override - public float floatValue() { - return 0; - } + public ArrayList> getLocations() { + return locations; + } + public ArrayList> getEdges() { + return edges; + } - @Override - public double doubleValue() { - return 0; - } - }; + public State getState() { + return state; } - public ArrayList> getLocations() { - return locations; + private String getComponentName(String id) { + var components = Ecdar.getProject().getComponents(); + for (var component : components) { + for (var edge : component.getEdges()) { + if (edge.getId().equals(id)) { + return component.getName(); + } + } + } + throw new RuntimeException("Could not find component name for edge with id " + id); } } diff --git a/src/main/java/ecdar/simulation/SimulationStateSuccessor.java b/src/main/java/ecdar/simulation/SimulationStateSuccessor.java deleted file mode 100644 index bf6eb85d..00000000 --- a/src/main/java/ecdar/simulation/SimulationStateSuccessor.java +++ /dev/null @@ -1,15 +0,0 @@ -package ecdar.simulation; - -import java.util.ArrayList; - -public class SimulationStateSuccessor { - public ArrayList getTransitions() { - // ToDo: Implement - return new ArrayList<>(); - } - - public SimulationState getState() { - // ToDo: Implement - return null; - } -} diff --git a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml index dc5c1a14..87a3843b 100644 --- a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml +++ b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml @@ -251,8 +251,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/ecdar/presentations/SimEdgePresentation.fxml b/src/main/resources/ecdar/presentations/SimEdgePresentation.fxml index 1c72435f..b1224493 100755 --- a/src/main/resources/ecdar/presentations/SimEdgePresentation.fxml +++ b/src/main/resources/ecdar/presentations/SimEdgePresentation.fxml @@ -4,8 +4,7 @@ + fx:controller="ecdar.controllers.SimEdgeController"> diff --git a/src/main/resources/ecdar/presentations/TransitionPaneElementPresentation.fxml b/src/main/resources/ecdar/presentations/TransitionPaneElementPresentation.fxml index b9b58ff9..8047bcb3 100755 --- a/src/main/resources/ecdar/presentations/TransitionPaneElementPresentation.fxml +++ b/src/main/resources/ecdar/presentations/TransitionPaneElementPresentation.fxml @@ -30,7 +30,7 @@ - + From 66913ea87e39c18924a4b8cc68f3171dcf6bd6b0 Mon Sep 17 00:00:00 2001 From: WassawRoki <56611129+WassawRoki@users.noreply.github.com> Date: Fri, 2 Dec 2022 14:07:46 +0100 Subject: [PATCH 115/120] clock names added + formatting --- .../controllers/TracePaneElementController.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/ecdar/controllers/TracePaneElementController.java b/src/main/java/ecdar/controllers/TracePaneElementController.java index 6898dfad..29efb90d 100755 --- a/src/main/java/ecdar/controllers/TracePaneElementController.java +++ b/src/main/java/ecdar/controllers/TracePaneElementController.java @@ -150,21 +150,24 @@ private void insertTraceState(final SimulationState state, final boolean shouldA * @return A string representing the state */ private String traceString(SimulationState state) { - StringBuilder title = new StringBuilder("("); + StringBuilder title = new StringBuilder(); int length = state.getLocations().size(); + for (int i = 0; i < length; i++) { - Location loc = Ecdar.getProject() - .findComponent(state.getLocations().get(i).getKey()) - .findLocation(state.getLocations().get(i).getValue()); + Location loc = Ecdar.getProject().findComponent(state.getLocations().get(i).getKey()).findLocation(state.getLocations().get(i).getValue()); String locationName = loc.getId(); + //Component name + title.append(Ecdar.getProject().findComponent(state.getLocations().get(i).getKey()).getName()); + //Clock names + title.append("|Clocks: " + state.getState().getFederation().getDisjunction().getConjunctions(0).getConstraints(i).getX().getClockName()); + //Location names + title.append("|Location: "); if (i == length - 1) { title.append(locationName); } else { - title.append(locationName).append(", "); + title.append(locationName).append(",\n"); } } - title.append(")"); - return title.toString(); } From 0fe6ff064a5972c87980552377e10e779b56203c Mon Sep 17 00:00:00 2001 From: WassawRoki <56611129+WassawRoki@users.noreply.github.com> Date: Mon, 5 Dec 2022 13:32:51 +0100 Subject: [PATCH 116/120] e --- .../java/ecdar/controllers/TracePaneElementController.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/ecdar/controllers/TracePaneElementController.java b/src/main/java/ecdar/controllers/TracePaneElementController.java index 29efb90d..7bdc8be6 100755 --- a/src/main/java/ecdar/controllers/TracePaneElementController.java +++ b/src/main/java/ecdar/controllers/TracePaneElementController.java @@ -152,14 +152,16 @@ private void insertTraceState(final SimulationState state, final boolean shouldA private String traceString(SimulationState state) { StringBuilder title = new StringBuilder(); int length = state.getLocations().size(); - + System.out.println(Ecdar.getSimulationHandler().currentState.getValue().getState()); for (int i = 0; i < length; i++) { Location loc = Ecdar.getProject().findComponent(state.getLocations().get(i).getKey()).findLocation(state.getLocations().get(i).getValue()); + int clock = Ecdar.getSimulationHandler().currentState.getValue().getState().getFederation().getDisjunction().getConjunctions(0).getConstraints(i).getC(); String locationName = loc.getId(); //Component name title.append(Ecdar.getProject().findComponent(state.getLocations().get(i).getKey()).getName()); //Clock names title.append("|Clocks: " + state.getState().getFederation().getDisjunction().getConjunctions(0).getConstraints(i).getX().getClockName()); + title.append(": "+ clock); //Location names title.append("|Location: "); if (i == length - 1) { From 7a5e09aa0183c2fc46994a7875822256cb3d9e3b Mon Sep 17 00:00:00 2001 From: APaludan Date: Mon, 5 Dec 2022 14:26:15 +0100 Subject: [PATCH 117/120] b --- .../TracePaneElementController.java | 48 +++++++++++++------ .../presentations/TransitionPresentation.fxml | 2 +- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/main/java/ecdar/controllers/TracePaneElementController.java b/src/main/java/ecdar/controllers/TracePaneElementController.java index 7bdc8be6..54a12323 100755 --- a/src/main/java/ecdar/controllers/TracePaneElementController.java +++ b/src/main/java/ecdar/controllers/TracePaneElementController.java @@ -150,27 +150,47 @@ private void insertTraceState(final SimulationState state, final boolean shouldA * @return A string representing the state */ private String traceString(SimulationState state) { - StringBuilder title = new StringBuilder(); + StringBuilder title = new StringBuilder("("); int length = state.getLocations().size(); - System.out.println(Ecdar.getSimulationHandler().currentState.getValue().getState()); for (int i = 0; i < length; i++) { - Location loc = Ecdar.getProject().findComponent(state.getLocations().get(i).getKey()).findLocation(state.getLocations().get(i).getValue()); - int clock = Ecdar.getSimulationHandler().currentState.getValue().getState().getFederation().getDisjunction().getConjunctions(0).getConstraints(i).getC(); + Location loc = Ecdar.getProject() + .findComponent(state.getLocations().get(i).getKey()) + .findLocation(state.getLocations().get(i).getValue()); String locationName = loc.getId(); - //Component name - title.append(Ecdar.getProject().findComponent(state.getLocations().get(i).getKey()).getName()); - //Clock names - title.append("|Clocks: " + state.getState().getFederation().getDisjunction().getConjunctions(0).getConstraints(i).getX().getClockName()); - title.append(": "+ clock); - //Location names - title.append("|Location: "); if (i == length - 1) { title.append(locationName); - } else { - title.append(locationName).append(",\n"); + } else { + title.append(locationName).append(", "); } } - return title.toString(); + title.append(")\n"); + + StringBuilder clocks = new StringBuilder(); + for (var constraint : state.getState().getFederation().getDisjunction().getConjunctions(0).getConstraintsList()) { + var x = constraint.getX().getClockName(); + var y = constraint.getY().getClockName(); + var c = constraint.getC(); + var strict = constraint.getStrict(); + clocks.append(x).append(" - ").append(y).append(strict ? " < " : " <= ").append(c).append("\n"); + } + // for (int i = 0; i < length; i++) { + // Location loc = Ecdar.getProject().findComponent(state.getLocations().get(i).getKey()).findLocation(state.getLocations().get(i).getValue()); + // int clock = Ecdar.getSimulationHandler().currentState.getValue().getState().getFederation().getDisjunction().getConjunctions(0).getConstraints(i).getC(); + // String locationName = loc.getId(); + // //Component name + // title.append(Ecdar.getProject().findComponent(state.getLocations().get(i).getKey()).getName()); + // //Clock names + // title.append("|Clocks: " + state.getState().getFederation().getDisjunction().getConjunctions(0).getConstraints(i).getX().getClockName()); + // title.append(": "+ clock); + // //Location names + // title.append("|Location: "); + // if (i == length - 1) { + // title.append(locationName); + // } else { + // title.append(locationName).append(",\n"); + // } + // } + return title.toString() + clocks.toString(); } /** diff --git a/src/main/resources/ecdar/presentations/TransitionPresentation.fxml b/src/main/resources/ecdar/presentations/TransitionPresentation.fxml index 3fa1c5cf..a738ef24 100755 --- a/src/main/resources/ecdar/presentations/TransitionPresentation.fxml +++ b/src/main/resources/ecdar/presentations/TransitionPresentation.fxml @@ -9,7 +9,7 @@ fx:controller="ecdar.controllers.TransitionController" type="AnchorPane"> - + From 381f20ab790f6646adad22967071627417ba9aec Mon Sep 17 00:00:00 2001 From: APaludan Date: Mon, 5 Dec 2022 14:45:57 +0100 Subject: [PATCH 118/120] n --- .../TracePaneElementController.java | 18 +----------------- .../TracePaneElementPresentation.fxml | 3 +++ .../presentations/TransitionPresentation.fxml | 2 +- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/main/java/ecdar/controllers/TracePaneElementController.java b/src/main/java/ecdar/controllers/TracePaneElementController.java index 54a12323..29f1e2cf 100755 --- a/src/main/java/ecdar/controllers/TracePaneElementController.java +++ b/src/main/java/ecdar/controllers/TracePaneElementController.java @@ -150,6 +150,7 @@ private void insertTraceState(final SimulationState state, final boolean shouldA * @return A string representing the state */ private String traceString(SimulationState state) { + System.out.println(state.getState().getFederation().getDisjunction().getConjunctionsList().size()); StringBuilder title = new StringBuilder("("); int length = state.getLocations().size(); for (int i = 0; i < length; i++) { @@ -173,23 +174,6 @@ private String traceString(SimulationState state) { var strict = constraint.getStrict(); clocks.append(x).append(" - ").append(y).append(strict ? " < " : " <= ").append(c).append("\n"); } - // for (int i = 0; i < length; i++) { - // Location loc = Ecdar.getProject().findComponent(state.getLocations().get(i).getKey()).findLocation(state.getLocations().get(i).getValue()); - // int clock = Ecdar.getSimulationHandler().currentState.getValue().getState().getFederation().getDisjunction().getConjunctions(0).getConstraints(i).getC(); - // String locationName = loc.getId(); - // //Component name - // title.append(Ecdar.getProject().findComponent(state.getLocations().get(i).getKey()).getName()); - // //Clock names - // title.append("|Clocks: " + state.getState().getFederation().getDisjunction().getConjunctions(0).getConstraints(i).getX().getClockName()); - // title.append(": "+ clock); - // //Location names - // title.append("|Location: "); - // if (i == length - 1) { - // title.append(locationName); - // } else { - // title.append(locationName).append(",\n"); - // } - // } return title.toString() + clocks.toString(); } diff --git a/src/main/resources/ecdar/presentations/TracePaneElementPresentation.fxml b/src/main/resources/ecdar/presentations/TracePaneElementPresentation.fxml index 0b83620e..0a233345 100755 --- a/src/main/resources/ecdar/presentations/TracePaneElementPresentation.fxml +++ b/src/main/resources/ecdar/presentations/TracePaneElementPresentation.fxml @@ -46,4 +46,7 @@ styleClass="caption"/> + + + diff --git a/src/main/resources/ecdar/presentations/TransitionPresentation.fxml b/src/main/resources/ecdar/presentations/TransitionPresentation.fxml index a738ef24..97edf762 100755 --- a/src/main/resources/ecdar/presentations/TransitionPresentation.fxml +++ b/src/main/resources/ecdar/presentations/TransitionPresentation.fxml @@ -9,7 +9,7 @@ fx:controller="ecdar.controllers.TransitionController" type="AnchorPane"> - + From 8635171f37e2fe344a6a2470575633212c8a747e Mon Sep 17 00:00:00 2001 From: APaludan Date: Mon, 5 Dec 2022 15:02:55 +0100 Subject: [PATCH 119/120] no height limit for trace element --- .../resources/ecdar/presentations/TransitionPresentation.fxml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/ecdar/presentations/TransitionPresentation.fxml b/src/main/resources/ecdar/presentations/TransitionPresentation.fxml index 97edf762..c529d876 100755 --- a/src/main/resources/ecdar/presentations/TransitionPresentation.fxml +++ b/src/main/resources/ecdar/presentations/TransitionPresentation.fxml @@ -9,7 +9,7 @@ fx:controller="ecdar.controllers.TransitionController" type="AnchorPane"> - + From 7cb2e62cd202d98995db469ac8a92656f8e58926 Mon Sep 17 00:00:00 2001 From: APaludan Date: Mon, 5 Dec 2022 19:12:37 +0100 Subject: [PATCH 120/120] padding --- .../ecdar/controllers/TracePaneElementController.java | 1 - .../ecdar/presentations/TransitionPresentation.fxml | 8 ++++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/ecdar/controllers/TracePaneElementController.java b/src/main/java/ecdar/controllers/TracePaneElementController.java index 29f1e2cf..01c27bf0 100755 --- a/src/main/java/ecdar/controllers/TracePaneElementController.java +++ b/src/main/java/ecdar/controllers/TracePaneElementController.java @@ -150,7 +150,6 @@ private void insertTraceState(final SimulationState state, final boolean shouldA * @return A string representing the state */ private String traceString(SimulationState state) { - System.out.println(state.getState().getFederation().getDisjunction().getConjunctionsList().size()); StringBuilder title = new StringBuilder("("); int length = state.getLocations().size(); for (int i = 0; i < length; i++) { diff --git a/src/main/resources/ecdar/presentations/TransitionPresentation.fxml b/src/main/resources/ecdar/presentations/TransitionPresentation.fxml index c529d876..900aa8d4 100755 --- a/src/main/resources/ecdar/presentations/TransitionPresentation.fxml +++ b/src/main/resources/ecdar/presentations/TransitionPresentation.fxml @@ -2,6 +2,7 @@ + -